@mentra/sdk 2.1.27 → 2.1.29-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 (95) hide show
  1. package/dist/app/session/api-client.d.ts.map +1 -1
  2. package/dist/app/session/dashboard.d.ts +5 -8
  3. package/dist/app/session/dashboard.d.ts.map +1 -1
  4. package/dist/app/session/events.d.ts +10 -4
  5. package/dist/app/session/events.d.ts.map +1 -1
  6. package/dist/app/session/index.d.ts +64 -4
  7. package/dist/app/session/index.d.ts.map +1 -1
  8. package/dist/app/session/modules/audio.d.ts +33 -4
  9. package/dist/app/session/modules/audio.d.ts.map +1 -1
  10. package/dist/app/session/modules/camera-managed-extension.d.ts +2 -3
  11. package/dist/app/session/modules/camera-managed-extension.d.ts.map +1 -1
  12. package/dist/app/session/modules/camera.d.ts +11 -10
  13. package/dist/app/session/modules/camera.d.ts.map +1 -1
  14. package/dist/app/session/modules/led.d.ts +141 -0
  15. package/dist/app/session/modules/led.d.ts.map +1 -0
  16. package/dist/app/session/modules/location.d.ts +1 -2
  17. package/dist/app/session/modules/location.d.ts.map +1 -1
  18. package/dist/app/session/modules/simple-storage.d.ts +22 -1
  19. package/dist/app/session/modules/simple-storage.d.ts.map +1 -1
  20. package/dist/display-utils.d.ts +989 -0
  21. package/dist/display-utils.d.ts.map +1 -0
  22. package/dist/display-utils.js +1197 -0
  23. package/dist/display-utils.js.map +17 -0
  24. package/dist/index.d.ts +7 -7
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +5427 -112
  27. package/dist/index.js.map +45 -0
  28. package/dist/logging/logger.d.ts +1 -1
  29. package/dist/logging/logger.d.ts.map +1 -1
  30. package/dist/types/capabilities.d.ts +3 -0
  31. package/dist/types/capabilities.d.ts.map +1 -1
  32. package/dist/types/index.d.ts +4 -14
  33. package/dist/types/index.d.ts.map +1 -1
  34. package/dist/types/message-types.d.ts +8 -1
  35. package/dist/types/message-types.d.ts.map +1 -1
  36. package/dist/types/messages/app-to-cloud.d.ts +49 -3
  37. package/dist/types/messages/app-to-cloud.d.ts.map +1 -1
  38. package/dist/types/messages/cloud-to-app.d.ts +18 -6
  39. package/dist/types/messages/cloud-to-app.d.ts.map +1 -1
  40. package/dist/types/messages/cloud-to-glasses.d.ts +30 -2
  41. package/dist/types/messages/cloud-to-glasses.d.ts.map +1 -1
  42. package/dist/types/messages/glasses-to-cloud.d.ts +24 -1
  43. package/dist/types/messages/glasses-to-cloud.d.ts.map +1 -1
  44. package/dist/types/rtmp-stream.d.ts +1 -1
  45. package/dist/types/rtmp-stream.d.ts.map +1 -1
  46. package/dist/types/streams.d.ts +31 -2
  47. package/dist/types/streams.d.ts.map +1 -1
  48. package/package.json +34 -11
  49. package/dist/app/index.js +0 -24
  50. package/dist/app/server/index.js +0 -658
  51. package/dist/app/session/api-client.js +0 -101
  52. package/dist/app/session/dashboard.js +0 -149
  53. package/dist/app/session/events.js +0 -315
  54. package/dist/app/session/index.js +0 -1573
  55. package/dist/app/session/layouts.js +0 -372
  56. package/dist/app/session/modules/audio.js +0 -321
  57. package/dist/app/session/modules/camera-managed-extension.js +0 -310
  58. package/dist/app/session/modules/camera.js +0 -607
  59. package/dist/app/session/modules/index.js +0 -19
  60. package/dist/app/session/modules/location.js +0 -61
  61. package/dist/app/session/modules/simple-storage.js +0 -232
  62. package/dist/app/session/settings.js +0 -358
  63. package/dist/app/token/index.js +0 -22
  64. package/dist/app/token/utils.js +0 -144
  65. package/dist/app/webview/index.js +0 -382
  66. package/dist/constants/index.js +0 -16
  67. package/dist/constants/log-messages/color.js +0 -14
  68. package/dist/constants/log-messages/logos.js +0 -48
  69. package/dist/constants/log-messages/updates.js +0 -55
  70. package/dist/constants/log-messages/warning.js +0 -89
  71. package/dist/examples/managed-rtmp-streaming-example.js +0 -158
  72. package/dist/examples/managed-rtmp-streaming-with-restream-example.js +0 -124
  73. package/dist/examples/rtmp-streaming-example.js +0 -102
  74. package/dist/logging/logger.js +0 -79
  75. package/dist/types/capabilities.js +0 -9
  76. package/dist/types/dashboard/index.js +0 -12
  77. package/dist/types/enums.js +0 -75
  78. package/dist/types/index.js +0 -101
  79. package/dist/types/layouts.js +0 -3
  80. package/dist/types/message-types.js +0 -212
  81. package/dist/types/messages/app-to-cloud.js +0 -95
  82. package/dist/types/messages/base.js +0 -3
  83. package/dist/types/messages/cloud-to-app.js +0 -78
  84. package/dist/types/messages/cloud-to-glasses.js +0 -68
  85. package/dist/types/messages/glasses-to-cloud.js +0 -140
  86. package/dist/types/models.js +0 -101
  87. package/dist/types/photo-data.js +0 -2
  88. package/dist/types/rtmp-stream.js +0 -3
  89. package/dist/types/streams.js +0 -306
  90. package/dist/types/token.js +0 -7
  91. package/dist/types/webhooks.js +0 -28
  92. package/dist/utils/animation-utils.js +0 -340
  93. package/dist/utils/bitmap-utils.js +0 -475
  94. package/dist/utils/permissions-utils.js +0 -263
  95. package/dist/utils/resource-tracker.js +0 -153
