@qusaieilouti99/call-manager 0.1.37 → 0.1.38

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.
@@ -12,16 +12,16 @@ import android.media.MediaPlayer
12
12
  import android.media.RingtoneManager
13
13
  import android.net.Uri
14
14
  import android.os.Build
15
- import android.os.Bundle // Explicit import for Bundle
15
+ import android.os.Bundle
16
16
  import android.os.PowerManager
17
- import android.telecom.CallAudioState // Explicit import
18
- import android.telecom.Connection // Explicit import
19
- import android.telecom.DisconnectCause // Explicit import
20
- import android.telecom.PhoneAccount // Explicit import
21
- import android.telecom.PhoneAccountHandle // Explicit import
22
- import android.telecom.TelecomManager // Explicit import
23
- import android.telecom.VideoProfile // Explicit import
24
- import android.util.Log // Explicit import for Log
17
+ import android.telecom.CallAudioState
18
+ import android.telecom.Connection
19
+ import android.telecom.DisconnectCause
20
+ import android.telecom.PhoneAccount
21
+ import android.telecom.PhoneAccountHandle
22
+ import android.telecom.TelecomManager
23
+ import android.telecom.VideoProfile
24
+ import android.util.Log
25
25
  import android.graphics.Color
26
26
  import android.app.Person
27
27
  import org.json.JSONArray
