diff --git a/README.md b/README.md
index 69ff92f..c0308f6 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,8 @@ expo-speech-recognition implements the iOS [`SFSpeechRecognizer`](https://develo
- [Transcribing audio files](#transcribing-audio-files)
- [Supported input audio formats](#supported-input-audio-formats)
- [File transcription example](#file-transcription-example)
+- [Volume metering](#volume-metering)
+ - [Volume metering example](#volume-metering-example)
- [Polyfilling the Web SpeechRecognition API](#polyfilling-the-web-speechrecognition-api)
- [Muting the beep sound on Android](#muting-the-beep-sound-on-android)
- [Improving accuracy of single-word prompts](#improving-accuracy-of-single-word-prompts)
@@ -299,6 +301,13 @@ ExpoSpeechRecognitionModule.start({
// Default: 50ms for network-based recognition, 15ms for on-device recognition
chunkDelayMillis: undefined,
},
+ // Settings for volume change events.
+ volumeChangeEventOptions: {
+ // [Default: false] Whether to emit the `volumechange` events when the input volume changes.
+ enabled: false,
+ // [Default: 100ms on iOS] The interval (in milliseconds) to emit `volumechange` events.
+ intervalMillis: 300,
+ },
});
// Stop capturing audio (and emit a final result if there is one)
@@ -312,17 +321,18 @@ ExpoSpeechRecognitionModule.abort();
Events are largely based on the [Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition). The following events are supported:
-| Event Name | Description | Notes |
-| ------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `audiostart` | Audio capturing has started | Includes the `uri` if `recordingOptions.persist` is enabled. |
-| `audioend` | Audio capturing has ended | Includes the `uri` if `recordingOptions.persist` is enabled. |
-| `end` | Speech recognition service has disconnected. | This should always be the last event dispatched, including after errors. |
-| `error` | Fired when a speech recognition error occurs. | You'll also receive an `error` event (with code "aborted") when calling `.abort()` |
-| `nomatch` | Speech recognition service returns a final result with no significant recognition. | You may have non-final results recognized. This may get emitted after cancellation. |
-| `result` | Speech recognition service returns a word or phrase has been positively recognized. | On Android, continous mode runs as a segmented session, meaning when a final result is reached, additional partial and final results will cover a new segment separate from the previous final result. On iOS, you should expect one final result before speech recognition has stopped. |
-| `speechstart` | Fired when any sound — recognizable speech or not — has been detected | On iOS, this will fire once in the session after a result has occurred |
-| `speechend` | Fired when speech recognized by the speech recognition service has stopped being detected. | Not supported yet on iOS |
-| `start` | Speech recognition has started | Use this event to indicate to the user when to speak. |
+| Event Name | Description | Notes |
+| -------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `audiostart` | Audio capturing has started | Includes the `uri` if `recordingOptions.persist` is enabled. |
+| `audioend` | Audio capturing has ended | Includes the `uri` if `recordingOptions.persist` is enabled. |
+| `end` | Speech recognition service has disconnected. | This should always be the last event dispatched, including after errors. |
+| `error` | Fired when a speech recognition error occurs. | You'll also receive an `error` event (with code "aborted") when calling `.abort()` |
+| `nomatch` | Speech recognition service returns a final result with no significant recognition. | You may have non-final results recognized. This may get emitted after cancellation. |
+| `result` | Speech recognition service returns a word or phrase has been positively recognized. | On Android, continous mode runs as a segmented session, meaning when a final result is reached, additional partial and final results will cover a new segment separate from the previous final result. On iOS, you should expect one final result before speech recognition has stopped. |
+| `speechstart` | Fired when any sound — recognizable speech or not — has been detected | On iOS, this will fire once in the session after a result has occurred |
+| `speechend` | Fired when speech recognized by the speech recognition service has stopped being detected. | Not supported yet on iOS |
+| `start` | Speech recognition has started | Use this event to indicate to the user when to speak. |
+| `volumechange` | Fired when the input volume changes. | Returns a value between -2 and 10 indicating the volume of the input audio. Consider anything below 0 to be inaudible. |
## Handling Errors
@@ -530,6 +540,43 @@ function TranscribeAudioFile() {
}
```
+## Volume metering
+
+You can use the `volumeChangeEventOptions.enabled` option to enable volume metering. This will emit a `volumechange` event with the current volume level (between -2 and 10) as a value. You can use this value to animate the volume metering of a user's voice, or to provide feedback to the user about the volume level.
+
+### Volume metering example
+
+![Volume metering example](./images/volume-metering.gif)
+
+See: [VolumeMeteringAvatar.tsx](https://github.com/jamsch/expo-speech-recognition/tree/main/example/components/VolumeMeteringAvatar.tsx) for a complete example that involves using `react-native-reanimated` to animate the volume metering.
+
+```tsx
+import { ExpoSpeechRecognitionModule } from "expo-speech-recognition";
+
+function VolumeMeteringAvatar() {
+ useSpeechRecognitionEvent("volumechange", (event) => {
+ console.log("Volume changed to:", event.value);
+ });
+
+ const handleStart = () => {
+ ExpoSpeechRecognitionModule.start({
+ lang: "en-US",
+ volumeChangeEventOptions: {
+ enabled: true,
+ intervalMillis: 300,
+ },
+ });
+ };
+
+ return (
+
+
+ Volume: {volume}
+
+ );
+}
+```
+
## Polyfilling the Web SpeechRecognition API
> [!IMPORTANT]
diff --git a/android/src/main/java/expo/modules/speechrecognition/ExpoSpeechRecognitionModule.kt b/android/src/main/java/expo/modules/speechrecognition/ExpoSpeechRecognitionModule.kt
index 9d84cd1..6db1141 100644
--- a/android/src/main/java/expo/modules/speechrecognition/ExpoSpeechRecognitionModule.kt
+++ b/android/src/main/java/expo/modules/speechrecognition/ExpoSpeechRecognitionModule.kt
@@ -86,6 +86,8 @@ class ExpoSpeechRecognitionModule : Module() {
"start",
// Called when there's results (as a string array, not API compliant)
"results",
+ // Fired when the input volume changes
+ "volumechange",
)
Function("getDefaultRecognitionService") {
@@ -325,26 +327,32 @@ class ExpoSpeechRecognitionModule : Module() {
promise: Promise,
) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
- promise.resolve(mapOf(
- "locales" to mutableListOf(),
- "installedLocales" to mutableListOf(),
- ))
+ promise.resolve(
+ mapOf(
+ "locales" to mutableListOf(),
+ "installedLocales" to mutableListOf(),
+ ),
+ )
return
}
if (options.androidRecognitionServicePackage == null && !SpeechRecognizer.isOnDeviceRecognitionAvailable(appContext)) {
- promise.resolve(mapOf(
- "locales" to mutableListOf(),
- "installedLocales" to mutableListOf(),
- ))
+ promise.resolve(
+ mapOf(
+ "locales" to mutableListOf(),
+ "installedLocales" to mutableListOf(),
+ ),
+ )
return
}
if (options.androidRecognitionServicePackage != null && !SpeechRecognizer.isRecognitionAvailable(appContext)) {
- promise.resolve(mapOf(
- "locales" to mutableListOf(),
- "installedLocales" to mutableListOf(),
- ))
+ promise.resolve(
+ mapOf(
+ "locales" to mutableListOf(),
+ "installedLocales" to mutableListOf(),
+ ),
+ )
return
}
diff --git a/android/src/main/java/expo/modules/speechrecognition/ExpoSpeechRecognitionOptions.kt b/android/src/main/java/expo/modules/speechrecognition/ExpoSpeechRecognitionOptions.kt
index b195948..46ea115 100644
--- a/android/src/main/java/expo/modules/speechrecognition/ExpoSpeechRecognitionOptions.kt
+++ b/android/src/main/java/expo/modules/speechrecognition/ExpoSpeechRecognitionOptions.kt
@@ -50,6 +50,17 @@ class SpeechRecognitionOptions : Record {
@Field
val iosCategory: Map? = null
+
+ @Field
+ val volumeChangeEventOptions: VolumeChangeEventOptions? = null
+}
+
+class VolumeChangeEventOptions : Record {
+ @Field
+ val enabled: Boolean? = false
+
+ @Field
+ val intervalMillis: Int? = null
}
class RecordingOptions : Record {
diff --git a/android/src/main/java/expo/modules/speechrecognition/ExpoSpeechService.kt b/android/src/main/java/expo/modules/speechrecognition/ExpoSpeechService.kt
index 71486db..18d73b5 100644
--- a/android/src/main/java/expo/modules/speechrecognition/ExpoSpeechService.kt
+++ b/android/src/main/java/expo/modules/speechrecognition/ExpoSpeechService.kt
@@ -50,6 +50,9 @@ class ExpoSpeechService(
private var speech: SpeechRecognizer? = null
private val mainHandler = Handler(Looper.getMainLooper())
+ private lateinit var options: SpeechRecognitionOptions
+ private var lastVolumeChangeEventTime: Long = 0L
+
/** Audio recorder for persisting audio */
private var audioRecorder: ExpoAudioRecorder? = null
@@ -108,6 +111,7 @@ class ExpoSpeechService(
/** Starts speech recognition */
fun start(options: SpeechRecognitionOptions) {
+ this.options = options
mainHandler.post {
log("Start recognition.")
@@ -119,6 +123,7 @@ class ExpoSpeechService(
delayedFileStreamer = null
recognitionState = RecognitionState.STARTING
soundState = SoundState.INACTIVE
+ lastVolumeChangeEventTime = 0L
try {
val intent = createSpeechIntent(options)
speech = createSpeechRecognizer(options)
@@ -454,6 +459,21 @@ class ExpoSpeechService(
}
override fun onRmsChanged(rmsdB: Float) {
+ if (options.volumeChangeEventOptions?.enabled != true) {
+ return
+ }
+
+ val intervalMs = options.volumeChangeEventOptions?.intervalMillis
+
+ if (intervalMs == null) {
+ sendEvent("volumechange", mapOf("value" to rmsdB))
+ } else {
+ val currentTime = System.currentTimeMillis()
+ if (currentTime - lastVolumeChangeEventTime >= intervalMs) {
+ sendEvent("volumechange", mapOf("value" to rmsdB))
+ lastVolumeChangeEventTime = currentTime
+ }
+ }
/*
val isSilent = rmsdB <= 0
diff --git a/example/App.tsx b/example/App.tsx
index 49f8aae..d4151dc 100644
--- a/example/App.tsx
+++ b/example/App.tsx
@@ -47,6 +47,7 @@ import {
AndroidOutputFormat,
IOSOutputFormat,
} from "expo-av/build/Audio";
+import { VolumeMeteringAvatar } from "./components/VolumeMeteringAvatar";
const speechRecognitionServices = getSpeechRecognitionServices();
@@ -71,7 +72,16 @@ export default function App() {
continuous: true,
requiresOnDeviceRecognition: false,
addsPunctuation: true,
- contextualStrings: ["Carlsen", "Ian Nepomniachtchi", "Praggnanandhaa"],
+ contextualStrings: [
+ "expo-speech-recognition",
+ "Carlsen",
+ "Ian Nepomniachtchi",
+ "Praggnanandhaa",
+ ],
+ volumeChangeEventOptions: {
+ enabled: false,
+ intervalMillis: 300,
+ },
});
useSpeechRecognitionEvent("result", (ev) => {
@@ -140,6 +150,10 @@ export default function App() {
+ {settings.volumeChangeEventOptions?.enabled ? (
+
+ ) : null}
+
{error ? JSON.stringify(error) : "Error messages go here"}
@@ -510,6 +524,17 @@ function GeneralSettings(props: {
checked={Boolean(settings.continuous)}
onPress={() => handleChange("continuous", !settings.continuous)}
/>
+
+
+ handleChange("volumeChangeEventOptions", {
+ enabled: !settings.volumeChangeEventOptions?.enabled,
+ intervalMillis: settings.volumeChangeEventOptions?.intervalMillis,
+ })
+ }
+ />
@@ -714,7 +739,7 @@ function AndroidSettings(props: {
onPress={() =>
handleChange("androidIntentOptions", {
...settings.androidIntentOptions,
- [key]: !settings.androidIntentOptions?.[key] ?? false,
+ [key]: !settings.androidIntentOptions?.[key],
})
}
/>
diff --git a/example/assets/avatar.png b/example/assets/avatar.png
new file mode 100644
index 0000000..2b8b658
Binary files /dev/null and b/example/assets/avatar.png differ
diff --git a/example/babel.config.js b/example/babel.config.js
index 7b9771e..2e8dd0a 100644
--- a/example/babel.config.js
+++ b/example/babel.config.js
@@ -1,9 +1,10 @@
const path = require("path");
-module.exports = function (api) {
+module.exports = (api) => {
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: [
+ "react-native-reanimated/plugin",
[
"module-resolver",
{
diff --git a/example/components/VolumeMeteringAvatar.tsx b/example/components/VolumeMeteringAvatar.tsx
new file mode 100644
index 0000000..6f9e9cd
--- /dev/null
+++ b/example/components/VolumeMeteringAvatar.tsx
@@ -0,0 +1,131 @@
+import { useSpeechRecognitionEvent } from "expo-speech-recognition";
+import { Image, StyleSheet, View } from "react-native";
+import Animated, {
+ Extrapolation,
+ interpolate,
+ useAnimatedStyle,
+ useSharedValue,
+ withTiming,
+ Easing,
+ withSpring,
+ withSequence,
+} from "react-native-reanimated";
+const avatar = require("../assets/avatar.png");
+
+const MIN_SCALE = 1;
+const MAX_SCALE = 2;
+
+/**
+ * This is an example component that uses the `volumechange` event to animate the volume metering of a user's voice.
+ */
+export function VolumeMeteringAvatar() {
+ const volumeScale = useSharedValue(MIN_SCALE);
+ const pulseScale = useSharedValue(MIN_SCALE);
+ const pulseOpacity = useSharedValue(0);
+
+ const reset = () => {
+ volumeScale.value = MIN_SCALE;
+ pulseScale.value = MIN_SCALE;
+ pulseOpacity.value = 0;
+ };
+
+ useSpeechRecognitionEvent("start", reset);
+ useSpeechRecognitionEvent("end", reset);
+
+ useSpeechRecognitionEvent("volumechange", (event) => {
+ // Don't animate anything if the volume is too low
+ if (event.value <= 1) {
+ return;
+ }
+
+ const newScale = interpolate(
+ event.value,
+ [-2, 10], // The value range is between -2 and 10
+ [MIN_SCALE, MAX_SCALE],
+ Extrapolation.CLAMP,
+ );
+
+ // Animate the volume scaling
+ volumeScale.value = withSequence(
+ withSpring(newScale, {
+ damping: 10,
+ stiffness: 150,
+ }),
+ // Scale back down, unless the volume changes again
+ withTiming(MIN_SCALE, { duration: 500 }),
+ );
+
+ // Animate the pulse (scale and fade out)
+ if (pulseOpacity.value <= 0) {
+ pulseScale.value = MIN_SCALE;
+ pulseOpacity.value = 1;
+ pulseScale.value = withTiming(MAX_SCALE, {
+ duration: 1000,
+ easing: Easing.out(Easing.quad),
+ });
+ pulseOpacity.value = withTiming(0, { duration: 1000 });
+ }
+ });
+
+ const volumeScaleStyle = useAnimatedStyle(() => ({
+ transform: [{ scale: volumeScale.value }],
+ }));
+
+ const pulseStyle = useAnimatedStyle(() => ({
+ opacity: pulseOpacity.value,
+ transform: [{ scale: pulseScale.value }],
+ }));
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ position: "relative",
+ marginVertical: 20,
+ },
+ absoluteCenteredContainer: {
+ position: "absolute",
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0,
+ justifyContent: "center",
+ alignItems: "center",
+ },
+ pulse: {
+ borderWidth: 1,
+ borderColor: "#539bf5",
+ width: 96,
+ height: 96,
+ borderRadius: 96,
+ },
+ circularBorder: {
+ backgroundColor: "#6b7280",
+ width: 96,
+ height: 96,
+ borderRadius: 96,
+ },
+ centered: {
+ justifyContent: "center",
+ alignItems: "center",
+ },
+ avatar: {
+ width: 96,
+ height: 96,
+ borderRadius: 96,
+ overflow: "hidden",
+ },
+});
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index 6cc6faf..aaf1870 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -6,7 +6,7 @@ PODS:
- ReactCommon/turbomodule/core
- EXConstants (16.0.2):
- ExpoModulesCore
- - Expo (51.0.36):
+ - Expo (51.0.37):
- ExpoModulesCore
- ExpoAsset (10.0.10):
- ExpoModulesCore
@@ -39,9 +39,9 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- - ExpoSpeechRecognition (0.2.22):
+ - ExpoSpeechRecognition (0.2.23):
- ExpoModulesCore
- - EXSplashScreen (0.27.5):
+ - EXSplashScreen (0.27.6):
- DoubleConversion
- ExpoModulesCore
- glog
@@ -1226,6 +1226,27 @@ PODS:
- React-logger (= 0.74.5)
- React-perflogger (= 0.74.5)
- React-utils (= 0.74.5)
+ - RNReanimated (3.10.1):
+ - DoubleConversion
+ - glog
+ - hermes-engine
+ - RCT-Folly (= 2024.01.01.00)
+ - RCTRequired
+ - RCTTypeSafety
+ - React-Codegen
+ - React-Core
+ - React-debug
+ - React-Fabric
+ - React-featureflags
+ - React-graphics
+ - React-ImageManager
+ - React-NativeModulesApple
+ - React-RCTFabric
+ - React-rendererdebug
+ - React-utils
+ - ReactCommon/turbomodule/bridging
+ - ReactCommon/turbomodule/core
+ - Yoga
- SocketRocket (0.7.0)
- Yoga (0.0.0)
@@ -1295,6 +1316,7 @@ DEPENDENCIES:
- React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`)
- React-utils (from `../node_modules/react-native/ReactCommon/react/utils`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
+ - RNReanimated (from `../node_modules/react-native-reanimated`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
@@ -1429,6 +1451,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/react/utils"
ReactCommon:
:path: "../node_modules/react-native/ReactCommon"
+ RNReanimated:
+ :path: "../node_modules/react-native-reanimated"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
@@ -1437,14 +1461,14 @@ SPEC CHECKSUMS:
DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5
EXAV: afa491e598334bbbb92a92a2f4dd33d7149ad37f
EXConstants: 409690fbfd5afea964e5e9d6c4eb2c2b59222c59
- Expo: 51c2d7fee7a8c7195ac257c9f9503378800d334e
+ Expo: 67b60b3b80a3c8e9f3bcaaa84d06d140229a246d
ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875
ExpoFileSystem: 80bfe850b1f9922c16905822ecbf97acd711dc51
ExpoFont: 00756e6c796d8f7ee8d211e29c8b619e75cbf238
ExpoKeepAwake: 3b8815d9dd1d419ee474df004021c69fdd316d08
ExpoModulesCore: 260ee156852434da26e782bbb993093186c5aade
- ExpoSpeechRecognition: 6ce34ee84af8950f0e472ba6f6b0b3def2553b2a
- EXSplashScreen: a7e8d13c476f9937e39d654af4235758b567a1be
+ ExpoSpeechRecognition: 078201eb1fbae87409cfecba6176edc262f75121
+ EXSplashScreen: 10b116117c9bb6a272ba782706f21dadc23f44d9
FBLazyVector: ac12dc084d1c8ec4cc4d7b3cf1b0ebda6dab85af
fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120
glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f
@@ -1496,6 +1520,7 @@ SPEC CHECKSUMS:
React-runtimescheduler: cfbe85c3510c541ec6dc815c7729b41304b67961
React-utils: f242eb7e7889419d979ca0e1c02ccc0ea6e43b29
ReactCommon: f7da14a8827b72704169a48c929bcde802698361
+ RNReanimated: 58a768c2c17a5589ef732fa6bd8d7ed0eb6df1c1
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
Yoga: 2246eea72aaf1b816a68a35e6e4b74563653ae09
diff --git a/example/package-lock.json b/example/package-lock.json
index d2ed85d..3937884 100644
--- a/example/package-lock.json
+++ b/example/package-lock.json
@@ -11,16 +11,17 @@
"@expo/metro-runtime": "~3.2.3",
"@types/dom-speech-recognition": "^0.0.4",
"babel-plugin-module-resolver": "^5.0.2",
- "expo": "~51.0.36",
+ "expo": "~51.0.37",
"expo-asset": "~10.0.10",
"expo-av": "~14.0.7",
"expo-build-properties": "~0.12.5",
"expo-file-system": "~17.0.1",
- "expo-splash-screen": "~0.27.5",
+ "expo-splash-screen": "~0.27.6",
"expo-status-bar": "~1.12.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.74.5",
+ "react-native-reanimated": "~3.10.1",
"react-native-web": "~0.19.10"
},
"devDependencies": {
@@ -42,11 +43,12 @@
}
},
"node_modules/@babel/code-frame": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
- "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz",
+ "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==",
+ "license": "MIT",
"dependencies": {
- "@babel/highlight": "^7.24.7",
+ "@babel/highlight": "^7.25.7",
"picocolors": "^1.0.0"
},
"engines": {
@@ -105,11 +107,12 @@
}
},
"node_modules/@babel/helper-annotate-as-pure": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz",
- "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz",
+ "integrity": "sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.24.7"
+ "@babel/types": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
@@ -143,18 +146,17 @@
}
},
"node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz",
- "integrity": "sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.24.7",
- "@babel/helper-environment-visitor": "^7.24.7",
- "@babel/helper-function-name": "^7.24.7",
- "@babel/helper-member-expression-to-functions": "^7.24.7",
- "@babel/helper-optimise-call-expression": "^7.24.7",
- "@babel/helper-replace-supers": "^7.24.7",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
- "@babel/helper-split-export-declaration": "^7.24.7",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.7.tgz",
+ "integrity": "sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.25.7",
+ "@babel/helper-member-expression-to-functions": "^7.25.7",
+ "@babel/helper-optimise-call-expression": "^7.25.7",
+ "@babel/helper-replace-supers": "^7.25.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7",
+ "@babel/traverse": "^7.25.7",
"semver": "^6.3.1"
},
"engines": {
@@ -222,6 +224,7 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz",
"integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==",
+ "peer": true,
"dependencies": {
"@babel/types": "^7.24.7"
},
@@ -230,24 +233,26 @@
}
},
"node_modules/@babel/helper-member-expression-to-functions": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz",
- "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.7.tgz",
+ "integrity": "sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==",
+ "license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.24.7",
- "@babel/types": "^7.24.7"
+ "@babel/traverse": "^7.25.7",
+ "@babel/types": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz",
- "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz",
+ "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==",
+ "license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.24.7",
- "@babel/types": "^7.24.7"
+ "@babel/traverse": "^7.25.7",
+ "@babel/types": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
@@ -272,20 +277,22 @@
}
},
"node_modules/@babel/helper-optimise-call-expression": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz",
- "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.7.tgz",
+ "integrity": "sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.24.7"
+ "@babel/types": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz",
- "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz",
+ "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
@@ -307,13 +314,14 @@
}
},
"node_modules/@babel/helper-replace-supers": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz",
- "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.7.tgz",
+ "integrity": "sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-environment-visitor": "^7.24.7",
- "@babel/helper-member-expression-to-functions": "^7.24.7",
- "@babel/helper-optimise-call-expression": "^7.24.7"
+ "@babel/helper-member-expression-to-functions": "^7.25.7",
+ "@babel/helper-optimise-call-expression": "^7.25.7",
+ "@babel/traverse": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
@@ -334,12 +342,13 @@
}
},
"node_modules/@babel/helper-skip-transparent-expression-wrappers": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz",
- "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.7.tgz",
+ "integrity": "sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==",
+ "license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.24.7",
- "@babel/types": "^7.24.7"
+ "@babel/traverse": "^7.25.7",
+ "@babel/types": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
@@ -357,25 +366,28 @@
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz",
- "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz",
+ "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
- "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz",
+ "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-option": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz",
- "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz",
+ "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
@@ -406,11 +418,12 @@
}
},
"node_modules/@babel/highlight": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
- "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz",
+ "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.24.7",
+ "@babel/helper-validator-identifier": "^7.25.7",
"chalk": "^2.4.2",
"js-tokens": "^4.0.0",
"picocolors": "^1.0.0"
@@ -420,9 +433,13 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz",
- "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==",
+ "version": "7.25.8",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz",
+ "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.25.8"
+ },
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -497,13 +514,14 @@
}
},
"node_modules/@babel/plugin-proposal-decorators": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.24.7.tgz",
- "integrity": "sha512-RL9GR0pUG5Kc8BUWLNDm2T5OpYwSX15r98I0IkgmRQTXuELq/OynH8xtMTMvTJFjXbMWFVTKtYkTaYQsuAwQlQ==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.7.tgz",
+ "integrity": "sha512-q1mqqqH0e1lhmsEQHV5U8OmdueBC2y0RFr2oUzZoFRtN3MvPmt2fsFRcNQAoGLTSNdHBFUYGnlgcRFhkBbKjPw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.24.7",
- "@babel/helper-plugin-utils": "^7.24.7",
- "@babel/plugin-syntax-decorators": "^7.24.7"
+ "@babel/helper-create-class-features-plugin": "^7.25.7",
+ "@babel/helper-plugin-utils": "^7.25.7",
+ "@babel/plugin-syntax-decorators": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
@@ -678,11 +696,12 @@
}
},
"node_modules/@babel/plugin-syntax-decorators": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.24.7.tgz",
- "integrity": "sha512-Ui4uLJJrRV1lb38zg1yYTmRKmiZLiftDEvZN2iq3kd9kUFU+PttmzTbAFC2ucRk/XJmtek6G23gPsuZbhrT8fQ==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.7.tgz",
+ "integrity": "sha512-oXduHo642ZhstLVYTe2z2GSJIruU0c/W3/Ghr6A5yGMsVrvdnxO1z+3pbTcT7f3/Clnt+1z8D/w1r1f1SHaCHw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.7"
+ "@babel/helper-plugin-utils": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
@@ -796,11 +815,12 @@
}
},
"node_modules/@babel/plugin-syntax-jsx": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz",
- "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.7.tgz",
+ "integrity": "sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.7"
+ "@babel/helper-plugin-utils": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1380,7 +1400,6 @@
"version": "7.22.11",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz",
"integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==",
- "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
@@ -1462,7 +1481,6 @@
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz",
"integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==",
- "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
@@ -1537,11 +1555,12 @@
}
},
"node_modules/@babel/plugin-transform-react-display-name": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz",
- "integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.7.tgz",
+ "integrity": "sha512-r0QY7NVU8OnrwE+w2IWiRom0wwsTbjx4+xH2RTd7AVdof3uurXOF+/mXHQDRk+2jIvWgSaCHKMgggfvM4dyUGA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.7"
+ "@babel/helper-plugin-utils": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1551,15 +1570,16 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz",
- "integrity": "sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.7.tgz",
+ "integrity": "sha512-vILAg5nwGlR9EXE8JIOX4NHXd49lrYbN8hnjffDtoULwpL9hUx/N55nqh2qd0q6FyNDfjl9V79ecKGvFbcSA0Q==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-annotate-as-pure": "^7.24.7",
- "@babel/helper-module-imports": "^7.24.7",
- "@babel/helper-plugin-utils": "^7.24.7",
- "@babel/plugin-syntax-jsx": "^7.24.7",
- "@babel/types": "^7.24.7"
+ "@babel/helper-annotate-as-pure": "^7.25.7",
+ "@babel/helper-module-imports": "^7.25.7",
+ "@babel/helper-plugin-utils": "^7.25.7",
+ "@babel/plugin-syntax-jsx": "^7.25.7",
+ "@babel/types": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1569,11 +1589,12 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx-development": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz",
- "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.7.tgz",
+ "integrity": "sha512-5yd3lH1PWxzW6IZj+p+Y4OLQzz0/LzlOG8vGqonHfVR3euf1vyzyMUJk9Ac+m97BH46mFc/98t9PmYLyvgL3qg==",
+ "license": "MIT",
"dependencies": {
- "@babel/plugin-transform-react-jsx": "^7.24.7"
+ "@babel/plugin-transform-react-jsx": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1611,12 +1632,13 @@
}
},
"node_modules/@babel/plugin-transform-react-pure-annotations": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz",
- "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.7.tgz",
+ "integrity": "sha512-6YTHJ7yjjgYqGc8S+CbEXhLICODk0Tn92j+vNJo07HFk9t3bjFgAKxPLFhHwF2NjmQVSI1zBRfBWUeVBa2osfA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-annotate-as-pure": "^7.24.7",
- "@babel/helper-plugin-utils": "^7.24.7"
+ "@babel/helper-annotate-as-pure": "^7.25.7",
+ "@babel/helper-plugin-utils": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1722,7 +1744,6 @@
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz",
"integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==",
- "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5"
},
@@ -1952,16 +1973,17 @@
}
},
"node_modules/@babel/preset-react": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz",
- "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.7.tgz",
+ "integrity": "sha512-GjV0/mUEEXpi1U5ZgDprMRRgajGMRW3G5FjMr5KLKD8nT2fTG8+h/klV3+6Dm5739QE+K5+2e91qFKAYI3pmRg==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.7",
- "@babel/helper-validator-option": "^7.24.7",
- "@babel/plugin-transform-react-display-name": "^7.24.7",
- "@babel/plugin-transform-react-jsx": "^7.24.7",
- "@babel/plugin-transform-react-jsx-development": "^7.24.7",
- "@babel/plugin-transform-react-pure-annotations": "^7.24.7"
+ "@babel/helper-plugin-utils": "^7.25.7",
+ "@babel/helper-validator-option": "^7.25.7",
+ "@babel/plugin-transform-react-display-name": "^7.25.7",
+ "@babel/plugin-transform-react-jsx": "^7.25.7",
+ "@babel/plugin-transform-react-jsx-development": "^7.25.7",
+ "@babel/plugin-transform-react-pure-annotations": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
@@ -2023,31 +2045,30 @@
}
},
"node_modules/@babel/template": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz",
- "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz",
+ "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==",
+ "license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.24.7",
- "@babel/parser": "^7.24.7",
- "@babel/types": "^7.24.7"
+ "@babel/code-frame": "^7.25.7",
+ "@babel/parser": "^7.25.7",
+ "@babel/types": "^7.25.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz",
- "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==",
- "dependencies": {
- "@babel/code-frame": "^7.24.7",
- "@babel/generator": "^7.24.7",
- "@babel/helper-environment-visitor": "^7.24.7",
- "@babel/helper-function-name": "^7.24.7",
- "@babel/helper-hoist-variables": "^7.24.7",
- "@babel/helper-split-export-declaration": "^7.24.7",
- "@babel/parser": "^7.24.7",
- "@babel/types": "^7.24.7",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz",
+ "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.25.7",
+ "@babel/generator": "^7.25.7",
+ "@babel/parser": "^7.25.7",
+ "@babel/template": "^7.25.7",
+ "@babel/types": "^7.25.7",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
@@ -2056,26 +2077,40 @@
}
},
"node_modules/@babel/traverse/node_modules/@babel/generator": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz",
- "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz",
+ "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.24.7",
+ "@babel/types": "^7.25.7",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
- "jsesc": "^2.5.1"
+ "jsesc": "^3.0.2"
},
"engines": {
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/traverse/node_modules/jsesc": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
+ "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/@babel/types": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz",
- "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==",
+ "version": "7.25.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz",
+ "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.24.7",
- "@babel/helper-validator-identifier": "^7.24.7",
+ "@babel/helper-string-parser": "^7.25.7",
+ "@babel/helper-validator-identifier": "^7.25.7",
"to-fast-properties": "^2.0.0"
},
"engines": {
@@ -2215,11 +2250,6 @@
"node": ">=10"
}
},
- "node_modules/@expo/cli/node_modules/@react-native/normalize-colors": {
- "version": "0.74.85",
- "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.85.tgz",
- "integrity": "sha512-pcE4i0X7y3hsAE0SpIl7t6dUc0B0NZLd1yv7ssm4FrLhWG+CGyIq4eFDXpmPU1XHmL5PPySxTAjEMiwv6tAmOw=="
- },
"node_modules/@expo/cli/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -3105,16 +3135,17 @@
}
},
"node_modules/@expo/prebuild-config": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-7.0.6.tgz",
- "integrity": "sha512-Hts+iGBaG6OQ+N8IEMMgwQElzJeSTb7iUJ26xADEHkaexsucAK+V52dM8M4ceicvbZR9q8M+ebJEGj0MCNA3dQ==",
+ "version": "7.0.8",
+ "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-7.0.8.tgz",
+ "integrity": "sha512-wH9NVg6HiwF5y9x0TxiMEeBF+ITPGDXy5/i6OUheSrKpPgb0lF1Mwzl/f2fLPXBEpl+ZXOQ8LlLW32b7K9lrNg==",
+ "license": "MIT",
"dependencies": {
"@expo/config": "~9.0.0-beta.0",
- "@expo/config-plugins": "~8.0.0-beta.0",
+ "@expo/config-plugins": "~8.0.8",
"@expo/config-types": "^51.0.0-unreleased",
"@expo/image-utils": "^0.5.0",
"@expo/json-file": "^8.3.0",
- "@react-native/normalize-colors": "0.74.84",
+ "@react-native/normalize-colors": "0.74.85",
"debug": "^4.3.1",
"fs-extra": "^9.0.0",
"resolve-from": "^5.0.0",
@@ -3129,6 +3160,7 @@
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "license": "MIT",
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
@@ -3143,6 +3175,7 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
@@ -3151,9 +3184,10 @@
}
},
"node_modules/@expo/prebuild-config/node_modules/semver": {
- "version": "7.6.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
- "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
@@ -3165,6 +3199,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
@@ -6016,9 +6051,10 @@
}
},
"node_modules/@react-native/normalize-colors": {
- "version": "0.74.84",
- "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.84.tgz",
- "integrity": "sha512-Y5W6x8cC5RuakUcTVUFNAIhUZ/tYpuqHZlRBoAuakrTwVuoNHXfQki8lj1KsYU7rW6e3VWgdEx33AfOQpdNp6A=="
+ "version": "0.74.85",
+ "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.85.tgz",
+ "integrity": "sha512-pcE4i0X7y3hsAE0SpIl7t6dUc0B0NZLd1yv7ssm4FrLhWG+CGyIq4eFDXpmPU1XHmL5PPySxTAjEMiwv6tAmOw==",
+ "license": "MIT"
},
"node_modules/@react-native/virtualized-lists": {
"version": "0.74.87",
@@ -6608,14 +6644,180 @@
}
},
"node_modules/babel-plugin-react-compiler": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-0.0.0.tgz",
- "integrity": "sha512-Kigl0V36a/6hLVH7+CCe1CCtU3mFBqBd829V//VtuG7I/pyq+B2QZJqOefd63snQmdfCryNhO9XW1FbGPBvYDA=="
+ "version": "0.0.0-experimental-592953e-20240517",
+ "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-0.0.0-experimental-592953e-20240517.tgz",
+ "integrity": "sha512-OjG1SVaeQZaJrqkMFJatg8W/MTow8Ak5rx2SI0ETQBO1XvOk/XZGMbltNCPdFJLKghBYoBjC+Y3Ap/Xr7B01mA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/generator": "7.2.0",
+ "@babel/types": "^7.19.0",
+ "chalk": "4",
+ "invariant": "^2.2.4",
+ "pretty-format": "^24",
+ "zod": "^3.22.4",
+ "zod-validation-error": "^2.1.0"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/@babel/generator": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.0.tgz",
+ "integrity": "sha512-BA75MVfRlFQG2EZgFYIwyT1r6xSkwfP2bdkY/kLZusEYWiJs4xCowab/alaEaT0wSvmVuXGqiefeBlP+7V1yKg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.2.0",
+ "jsesc": "^2.5.1",
+ "lodash": "^4.17.10",
+ "source-map": "^0.5.0",
+ "trim-right": "^1.0.1"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/@jest/types": {
+ "version": "24.9.0",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz",
+ "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^1.1.1",
+ "@types/yargs": "^13.0.0"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/@types/istanbul-reports": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz",
+ "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "*",
+ "@types/istanbul-lib-report": "*"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/@types/yargs": {
+ "version": "13.0.12",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz",
+ "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/chalk/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/pretty-format": {
+ "version": "24.9.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz",
+ "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "^24.9.0",
+ "ansi-regex": "^4.0.0",
+ "ansi-styles": "^3.2.0",
+ "react-is": "^16.8.4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
},
"node_modules/babel-plugin-react-native-web": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.12.tgz",
- "integrity": "sha512-eYZ4+P6jNcB37lObWIg0pUbi7+3PKoU1Oie2j0C8UF3cXyXoR74tO2NBjI/FORb2LJyItJZEAmjU5pSaJYEL1w=="
+ "integrity": "sha512-eYZ4+P6jNcB37lObWIg0pUbi7+3PKoU1Oie2j0C8UF3cXyXoR74tO2NBjI/FORb2LJyItJZEAmjU5pSaJYEL1w==",
+ "license": "MIT"
},
"node_modules/babel-plugin-transform-flow-enums": {
"version": "0.0.2",
@@ -6626,9 +6828,10 @@
}
},
"node_modules/babel-preset-expo": {
- "version": "11.0.14",
- "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-11.0.14.tgz",
- "integrity": "sha512-4BVYR0Sc2sSNxYTiE/OLSnPiOp+weFNy8eV+hX3aD6YAIbBnw+VubKRWqJV/sOJauzOLz0SgYAYyFciYMqizRA==",
+ "version": "11.0.15",
+ "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-11.0.15.tgz",
+ "integrity": "sha512-rgiMTYwqIPULaO7iZdqyL7aAff9QLOX6OWUtLZBlOrOTreGY1yHah/5+l8MvI6NVc/8Zj5LY4Y5uMSnJIuzTLw==",
+ "license": "MIT",
"dependencies": {
"@babel/plugin-proposal-decorators": "^7.12.9",
"@babel/plugin-transform-export-namespace-from": "^7.22.11",
@@ -6637,7 +6840,7 @@
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.23.0",
"@react-native/babel-preset": "0.74.87",
- "babel-plugin-react-compiler": "^0.0.0-experimental-592953e-20240517",
+ "babel-plugin-react-compiler": "0.0.0-experimental-592953e-20240517",
"babel-plugin-react-native-web": "~0.19.10",
"react-refresh": "^0.14.2"
}
@@ -7917,9 +8120,10 @@
}
},
"node_modules/expo": {
- "version": "51.0.36",
- "resolved": "https://registry.npmjs.org/expo/-/expo-51.0.36.tgz",
- "integrity": "sha512-eQIC0l6fz3p4cU/hV8+QcyKSacyROhaoA1oohfCD6I3F09dxmC8b3SESpzGqHfuq8wsgcUc4q8ckX7ec25IV1g==",
+ "version": "51.0.37",
+ "resolved": "https://registry.npmjs.org/expo/-/expo-51.0.37.tgz",
+ "integrity": "sha512-zMdfTiGNgNWG0HOOFA3zRreS94iQ7fDxxgEIR6wdQCbncTpbeYj+5mscTAlHE9JJ+oBkcNyJXrLSjE/YVbFERg==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/cli": "0.18.30",
@@ -7927,7 +8131,7 @@
"@expo/config-plugins": "8.0.10",
"@expo/metro-config": "0.18.11",
"@expo/vector-icons": "^14.0.3",
- "babel-preset-expo": "~11.0.14",
+ "babel-preset-expo": "~11.0.15",
"expo-asset": "~10.0.10",
"expo-file-system": "~17.0.1",
"expo-font": "~12.0.10",
@@ -8149,11 +8353,12 @@
}
},
"node_modules/expo-splash-screen": {
- "version": "0.27.5",
- "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-0.27.5.tgz",
- "integrity": "sha512-9rdZuLkFCfgJBxrheUsOEOIW6Rp+9NVlpSE0hgXQwbTCLTncf00IHSE8/L2NbFyeDLNjof1yZBppaV7tXHRUzA==",
+ "version": "0.27.6",
+ "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-0.27.6.tgz",
+ "integrity": "sha512-joUwZQS48k3VMnucQ0Y8Dle1t1FyIvluQA4kjuPx2x7l2dRrfctbo34ahTnC0p1o2go5oN2iEnSTOElY4wRQHw==",
+ "license": "MIT",
"dependencies": {
- "@expo/prebuild-config": "7.0.6"
+ "@expo/prebuild-config": "7.0.8"
},
"peerDependencies": {
"expo": "*"
@@ -12387,6 +12592,27 @@
}
}
},
+ "node_modules/react-native-reanimated": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.10.1.tgz",
+ "integrity": "sha512-sfxg6vYphrDc/g4jf/7iJ7NRi+26z2+BszPmvmk0Vnrz6FL7HYljJqTf531F1x6tFmsf+FEAmuCtTUIXFLVo9w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/plugin-transform-arrow-functions": "^7.0.0-0",
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0",
+ "@babel/plugin-transform-optional-chaining": "^7.0.0-0",
+ "@babel/plugin-transform-shorthand-properties": "^7.0.0-0",
+ "@babel/plugin-transform-template-literals": "^7.0.0-0",
+ "@babel/preset-typescript": "^7.16.7",
+ "convert-source-map": "^2.0.0",
+ "invariant": "^2.2.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0",
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/react-native-web": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.19.12.tgz",
@@ -13860,6 +14086,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/trim-right": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
+ "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
@@ -14580,6 +14815,27 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zod": {
+ "version": "3.23.8",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
+ "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-2.1.0.tgz",
+ "integrity": "sha512-VJh93e2wb4c3tWtGgTa0OF/dTt/zoPCPzXq4V11ZjxmEAFaPi/Zss1xIZdEB5RD8GD00U0/iVXgqkF77RV7pdQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.18.0"
+ }
}
}
}
diff --git a/example/package.json b/example/package.json
index e37c889..635de1b 100644
--- a/example/package.json
+++ b/example/package.json
@@ -13,17 +13,18 @@
"@expo/metro-runtime": "~3.2.3",
"@types/dom-speech-recognition": "^0.0.4",
"babel-plugin-module-resolver": "^5.0.2",
- "expo": "~51.0.36",
+ "expo": "~51.0.37",
"expo-asset": "~10.0.10",
"expo-av": "~14.0.7",
"expo-build-properties": "~0.12.5",
"expo-file-system": "~17.0.1",
- "expo-splash-screen": "~0.27.5",
+ "expo-splash-screen": "~0.27.6",
"expo-status-bar": "~1.12.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.74.5",
- "react-native-web": "~0.19.10"
+ "react-native-web": "~0.19.10",
+ "react-native-reanimated": "~3.10.1"
},
"devDependencies": {
"@babel/core": "^7.24.0",
diff --git a/images/volume-metering.gif b/images/volume-metering.gif
new file mode 100644
index 0000000..a137f59
Binary files /dev/null and b/images/volume-metering.gif differ
diff --git a/ios/ExpoSpeechRecognitionModule.swift b/ios/ExpoSpeechRecognitionModule.swift
index 80438d8..fa05332 100644
--- a/ios/ExpoSpeechRecognitionModule.swift
+++ b/ios/ExpoSpeechRecognitionModule.swift
@@ -93,7 +93,9 @@ public class ExpoSpeechRecognitionModule: Module {
// intent to recognize grammars associated with the current SpeechRecognition
"start",
// Called when there's results (as a string array, not API compliant)
- "result"
+ "result",
+ // Fired when the input volume changes
+ "volumechange"
)
OnCreate {
@@ -193,6 +195,9 @@ public class ExpoSpeechRecognitionModule: Module {
} else {
self?.sendEvent("audioend", ["uri": nil])
}
+ },
+ volumeChangeHandler: { [weak self] value in
+ self?.sendEvent("volumechange", ["value": value])
}
)
} catch {
@@ -443,7 +448,7 @@ public class ExpoSpeechRecognitionModule: Module {
previousResultHadTranscriptions = !previousResult.transcriptions.isEmpty
}
- if !previousResultWasFinal || !previousResultHadTranscriptions {
+ if !previousResultWasFinal || !previousResultHadTranscriptions {
// https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/nomatch_event
// The nomatch event of the Web Speech API is fired
// when the speech recognition service returns a final result with no significant recognition.
diff --git a/ios/ExpoSpeechRecognizer.swift b/ios/ExpoSpeechRecognizer.swift
index 9d3f5b5..3a39cc0 100644
--- a/ios/ExpoSpeechRecognizer.swift
+++ b/ios/ExpoSpeechRecognizer.swift
@@ -1,4 +1,5 @@
import AVFoundation
+import Accelerate
import Foundation
import Speech
@@ -24,7 +25,6 @@ enum RecognizerError: Error {
}
actor ExpoSpeechRecognizer: ObservableObject {
-
private var options: SpeechRecognitionOptions?
private var audioEngine: AVAudioEngine?
private var request: SFSpeechRecognitionRequest?
@@ -41,6 +41,7 @@ actor ExpoSpeechRecognizer: ObservableObject {
@MainActor var endHandler: (() -> Void)?
@MainActor var audioEndHandler: ((String?) -> Void)?
+ @MainActor var volumeChangeHandler: ((Float) -> Void)?
/// Initializes a new speech recognizer. If this is the first time you've used the class, it
/// requests access to the speech recognizer and the microphone.
@@ -120,6 +121,7 @@ actor ExpoSpeechRecognizer: ObservableObject {
return recognizer?.locale.identifier
}
+ // Update the start method signature to include volumeChangeHandler
@MainActor func start(
options: SpeechRecognitionOptions,
resultHandler: @escaping (SFSpeechRecognitionResult) -> Void,
@@ -128,10 +130,12 @@ actor ExpoSpeechRecognizer: ObservableObject {
startHandler: @escaping (() -> Void),
speechStartHandler: @escaping (() -> Void),
audioStartHandler: @escaping (String?) -> Void,
- audioEndHandler: @escaping (String?) -> Void
+ audioEndHandler: @escaping (String?) -> Void,
+ volumeChangeHandler: @escaping (Float) -> Void
) {
self.endHandler = endHandler
self.audioEndHandler = audioEndHandler
+ self.volumeChangeHandler = volumeChangeHandler
Task {
await startRecognizer(
options: options,
@@ -282,7 +286,12 @@ actor ExpoSpeechRecognizer: ObservableObject {
mixerNode: mixerNode,
options: options,
request: request,
- audioFileRef: self.audioFileRef
+ audioFileRef: self.audioFileRef,
+ volumeChangeHandler: { volume in
+ Task { @MainActor in
+ self.volumeChangeHandler?(volume)
+ }
+ }
)
}
@@ -416,6 +425,11 @@ actor ExpoSpeechRecognizer: ObservableObject {
stoppedListening = false
task?.cancel()
audioEngine?.stop()
+ // map through all the attached nodes
+ // and remove the tap
+ audioEngine?.attachedNodes.forEach(
+ { $0.removeTap(onBus: 0) }
+ )
audioEngine?.inputNode.removeTap(onBus: 0)
audioEngine?.inputNode.reset()
audioEngine?.reset()
@@ -515,7 +529,8 @@ actor ExpoSpeechRecognizer: ObservableObject {
mixerNode: AVAudioMixerNode,
options: SpeechRecognitionOptions,
request: SFSpeechRecognitionRequest,
- audioFileRef: ExtAudioFileRef?
+ audioFileRef: ExtAudioFileRef?,
+ volumeChangeHandler: ((Float) -> Void)?
) throws {
guard let audioBufferRequest = request as? SFSpeechAudioBufferRecognitionRequest else {
throw RecognizerError.invalidAudioSource
@@ -564,10 +579,77 @@ actor ExpoSpeechRecognizer: ObservableObject {
}
}
+ // Install separate tap with a longer buffer size to listen for volume changes
+ if options.volumeChangeEventOptions?.enabled == true {
+ let desiredDuration: TimeInterval =
+ TimeInterval(options.volumeChangeEventOptions?.intervalMillis ?? 100) / 1000.0
+ let bufferSize = AVAudioFrameCount(audioFormat.sampleRate * desiredDuration)
+
+ let volumeMixerNode = AVAudioMixerNode()
+ audioEngine.attach(volumeMixerNode)
+ audioEngine.connect(mixerNode, to: volumeMixerNode, format: audioFormat)
+
+ volumeMixerNode.installTap(
+ onBus: 0,
+ bufferSize: bufferSize,
+ format: audioFormat
+ ) { buffer, when in
+ guard let power = Self.calculatePower(buffer: buffer) else { return }
+
+ let minDb: Float = -60.0
+ let maxDb: Float = 0.0
+ let normalized: Float = (power - minDb) / (maxDb - minDb) // Normalized to 0.0 - 1.0
+ let clampedNormalized = min(max(normalized, 0.0), 1.0)
+
+ let scaledValue = clampedNormalized * (10 - (-2)) + (-2) // Scaled to -2 - 10
+
+ // Send the volume change event
+ volumeChangeHandler?(scaledValue)
+ }
+ }
+
audioEngine.prepare()
try audioEngine.start()
}
+ private static func calculatePower(buffer: AVAudioPCMBuffer) -> Float? {
+ // let channelCount = Int(buffer.format.channelCount)
+ let length = vDSP_Length(buffer.frameLength)
+ let channel = 0
+
+ if let floatData = buffer.floatChannelData {
+ return calculatePowers(data: floatData[channel], strideFrames: buffer.stride, length: length)
+ } else if let int16Data = buffer.int16ChannelData {
+ // Convert the data from int16 to float values before calculating the power values.
+ var floatChannelData: [Float] = Array(repeating: Float(0.0), count: Int(buffer.frameLength))
+ vDSP_vflt16(int16Data[channel], buffer.stride, &floatChannelData, buffer.stride, length)
+ var scalar = Float(INT16_MAX)
+ vDSP_vsdiv(floatChannelData, buffer.stride, &scalar, &floatChannelData, buffer.stride, length)
+ return calculatePowers(data: floatChannelData, strideFrames: buffer.stride, length: length)
+ } else if let int32Data = buffer.int32ChannelData {
+ // Convert the data from int32 to float values before calculating the power values.
+ var floatChannelData: [Float] = Array(repeating: Float(0.0), count: Int(buffer.frameLength))
+ vDSP_vflt32(int32Data[channel], buffer.stride, &floatChannelData, buffer.stride, length)
+ var scalar = Float(INT32_MAX)
+ vDSP_vsdiv(floatChannelData, buffer.stride, &scalar, &floatChannelData, buffer.stride, length)
+ return calculatePowers(data: floatChannelData, strideFrames: buffer.stride, length: length)
+ }
+
+ return nil
+ }
+
+ private static func calculatePowers(
+ data: UnsafePointer, strideFrames: Int, length: vDSP_Length
+ ) -> Float? {
+ let kMinLevel: Float = 1e-7 // -160 dB
+ var max: Float = 0.0
+ vDSP_maxv(data, strideFrames, &max, length)
+ if max < kMinLevel {
+ max = kMinLevel
+ }
+ return 20.0 * log10(max)
+ }
+
/// Downsamples the audio buffer to a file ref
private static func downsampleToFile(
buffer: AVAudioPCMBuffer,
diff --git a/ios/SpeechRecognitionOptions.swift b/ios/SpeechRecognitionOptions.swift
index 69f98fd..79021ab 100644
--- a/ios/SpeechRecognitionOptions.swift
+++ b/ios/SpeechRecognitionOptions.swift
@@ -34,6 +34,17 @@ struct SpeechRecognitionOptions: Record {
@Field
var iosCategory: SetCategoryOptions? = nil
+
+ @Field
+ var volumeChangeEventOptions: VolumeChangeEventOptions? = nil
+}
+
+struct VolumeChangeEventOptions: Record {
+ @Field
+ var enabled: Bool? = false
+
+ @Field
+ var intervalMillis: Int? = nil
}
enum IOSTaskHint: String, Enumerable {
diff --git a/src/ExpoSpeechRecognitionModule.types.ts b/src/ExpoSpeechRecognitionModule.types.ts
index a83e839..d14216e 100644
--- a/src/ExpoSpeechRecognitionModule.types.ts
+++ b/src/ExpoSpeechRecognitionModule.types.ts
@@ -127,6 +127,14 @@ export type ExpoSpeechRecognitionNativeEventMap = {
end: null;
soundstart: null;
soundend: null;
+ volumechange: {
+ /**
+ * A float value between -2 and 10 indicating the volume of the input audio
+ *
+ * Consider anything below 0 to be inaudible
+ */
+ value: number;
+ };
};
export type ExpoSpeechRecognitionOptions = {
@@ -234,6 +242,26 @@ export type ExpoSpeechRecognitionOptions = {
* Docs: https://developer.apple.com/documentation/avfaudio/avaudiosession/category
*/
iosCategory?: SetCategoryOptions;
+
+ /**
+ * Settings for volume change events.
+ */
+ volumeChangeEventOptions?: {
+ /**
+ * Whether to emit volume change events.
+ *
+ * Default: false
+ */
+ enabled?: boolean;
+ /**
+ * Specifies the interval (in milliseconds) to emit `volumechange` events.
+ *
+ * Default: 100ms on iOS
+ *
+ * Increasing this value will improve performance
+ */
+ intervalMillis?: number;
+ };
};
export type IOSTaskHintValue = (typeof TaskHintIOS)[keyof typeof TaskHintIOS];