@@ -1,101 +0,0 @@
1
- "use strict";
2
- /**
3
- * 🔌 API Client Module
4
- *
5
- * Provides HTTP API access to MentraOS Cloud services.
6
- * Automatically uses the correct server URL derived from the WebSocket URL.
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.ApiClient = void 0;
10
- exports.wsUrlToHttpUrl = wsUrlToHttpUrl;
11
- /**
12
- * Convert a WebSocket URL to a HTTP/HTTPS URL
13
- *
14
- * @param wsUrl WebSocket URL to convert
15
- * @returns HTTP URL equivalent
16
- */
17
- function wsUrlToHttpUrl(wsUrl) {
18
- if (!wsUrl)
19
- return undefined;
20
- try {
21
- // Parse the WebSocket URL
22
- const url = new URL(wsUrl);
23
- // Change protocol from ws/wss to http/https
24
- const protocol = url.protocol === 'wss:' ? 'https:' : 'http:';
25
- // Recreate the URL with the new protocol
26
- return `${protocol}//${url.host}`;
27
- }
28
- catch (error) {
29
- console.error('Error converting WebSocket URL to HTTP URL:', error);
30
- return undefined;
31
- }
32
- }
33
- /**
34
- * API client class for making HTTP requests to MentraOS Cloud
35
- */
36
- class ApiClient {
37
- /**
38
- * Create a new API client
39
- *
40
- * @param packageName App package name
41
- * @param wsUrl WebSocket URL (optional, can be set later)
42
- * @param userId User ID (optional, for authenticated requests)
43
- */
44
- constructor(packageName, wsUrl, userId) {
45
- this.packageName = packageName;
46
- this.userId = userId;
47
- if (wsUrl) {
48
- this.baseUrl = wsUrlToHttpUrl(wsUrl);
49
- }
50
- }
51
- /**
52
- * Set the WebSocket URL to derive the HTTP base URL
53
- *
54
- * @param wsUrl WebSocket URL
55
- */
56
- setWebSocketUrl(wsUrl) {
57
- this.baseUrl = wsUrlToHttpUrl(wsUrl);
58
- }
59
- /**
60
- * Set the user ID for authenticated requests
61
- *
62
- * @param userId User ID
63
- */
64
- setUserId(userId) {
65
- this.userId = userId;
66
- }
67
- /**
68
- * Fetch settings from MentraOS Cloud
69
- *
70
- * @returns Promise resolving to settings array
71
- * @throws Error if client is not configured correctly or if request fails
72
- */
73
- async fetchSettings() {
74
- if (!this.baseUrl) {
75
- throw new Error('API client is not configured with a base URL');
76
- }
77
- if (!this.userId) {
78
- throw new Error('User ID is required for fetching settings');
79
- }
80
- const url = `${this.baseUrl}/appsettings/user/${this.packageName}`;
81
- try {
82
- const response = await fetch(url, {
83
- method: 'GET',
84
- headers: {
85
- 'Authorization': `Bearer ${this.userId}`,
86
- 'Content-Type': 'application/json'
87
- }
88
- });
89
- if (!response.ok) {
90
- throw new Error(`Failed to fetch settings: ${response.status} ${response.statusText}`);
91
- }
92
- const data = await response.json();
93
- return data.settings || [];
94
- }
95
- catch (error) {
96
- console.error('Error fetching settings:', error);
97
- throw error;
98
- }
99
- }
100
- }
101
- exports.ApiClient = ApiClient;
@@ -1,149 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.DashboardManager = exports.DashboardContentManager = exports.DashboardSystemManager = void 0;
7
- /**
8
- * Dashboard API Implementation
9
- *
10
- * Provides dashboard functionality for Apps, allowing them to write content
11
- * to the dashboard and respond to dashboard mode changes.
12
- */
13
- // import { systemApps } from '../../constants';
14
- const dashboard_1 = require("../../types/dashboard");
15
- const message_types_1 = require("../../types/message-types");
16
- const dotenv_1 = __importDefault(require("dotenv"));
17
- // Load environment variables from .env file
18
- dotenv_1.default.config();
19
- const SYSTEM_DASHBOARD_PACKAGE_NAME = process.env.SYSTEM_DASHBOARD_PACKAGE_NAME || "system.augmentos.dashboard";
20
- /**
21
- * Implementation of DashboardSystemAPI interface for system dashboard App
22
- */
23
- class DashboardSystemManager {
24
- constructor(session, packageName, send) {
25
- this.session = session;
26
- this.packageName = packageName;
27
- this.send = send;
28
- }
29
- setTopLeft(content) {
30
- this.updateSystemSection("topLeft", content);
31
- }
32
- setTopRight(content) {
33
- this.updateSystemSection("topRight", content);
34
- }
35
- setBottomLeft(content) {
36
- this.updateSystemSection("bottomLeft", content);
37
- }
38
- setBottomRight(content) {
39
- this.updateSystemSection("bottomRight", content);
40
- }
41
- setViewMode(mode) {
42
- const message = {
43
- type: message_types_1.AppToCloudMessageType.DASHBOARD_MODE_CHANGE,
44
- packageName: this.packageName,
45
- sessionId: `${this.session.getSessionId()}-${this.packageName}`,
46
- mode,
47
- timestamp: new Date(),
48
- };
49
- this.send(message);
50
- }
51
- updateSystemSection(section, content) {
52
- const message = {
53
- type: message_types_1.AppToCloudMessageType.DASHBOARD_SYSTEM_UPDATE,
54
- packageName: this.packageName,
55
- sessionId: `${this.session.getSessionId()}-${this.packageName}`,
56
- section,
57
- content,
58
- timestamp: new Date(),
59
- };
60
- this.send(message);
61
- }
62
- }
63
- exports.DashboardSystemManager = DashboardSystemManager;
64
- /**
65
- * Implementation of DashboardContentAPI interface for all Apps
66
- */
67
- class DashboardContentManager {
68
- // private alwaysOnEnabled: boolean = false;
69
- constructor(session, packageName, send, events) {
70
- this.session = session;
71
- this.packageName = packageName;
72
- this.send = send;
73
- this.events = events;
74
- this.currentMode = "none";
75
- }
76
- write(content, targets = [dashboard_1.DashboardMode.MAIN]) {
77
- const message = {
78
- type: message_types_1.AppToCloudMessageType.DASHBOARD_CONTENT_UPDATE,
79
- packageName: this.packageName,
80
- sessionId: `${this.session.getSessionId()}-${this.packageName}`,
81
- content,
82
- modes: targets,
83
- timestamp: new Date(),
84
- };
85
- this.send(message);
86
- }
87
- writeToMain(content) {
88
- this.write(content, [dashboard_1.DashboardMode.MAIN]);
89
- }
90
- writeToExpanded(content) {
91
- const message = {
92
- type: message_types_1.AppToCloudMessageType.DASHBOARD_CONTENT_UPDATE,
93
- packageName: this.packageName,
94
- sessionId: `${this.session.getSessionId()}-${this.packageName}`,
95
- content,
96
- modes: [dashboard_1.DashboardMode.EXPANDED],
97
- timestamp: new Date(),
98
- };
99
- this.send(message);
100
- }
101
- // writeToAlwaysOn(content: string): void {
102
- // this.write(content, [DashboardMode.ALWAYS_ON]);
103
- // }
104
- async getCurrentMode() {
105
- return this.currentMode;
106
- }
107
- // async isAlwaysOnEnabled(): Promise<boolean> {
108
- // return this.alwaysOnEnabled;
109
- // }
110
- onModeChange(callback) {
111
- return this.events.onDashboardModeChange((data) => {
112
- this.currentMode = data.mode;
113
- callback(data.mode);
114
- });
115
- }
116
- // onAlwaysOnChange(callback: (enabled: boolean) => void): () => void {
117
- // return this.events.onDashboardAlwaysOnChange((data) => {
118
- // this.alwaysOnEnabled = data.enabled;
119
- // callback(data.enabled);
120
- // });
121
- // }
122
- // Internal methods to update state
123
- setCurrentMode(mode) {
124
- this.currentMode = mode;
125
- this.events.emit("dashboard_mode_change", { mode });
126
- }
127
- }
128
- exports.DashboardContentManager = DashboardContentManager;
129
- /**
130
- * Dashboard Manager - Main class that manages dashboard functionality
131
- * Each AppSession instance gets its own DashboardManager instance
132
- */
133
- class DashboardManager {
134
- constructor(session, send) {
135
- const packageName = session.getPackageName();
136
- const events = session.events;
137
- // Create content API (available to all Apps)
138
- this.content = new DashboardContentManager(session, packageName, send, events);
139
- // Add system API if this is the system dashboard App
140
- if (packageName === SYSTEM_DASHBOARD_PACKAGE_NAME) {
141
- session.logger.info({ service: "SDK:DashboardManager" }, "Initializing system dashboard manager");
142
- this.system = new DashboardSystemManager(session, packageName, send);
143
- }
144
- else {
145
- session.logger.info({ service: "SDK:DashboardManager" }, `Not the system dashboard: ${packageName}`);
146
- }
147
- }
148
- }
149
- exports.DashboardManager = DashboardManager;
@@ -1,315 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.EventManager = void 0;
7
- /**
8
- * 🎮 Event Manager Module
9
- */
10
- const events_1 = __importDefault(require("events"));
11
- const types_1 = require("../../types");
12
- const permissions_utils_1 = require("../../utils/permissions-utils");
13
- class EventManager {
14
- constructor(subscribe, unsubscribe, packageName, baseUrl) {
15
- this.subscribe = subscribe;
16
- this.unsubscribe = unsubscribe;
17
- this.packageName = packageName;
18
- this.baseUrl = baseUrl;
19
- this.emitter = new events_1.default();
20
- this.handlers = new Map();
21
- this.lastLanguageTranscriptioCleanupHandler = () => { };
22
- this.lastLanguageTranslationCleanupHandler = () => { };
23
- }
24
- // Convenience handlers for common event types
25
- onTranscription(handler) {
26
- // Only make the API call if we have a base URL (server-side environment)
27
- (0, permissions_utils_1.microPhoneWarnLog)(this.baseUrl, this.packageName, this.onTranscription.name);
28
- return this.addHandler((0, types_1.createTranscriptionStream)("en-US"), handler);
29
- }
30
- /**
31
- * 🎤 Listen for transcription events in a specific language
32
- * @param language - Language code (e.g., "en-US")
33
- * @param handler - Function to handle transcription data
34
- * @param disableLanguageIdentification - Optional flag to disable language identification (defaults to false/enabled)
35
- * @returns Cleanup function to remove the handler
36
- * @throws Error if language code is invalid
37
- */
38
- onTranscriptionForLanguage(language, handler, disableLanguageIdentification = false) {
39
- if (!(0, types_1.isValidLanguageCode)(language)) {
40
- throw new Error(`Invalid language code: ${language}`);
41
- }
42
- this.lastLanguageTranscriptioCleanupHandler();
43
- const streamType = (0, types_1.createTranscriptionStream)(language, {
44
- disableLanguageIdentification,
45
- });
46
- this.lastLanguageTranscriptioCleanupHandler = this.addHandler(streamType, handler);
47
- return this.lastLanguageTranscriptioCleanupHandler;
48
- }
49
- /**
50
- * 🌐 Listen for translation events for a specific language pair
51
- * @param sourceLanguage - Source language code (e.g., "es-ES")
52
- * @param targetLanguage - Target language code (e.g., "en-US")
53
- * @param handler - Function to handle translation data
54
- * @returns Cleanup function to remove the handler
55
- * @throws Error if language codes are invalid
56
- */
57
- ontranslationForLanguage(sourceLanguage, targetLanguage, handler) {
58
- (0, permissions_utils_1.microPhoneWarnLog)(this.baseUrl || "", this.packageName, this.ontranslationForLanguage.name);
59
- if (!(0, types_1.isValidLanguageCode)(sourceLanguage)) {
60
- throw new Error(`Invalid source language code: ${sourceLanguage}`);
61
- }
62
- if (!(0, types_1.isValidLanguageCode)(targetLanguage)) {
63
- throw new Error(`Invalid target language code: ${targetLanguage}`);
64
- }
65
- this.lastLanguageTranslationCleanupHandler();
66
- const streamType = (0, types_1.createTranslationStream)(sourceLanguage, targetLanguage);
67
- this.lastLanguageTranslationCleanupHandler = this.addHandler(streamType, handler);
68
- return this.lastLanguageTranslationCleanupHandler;
69
- }
70
- onHeadPosition(handler) {
71
- return this.addHandler(types_1.StreamType.HEAD_POSITION, handler);
72
- }
73
- onButtonPress(handler) {
74
- return this.addHandler(types_1.StreamType.BUTTON_PRESS, handler);
75
- }
76
- onPhoneNotifications(handler) {
77
- return this.addHandler(types_1.StreamType.PHONE_NOTIFICATION, handler);
78
- }
79
- onPhoneNotificationDismissed(handler) {
80
- return this.addHandler(types_1.StreamType.PHONE_NOTIFICATION_DISMISSED, handler);
81
- }
82
- onGlassesBattery(handler) {
83
- return this.addHandler(types_1.StreamType.GLASSES_BATTERY_UPDATE, handler);
84
- }
85
- onPhoneBattery(handler) {
86
- return this.addHandler(types_1.StreamType.PHONE_BATTERY_UPDATE, handler);
87
- }
88
- onVoiceActivity(handler) {
89
- (0, permissions_utils_1.microPhoneWarnLog)(this.baseUrl || "", this.packageName, this.onVoiceActivity.name);
90
- return this.addHandler(types_1.StreamType.VAD, handler);
91
- }
92
- onLocation(handler) {
93
- return this.addHandler(types_1.StreamType.LOCATION_UPDATE, handler);
94
- }
95
- onCalendarEvent(handler) {
96
- return this.addHandler(types_1.StreamType.CALENDAR_EVENT, handler);
97
- }
98
- /**
99
- * 🎤 Listen for audio chunk data
100
- * @param handler - Function to handle audio chunks
101
- * @returns Cleanup function to remove the handler
102
- */
103
- onAudioChunk(handler) {
104
- return this.addHandler(types_1.StreamType.AUDIO_CHUNK, handler);
105
- }
106
- // System event handlers
107
- onConnected(handler) {
108
- this.emitter.on("connected", handler);
109
- return () => this.emitter.off("connected", handler);
110
- }
111
- onDisconnected(handler) {
112
- this.emitter.on("disconnected", handler);
113
- return () => this.emitter.off("disconnected", handler);
114
- }
115
- onError(handler) {
116
- this.emitter.on("error", handler);
117
- return () => this.emitter.off("error", handler);
118
- }
119
- onSettingsUpdate(handler) {
120
- this.emitter.on("settings_update", handler);
121
- return () => this.emitter.off("settings_update", handler);
122
- }
123
- /**
124
- * 🔧 Listen for device capabilities updates
125
- * @param handler - Function to handle capabilities updates
126
- * @returns Cleanup function to remove the handler
127
- */
128
- onCapabilitiesUpdate(handler) {
129
- this.emitter.on("capabilities_update", handler);
130
- return () => this.emitter.off("capabilities_update", handler);
131
- }
132
- /**
133
- * 🌐 Listen for dashboard mode changes
134
- * @param handler - Function to handle dashboard mode changes
135
- * @returns Cleanup function to remove the handler
136
- */
137
- onDashboardModeChange(handler) {
138
- this.emitter.on("dashboard_mode_change", handler);
139
- return () => this.emitter.off("dashboard_mode_change", handler);
140
- }
141
- /**
142
- * 🌐 Listen for dashboard always-on mode changes
143
- * @param handler - Function to handle dashboard always-on mode changes
144
- * @returns Cleanup function to remove the handler
145
- */
146
- onDashboardAlwaysOnChange(handler) {
147
- this.emitter.on("dashboard_always_on_change", handler);
148
- return () => this.emitter.off("dashboard_always_on_change", handler);
149
- }
150
- /**
151
- * 🚫 Listen for permission errors when subscriptions are rejected
152
- * @param handler - Function to handle permission errors
153
- * @returns Cleanup function to remove the handler
154
- */
155
- onPermissionError(handler) {
156
- this.emitter.on("permission_error", handler);
157
- return () => this.emitter.off("permission_error", handler);
158
- }
159
- /**
160
- * 🚫 Listen for individual permission denied events for specific streams
161
- * @param handler - Function to handle permission denied events
162
- * @returns Cleanup function to remove the handler
163
- */
164
- onPermissionDenied(handler) {
165
- this.emitter.on("permission_denied", handler);
166
- return () => this.emitter.off("permission_denied", handler);
167
- }
168
- /**
169
- * 🔄 Listen for changes to a specific setting
170
- * @param key - Setting key to monitor
171
- * @param handler - Function to handle setting value changes
172
- * @returns Cleanup function to remove the handler
173
- */
174
- onSettingChange(key, handler) {
175
- let previousValue = undefined;
176
- const settingsHandler = (settings) => {
177
- try {
178
- const setting = settings.find((s) => s.key === key);
179
- if (setting) {
180
- // Only call handler if value has changed
181
- if (setting.value !== previousValue) {
182
- const newValue = setting.value;
183
- handler(newValue, previousValue);
184
- previousValue = newValue;
185
- }
186
- }
187
- }
188
- catch (error) {
189
- console.error(`Error in onSettingChange handler for key "${key}":`, error);
190
- }
191
- };
192
- this.emitter.on("settings_update", settingsHandler);
193
- this.emitter.on("connected", settingsHandler); // Also check when first connected
194
- return () => {
195
- this.emitter.off("settings_update", settingsHandler);
196
- this.emitter.off("connected", settingsHandler);
197
- };
198
- }
199
- /**
200
- * 🔄 Generic event handler
201
- *
202
- * Use this for stream types without specific handler methods
203
- */
204
- on(type, handler) {
205
- // Check permissions for specific stream types
206
- if (type === types_1.StreamType.CALENDAR_EVENT) {
207
- (0, permissions_utils_1.calendarWarnLog)(this.baseUrl, this.packageName, "on");
208
- }
209
- return this.addHandler(type, handler);
210
- }
211
- /**
212
- * ➕ Add an event handler and subscribe if needed
213
- */
214
- addHandler(type, handler) {
215
- const handlers = this.handlers.get(type) ?? new Set();
216
- if (handlers.size === 0) {
217
- this.handlers.set(type, handlers);
218
- this.subscribe(type);
219
- }
220
- handlers.add(handler);
221
- return () => this.removeHandler(type, handler);
222
- }
223
- /**
224
- * ➖ Remove an event handler
225
- */
226
- removeHandler(type, handler) {
227
- const handlers = this.handlers.get(type);
228
- if (!handlers)
229
- return;
230
- handlers.delete(handler);
231
- if (handlers.size === 0) {
232
- this.handlers.delete(type);
233
- this.unsubscribe(type);
234
- }
235
- }
236
- /**
237
- * 📡 Emit an event to all registered handlers with error isolation
238
- */
239
- emit(event, data) {
240
- try {
241
- // Emit to EventEmitter handlers (system events)
242
- // console.log(`#### Emitting to ${event}`);
243
- this.emitter.emit(event, data);
244
- // Emit to stream handlers if applicable
245
- const handlers = this.handlers.get(event);
246
- // console.log(`#### Handlers: ${JSON.stringify(handlers)}`);
247
- if (handlers) {
248
- // Create array of handlers to prevent modification during iteration
249
- const handlersArray = Array.from(handlers);
250
- // console.log(`((())) HandlersArray: ${JSON.stringify(handlersArray)}`);
251
- // Execute each handler in isolated try/catch to prevent one handler
252
- // from crashing the entire App
253
- handlersArray.forEach((handler) => {
254
- try {
255
- handler(data);
256
- }
257
- catch (handlerError) {
258
- // Log the error but don't let it propagate
259
- console.error(`Error in handler for event '${String(event)}':`, handlerError);
260
- // Emit an error event for tracking purposes
261
- if (event !== "error") {
262
- // Prevent infinite recursion
263
- const errorMessage = handlerError instanceof Error
264
- ? handlerError.message
265
- : String(handlerError);
266
- this.emitter.emit("error", new Error(`Handler error for event '${String(event)}': ${errorMessage}`));
267
- }
268
- }
269
- });
270
- }
271
- }
272
- catch (emitError) {
273
- // Catch any errors in the emission process itself
274
- console.error(`Fatal error emitting event '${String(event)}':`, emitError);
275
- // Try to emit an error event if we're not already handling an error
276
- if (event !== "error") {
277
- try {
278
- const errorMessage = emitError instanceof Error ? emitError.message : String(emitError);
279
- this.emitter.emit("error", new Error(`Event emission error for '${String(event)}': ${errorMessage}`));
280
- }
281
- catch (nestedError) {
282
- // If even this fails, just log it - nothing more we can do
283
- console.error("Failed to emit error event:", nestedError);
284
- }
285
- }
286
- }
287
- }
288
- /**
289
- * 📨 Listen for custom messages with a specific action
290
- * @param action - The action identifier to filter by
291
- * @param handler - Function to handle the message
292
- * @returns Cleanup function to remove the handler
293
- */
294
- onCustomMessage(action, handler) {
295
- const messageHandler = (message) => {
296
- if (message.action === action) {
297
- handler(message.payload);
298
- }
299
- };
300
- this.emitter.on("custom_message", messageHandler);
301
- return () => this.emitter.off("custom_message", messageHandler);
302
- }
303
- onVpsCoordinates(handler) {
304
- return this.addHandler(types_1.StreamType.VPS_COORDINATES, handler);
305
- }
306
- /**
307
- * 📸 Listen for photo responses
308
- * @param handler - Function to handle photo response data
309
- * @returns Cleanup function to remove the handler
310
- */
311
- onPhotoTaken(handler) {
312
- return this.addHandler(types_1.StreamType.PHOTO_TAKEN, handler);
313
- }
314
- }
315
- exports.EventManager = EventManager;