@@ -38,15 +38,15 @@ object CallEngine {
38
38
  private const val FOREGROUND_NOTIF_ID = 1001
39
39
 
40
40
  private var ringtone: android.media.Ringtone? = null
41
- private var ringbackPlayer: MediaPlayer? = null // For outgoing call ringback
41
+ private var ringbackPlayer: MediaPlayer? = null
42
42
  private var audioManager: AudioManager? = null
43
43
  private var wakeLock: PowerManager.WakeLock? = null
44
44
  private var appContext: Context? = null
45
45
 
46
46
  // --- Multi-call state ---
47
- private val activeCalls = ConcurrentHashMap<String, CallInfo>() // callId -> CallInfo
48
- private val telecomConnections = ConcurrentHashMap<String, Connection>() // callId -> Telecom Connection
49
- private var currentCallId: String? = null // The call currently in foreground (active/ringing/dialing)
47
+ private val activeCalls = ConcurrentHashMap<String, CallInfo>()
48
+ private val telecomConnections = ConcurrentHashMap<String, Connection>()
49
+ private var currentCallId: String? = null
50
50
  private var canMakeMultipleCalls: Boolean = true
51
51
 
52
52
  data class CallInfo(
@@ -62,12 +62,11 @@ object CallEngine {
62
62
 
63
63
  // --- Event handler for JS ---
64
64
  private var eventHandler: ((CallEventType, String) -> Unit)? = null
65
- private val cachedEvents = mutableListOf<Pair<CallEventType, String>>() // For caching events
65
+ private val cachedEvents = mutableListOf<Pair<CallEventType, String>>()
66
66
 
67
67
  fun setEventHandler(handler: ((CallEventType, String) -> Unit)?) {
68
68
  Log.d(TAG, "setEventHandler called. Handler present: ${handler != null}")
69
69
  eventHandler = handler
70
- // Emit cached events if a handler is now available
71
70
  handler?.let { h ->
72
71
  if (cachedEvents.isNotEmpty()) {
73
72
  Log.d(TAG, "Emitting ${cachedEvents.size} cached events.")
@@ -124,6 +123,7 @@ object CallEngine {
124
123
  return result
125
124
  }
126
125
 
126
+ // This is for INCOMING calls using TelecomManager.addNewIncomingCall
127
127
  fun reportIncomingCall(context: Context, callId: String, callData: String) {
128
128
  appContext = context.applicationContext
129
129
  Log.d(TAG, "reportIncomingCall: $callId, $callData")
@@ -133,12 +133,12 @@ object CallEngine {
133
133
  json.optString("name", "Unknown")
134
134
  } catch (e: Exception) { "Unknown" }
135
135
 
136
- val parsedCallType = try { // Extract callType from callData
136
+ val parsedCallType = try {
137
137
  val json = JSONObject(callData)
138
138
  json.optString("callType", "Audio")
139
139
  } catch (e: Exception) { "Audio" }
140
140
 
141
- val isVideoCallBoolean = parsedCallType == "Video" // Convert to boolean for Telecom API
141
+ val isVideoCallBoolean = parsedCallType == "Video"
142
142
 
143
143
  if (!canMakeMultipleCalls && activeCalls.isNotEmpty()) {
144
144
  Log.d(TAG, "Can't make multiple calls, holding existing calls.")
@@ -150,14 +150,15 @@ object CallEngine {
150
150
  Log.d(TAG, "Call $callId added to activeCalls. State: INCOMING, callType: $parsedCallType")
151
151
 
152
152
  showIncomingCallUI(context, callId, callerName, parsedCallType)
153
- registerPhoneAccount(context)
153
+ registerPhoneAccount(context) // Register phone account so Telecom can handle incoming
154
154
  val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
155
155
  val phoneAccountHandle = getPhoneAccountHandle(context)
156
156
  val extras = Bundle().apply {
157
- putString(MyConnectionService.EXTRA_CALL_DATA, callData) // Pass raw callData
158
- putBoolean(MyConnectionService.EXTRA_IS_VIDEO_CALL_BOOLEAN, isVideoCallBoolean) // Pass boolean for Telecom
157
+ putString(MyConnectionService.EXTRA_CALL_DATA, callData)
158
+ putBoolean(MyConnectionService.EXTRA_IS_VIDEO_CALL_BOOLEAN, isVideoCallBoolean)
159
159
  }
160
160
  try {
161
+ // This API is fundamental for INCOMING call integration with Telecom.
161
162
  telecomManager.addNewIncomingCall(phoneAccountHandle, extras)
162
163
  startForegroundService(context)
163
164
  Log.d(TAG, "Successfully reported incoming call to TelecomManager for $callId")
@@ -171,6 +172,7 @@ object CallEngine {
171
172
  notifyCallStateChanged(context)
172
173
  }
173
174
 
175
+ // CORRECTED: Uses TelecomManager.placeCall for outgoing calls.
174
176
  fun startOutgoingCall(context: Context, callId: String, callData: String) {
175
177
  appContext = context.applicationContext
176
178
  Log.d(TAG, "startOutgoingCall: $callId, $callData")
@@ -180,12 +182,12 @@ object CallEngine {
180
182
  json.optString("name", "Unknown")
181
183
  } catch (e: Exception) { "Unknown" }
182
184
 
183
- val parsedCallType = try { // Extract callType from callData
185
+ val parsedCallType = try {
184
186
  val json = JSONObject(callData)
185
187
  json.optString("callType", "Audio")
186
188
  } catch (e: Exception) { "Audio" }
187
189
 
188
- val isVideoCallBoolean = parsedCallType == "Video" // Convert to boolean for Telecom API
190
+ val isVideoCallBoolean = parsedCallType == "Video"
189
191
 
190
192
  if (!canMakeMultipleCalls && activeCalls.isNotEmpty()) {
191
193
  Log.d(TAG, "Can't make multiple calls, holding existing calls before outgoing.")
@@ -196,36 +198,41 @@ object CallEngine {
196
198
  currentCallId = callId
197
199
  Log.d(TAG, "Call $callId added to activeCalls. State: DIALING, callType: $parsedCallType")
198
200
 
199
- registerPhoneAccount(context)
201
+ registerPhoneAccount(context) // Ensure PhoneAccount is registered for Telecom to recognize it
200
202
  val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
201
203
  val phoneAccountHandle = getPhoneAccountHandle(context)
204
+
205
+ // For `placeCall`, the address URI is usually required. Use a generic "tel:" scheme with callId or number.
206
+ val addressUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, callId, null) // Using callId as a unique identifier for URI
207
+
202
208
  val extras = Bundle().apply {
203
- putString(MyConnectionService.EXTRA_CALL_DATA, callData) // Pass raw callData
204
- putBoolean(MyConnectionService.EXTRA_IS_VIDEO_CALL_BOOLEAN, isVideoCallBoolean) // Pass boolean for Telecom
205
- // Set speaker for video calls initially in Telecom's extras
209
+ // Essential for self-managed apps using placeCall
210
+ putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
211
+ // Pass additional data for your ConnectionService to retrieve
212
+ putString(MyConnectionService.EXTRA_CALL_DATA, callData)
213
+ putBoolean(MyConnectionService.EXTRA_IS_VIDEO_CALL_BOOLEAN, isVideoCallBoolean)
214
+ // Hint for initial speakerphone state
206
215
  putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, isVideoCallBoolean)
207
216
  }
208
217
 
209
218
  try {
210
- // Error: Unresolved reference 'addNewOutgoingCall'. This usually means TelecomManager is not correctly imported
211
- // or the minSdkVersion is too low for this method (requires API 23+).
212
- // Imports already checked; assuming minSdkVersion 26+.
213
- telecomManager.addNewOutgoingCall(phoneAccountHandle, extras)
219
+ // CORRECT API for self-managed outgoing calls that interact with Telecom
220
+ telecomManager.placeCall(addressUri, extras)
214
221
  startForegroundService(context)
215
- Log.d(TAG, "Successfully reported outgoing call to TelecomManager for $callId")
222
+ Log.d(TAG, "Successfully reported outgoing call to TelecomManager via placeCall for $callId")
216
223
  startRingback()
217
224
  bringAppToForeground(context)
218
225
  keepScreenAwake(context, true)
219
- if (parsedCallType == "Video") { // Initial audio route for video calls
226
+ if (parsedCallType == "Video") {
220
227
  setAudioRoute(context, "Speaker")
221
228
  } else {
222
- setAudioRoute(context, "Earpiece") // Initial audio route for audio calls
229
+ setAudioRoute(context, "Earpiece")
223
230
  }
224
231
  } catch (e: SecurityException) {
225
- Log.e(TAG, "SecurityException: Failed to start outgoing call. Check MANAGE_OWN_CALLS permission: ${e.message}", e)
232
+ Log.e(TAG, "SecurityException: Failed to start outgoing call via placeCall. Check MANAGE_OWN_CALLS permission: ${e.message}", e)
226
233
  endCall(context, callId)
227
234
  } catch (e: Exception) {
228
- Log.e(TAG, "Failed to start outgoing call: ${e.message}", e)
235
+ Log.e(TAG, "Failed to start outgoing call via placeCall: ${e.message}", e)
229
236
  endCall(context, callId)
230
237
  }
231
238
  notifyCallStateChanged(context)
@@ -262,7 +269,7 @@ object CallEngine {
262
269
 
263
270
  fun holdCall(context: Context, callId: String) {
264
271
  Log.d(TAG, "holdCall: $callId")
265
- activeCalls[callId]?.state = CallState.HELD
272
+ // Always attempt to inform Telecom if a Connection object exists for this callId
266
273
  val connection = telecomConnections[callId]
267
274
  connection?.setOnHold()
268
275
  emitEvent(CallEventType.CALL_HELD, JSONObject().put("callId", callId))
@@ -272,6 +279,7 @@ object CallEngine {
272
279
  fun unholdCall(context: Context, callId: String) {
273
280
  Log.d(TAG, "unholdCall: $callId")
274
281
  activeCalls[callId]?.state = CallState.ACTIVE
282
+ // Always attempt to inform Telecom if a Connection object exists for this callId
275
283
  val connection = telecomConnections[callId]
276
284
  connection?.setActive()
277
285
  emitEvent(CallEventType.CALL_UNHELD, JSONObject().put("callId", callId))
@@ -309,6 +317,7 @@ object CallEngine {
309
317
 
310
318
  cancelIncomingCallUI(context)
311
319
 
320
+ // Only disconnect from Telecom if a Connection object exists for this callId
312
321
  val connection = telecomConnections[callId]
313
322
  if (connection != null) {
314
323
  connection.setDisconnected(DisconnectCause(DisconnectCause.LOCAL))
@@ -316,7 +325,7 @@ object CallEngine {
316
325
  removeTelecomConnection(callId)
317
326
  Log.d(TAG, "Telecom Connection for $callId disconnected and destroyed.")
318
327
  } else {
319
- Log.w(TAG, "No Telecom Connection found for callId=$callId. It might have already been handled by Telecom.")
328
+ Log.d(TAG, "No Telecom Connection found for callId=$callId. Likely an outgoing call not initially handled by Telecom.")
320
329
  }
321
330
 
322
331
  if (activeCalls.isEmpty()) {
@@ -445,11 +454,10 @@ object CallEngine {
445
454
 
446
455
  // --- Audio Device Management ---
447
456
 
448
- // Returns the generated AudioRoutesInfo data class
449
457
  fun getAudioDevices(): AudioRoutesInfo {
450
- audioManager = audioManager ?: appContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
458
+ audioManager = appContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
451
459
  Log.e(TAG, "getAudioDevices: AudioManager is null or appContext is not set. Returning default.")
452
- return AudioRoutesInfo(emptyArray(), "Unknown") // Return default empty info
460
+ return AudioRoutesInfo(emptyArray(), "Unknown")
453
461
  }
454
462
  val devices = mutableListOf<String>()
455
463
  var currentRoute: String = "Unknown"
@@ -460,7 +468,7 @@ object CallEngine {
460
468
  when (device.type) {
461
469
  AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> {
462
470
  devices.add("Bluetooth")
463
- if (audioManager?.isBluetoothScoOn == true && !device.isSource) currentRoute = "Bluetooth" // check if output device
471
+ if (audioManager?.isBluetoothScoOn == true && !device.isSource) currentRoute = "Bluetooth"
464
472
  }
465
473
  AudioDeviceInfo.TYPE_WIRED_HEADPHONES, AudioDeviceInfo.TYPE_WIRED_HEADSET -> {
466
474
  devices.add("Headset")
@@ -472,8 +480,6 @@ object CallEngine {
472
480
  }
473
481
  AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> {
474
482
  devices.add("Earpiece")
475
- // Earpiece is active if speaker, bluetooth, and headset are off
476
- // This heuristic is tricky, relies on other routes being off
477
483
  if (audioManager?.isSpeakerphoneOn == false && audioManager?.isWiredHeadsetOn == false && audioManager?.isBluetoothScoOn == false && !device.isSource) {
478
484
  currentRoute = "Earpiece"
479
485
  }
@@ -482,17 +488,15 @@ object CallEngine {
482
488
  }
483
489
  }
484
490
  } else {
485
- // Fallback for older APIs. Detection is less reliable.
486
491
  devices.add("Speaker")
487
492
  devices.add("Earpiece")
488
493
  if (audioManager?.isSpeakerphoneOn == true) currentRoute = "Speaker"
489
- else currentRoute = "Earpiece" // Best guess for older API
494
+ else currentRoute = "Earpiece"
490
495
  }
491
496
 
492
497
  val distinctDevices = devices.distinct().toTypedArray()
493
498
 
494
- // Final check/refinement for currentRoute based on AudioManager flags
495
- if (currentRoute == "Unknown" || currentRoute == "Earpiece") { // If still unknown or defaulted to earpiece
499
+ if (currentRoute == "Unknown" || currentRoute == "Earpiece") {
496
500
  if (audioManager?.isBluetoothScoOn == true) {
497
501
  currentRoute = "Bluetooth"
498
502
  } else if (audioManager?.isSpeakerphoneOn == true) {
@@ -500,7 +504,7 @@ object CallEngine {
500
504
  } else if (audioManager?.isWiredHeadsetOn == true) {
501
505
  currentRoute = "Headset"
502
506
  } else {
503
- currentRoute = "Earpiece" // Assume earpiece as last resort
507
+ currentRoute = "Earpiece"
504
508
  }
505
509
  }
506
510
 
@@ -542,8 +546,6 @@ object CallEngine {
542
546
  Log.w(TAG, "Unknown audio route: $route. No action taken.")
543
547
  }
544
548
  }
545
- // Emit event immediately with the requested route.
546
- // The actual route change will be confirmed by onCallAudioStateChanged callback.
547
549
  emitEvent(CallEventType.AUDIO_ROUTE_CHANGED, JSONObject().put("route", route))
548
550
  }
549
551
 
@@ -615,12 +617,12 @@ object CallEngine {
615
617
  Log.w(TAG, "Cannot emit AudioDevicesChanged: appContext is null.")
616
618
  return
617
619
  }
618
- val audioInfo = getAudioDevices() // Get AudioRoutesInfo data class
619
- val jsonPayload = JSONObject().apply { // Convert to JSONObject for emitEvent
620
+ val audioInfo = getAudioDevices()
621
+ val jsonPayload = JSONObject().apply {
620
622
  put("devices", JSONArray(audioInfo.devices.toList()))
621
623
  put("currentRoute", audioInfo.currentRoute)
622
624
  }
623
- emitEvent(CallEventType.AUDIO_DEVICES_CHANGED, jsonPayload) // Emit the full JSON object
625
+ emitEvent(CallEventType.AUDIO_DEVICES_CHANGED, jsonPayload)
624
626
  }
625
627
 
626
628
  // --- Call State Change Notification ---
@@ -669,12 +671,13 @@ object CallEngine {
669
671
  }
670
672
  }
671
673
 
674
+ // PhoneAccount registration is used for both INCOMING and OUTGOING calls when interacting with Telecom
672
675
  private fun registerPhoneAccount(context: Context) {
673
676
  val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
674
677
  val phoneAccountHandle = getPhoneAccountHandle(context)
675
678
  if (telecomManager.getPhoneAccount(phoneAccountHandle) == null) {
676
679
  val phoneAccount = PhoneAccount.builder(phoneAccountHandle, "PingMe Call")
677
- .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED or PhoneAccount.CAPABILITY_SUPPORTS_HOLD) // Resolved
680
+ .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED or PhoneAccount.CAPABILITY_SUPPORTS_HOLD)
678
681
  .build()
679
682
  try {
680
683
  telecomManager.registerPhoneAccount(phoneAccount)
@@ -697,7 +700,6 @@ object CallEngine {
697
700
  }
698
701
 
699
702
  // --- Ringtone Management (for incoming calls, pre-Android S) ---
700
- // Can rely on default ringtone without adding custom file for incoming calls.
701
703
  fun playRingtone(context: Context) {
702
704
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
703
705
  Log.d(TAG, "playRingtone: Android S+ detected, system will handle ringtone via Telecom.")
@@ -730,15 +732,12 @@ object CallEngine {
730
732
  }
731
733
 
732
734
  // --- Ringback Tone Management (for outgoing calls) ---
733
- // Requires `res/raw/ringback_tone.mp3` for custom outgoing ringback.
734
735
  private fun startRingback() {
735
736
  if (ringbackPlayer != null && ringbackPlayer!!.isPlaying) {
736
737
  Log.d(TAG, "Ringback tone already playing.")
737
738
  return
738
739
  }
739
740
  try {
740
- // Ensure you have `res/raw/ringback_tone.mp3` in your project for this to work.
741
- // If you don't want a custom ringback, you can play a silent audio file or do nothing here.
742
741
  val ringbackUri = Uri.parse("android.resource://${appContext?.packageName}/raw/ringback_tone")
743
742
  ringbackPlayer = MediaPlayer.create(appContext, ringbackUri)
744
743
  if (ringbackPlayer == null) {
@@ -8,7 +8,7 @@ import android.content.Intent
8
8
  import android.os.Build
9
9
  import android.util.Log
10
10
  import android.os.IBinder
11
- import android.app.NotificationManager // Explicit import
11
+ import android.app.NotificationManager
12
12
 
13
13
  class CallForegroundService : Service() {
14
14
 
@@ -61,7 +61,7 @@ class CallForegroundService : Service() {
61
61
  NotificationManager.IMPORTANCE_LOW
62
62
  )
63
63
  channel.description = "Notification for ongoing calls"
64
- val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Corrected type inference and explicit import
64
+ val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
65
65
  manager.createNotificationChannel(channel)
66
66
  Log.d(TAG, "Foreground notification channel '$CHANNEL_ID' created/updated.")
67
67
  }
@@ -1,7 +1,7 @@
1
1
  package com.margelo.nitro.qusaieilouti99.callmanager
2
2
 
3
3
  import com.facebook.proguard.annotations.DoNotStrip
4
- import android.util.Log // Explicit import
4
+ import android.util.Log
5
5
  import org.json.JSONArray
6
6
  import org.json.JSONObject
7
7
 
@@ -10,7 +10,6 @@ class CallManager : HybridCallManagerSpec() {
10
10
 
11
11
  private val TAG = "CallManager"
12
12
 
13
- // Listener type now directly matches the generated Func_void_CallEventType_std__string
14
13
  private var currentListener: Func_void_CallEventType_std__string? = null
15
14
 
16
15
  override fun endCall(callId: String) {
@@ -27,14 +26,13 @@ class CallManager : HybridCallManagerSpec() {
27
26
  } ?: Log.e(TAG, "App context not set for silenceRingtone.")
28
27
  }
29
28
 
30
- // Return type changed to AudioRoutesInfo from String
31
29
  override fun getAudioDevices(): AudioRoutesInfo {
32
30
  Log.d(TAG, "getAudioDevices requested.")
33
31
  return CallEngine.getAppContext()?.let {
34
- CallEngine.getAudioDevices() // This now directly returns AudioRoutesInfo
32
+ CallEngine.getAudioDevices()
35
33
  } ?: run {
36
34
  Log.e(TAG, "App context not set for getAudioDevices. Returning empty AudioRoutesInfo.")
37
- AudioRoutesInfo(emptyArray(), "Unknown") // Return default empty data class
35
+ AudioRoutesInfo(emptyArray(), "Unknown")
38
36
  }
39
37
  }
40
38
 
@@ -52,19 +50,15 @@ class CallManager : HybridCallManagerSpec() {
52
50
  } ?: Log.e(TAG, "App context not set for keepAwake.")
53
51
  }
54
52
 
55
- // Corrected addListener signature to match generated HybridCallManagerSpec
56
53
  override fun addListener(
57
- listener: Func_void_CallEventType_std__string // Use generated type directly
58
- ): Func_void { // Return generated type
54
+ listener: Func_void_CallEventType_std__string
55
+ ): Func_void {
59
56
  Log.d(TAG, "addListener called with listener: $listener")
60
- // Store the incoming Nitro-generated listener.
61
- // It already implements the (CallEventType, String) -> Unit interface.
62
57
  currentListener = listener
63
- CallEngine.setEventHandler(listener) // Pass it directly to CallEngine
58
+ CallEngine.setEventHandler(listener)
64
59
 
65
- // Return a Nitro-generated Func_void for the remove function
66
60
  return Func_void_java {
67
- if (currentListener === listener) { // Compare against the original listener passed
61
+ if (currentListener === listener) {
68
62
  CallEngine.setEventHandler(null)
69
63
  currentListener = null
70
64
  Log.d(TAG, "Listener removed.")
@@ -74,7 +68,6 @@ class CallManager : HybridCallManagerSpec() {
74
68
  }
75
69
  }
76
70
 
77
- // Removed callType param, now extracted from callData
78
71
  override fun startOutgoingCall(callId: String, callData: String) {
79
72
  Log.d(TAG, "startOutgoingCall requested: callId=$callId, callData=$callData")
80
73
  CallEngine.getAppContext()?.let {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qusaieilouti99/call-manager",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "description": "Call manager",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",