From 862213dae9252ee9961bda1e0c90ce05b7e638b5 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Thu, 19 Feb 2026 03:08:31 -0300 Subject: [PATCH 01/45] chore(barcodeScanning): add native deps for barcodeScanning --- VisionCameraMLKit.podspec | 2 +- android/build.gradle | 2 +- example/android/build.gradle | 2 +- example/ios/Podfile | 2 +- example/ios/Podfile.lock | 13 +++++++++++-- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/VisionCameraMLKit.podspec b/VisionCameraMLKit.podspec index e7625ab..63ab2c3 100644 --- a/VisionCameraMLKit.podspec +++ b/VisionCameraMLKit.podspec @@ -14,7 +14,7 @@ default_config = { 'poseDetection' => false, 'poseDetectionAccurate' => false, 'selfieSegmentation' => false, - 'barcodeScanning' => false, + 'barcodeScanning' => true, 'imageLabeling' => false, 'objectDetection' => false, 'digitalInkRecognition' => false, diff --git a/android/build.gradle b/android/build.gradle index 2dfea3f..f7cc6c2 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -42,7 +42,7 @@ def getMLKitConfig() { selfieSegmentation : false, subjectSegmentation : false, documentScanner : false, - barcodeScanning : false, + barcodeScanning : true, imageLabeling : false, objectDetection : false, digitalInkRecognition : false, diff --git a/example/android/build.gradle b/example/android/build.gradle index 19c421d..2f2de8a 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -21,7 +21,7 @@ buildscript { selfieSegmentation : false, subjectSegmentation : false, documentScanner : false, - barcodeScanning : false, + barcodeScanning : true, imageLabeling : false, objectDetection : false, digitalInkRecognition : false, diff --git a/example/ios/Podfile b/example/ios/Podfile index 2bfec14..62887aa 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -10,7 +10,7 @@ $VisionCameraMLKit = { 'poseDetection' => false, 'poseDetectionAccurate' => false, 'selfieSegmentation' => false, - 'barcodeScanning' => false, + 'barcodeScanning' => true, 'imageLabeling' => false, 'objectDetection' => false, 'digitalInkRecognition' => false, diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 0788f76..e77f223 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -9,6 +9,9 @@ PODS: - GoogleUtilities/Environment (~> 7.7) - nanopb (< 2.30911.0, >= 2.30908.0) - PromisesObjC (< 3.0, >= 1.2) + - GoogleMLKit/BarcodeScanning (5.0.0): + - GoogleMLKit/MLKitCore + - MLKitBarcodeScanning (~> 4.0.0) - GoogleMLKit/MLKitCore (5.0.0): - MLKitCommon (~> 10.0.0) - GoogleMLKit/TextRecognition (5.0.0): @@ -55,6 +58,9 @@ PODS: - hermes-engine/Pre-built (= 0.81.1) - hermes-engine/Pre-built (0.81.1) - MLImage (1.0.0-beta5) + - MLKitBarcodeScanning (4.0.0): + - MLKitCommon (~> 10.0) + - MLKitVision (~> 6.0) - MLKitCommon (10.0.0): - GoogleDataTransport (~> 9.0) - GoogleToolboxForMac/Logger (~> 2.1) @@ -2943,6 +2949,7 @@ PODS: - fast_float - fmt - glog + - GoogleMLKit/BarcodeScanning - GoogleMLKit/TextRecognition - GoogleMLKit/TextRecognitionChinese - GoogleMLKit/TextRecognitionDevanagari @@ -3070,6 +3077,7 @@ SPEC REPOS: - GoogleUtilitiesComponents - GTMSessionFetcher - MLImage + - MLKitBarcodeScanning - MLKitCommon - MLKitTextRecognition - MLKitTextRecognitionChinese @@ -3268,6 +3276,7 @@ SPEC CHECKSUMS: GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 hermes-engine: 4f8246b1f6d79f625e0d99472d1f3a71da4d28ca MLImage: 1824212150da33ef225fbd3dc49f184cf611046c + MLKitBarcodeScanning: 9cb0ec5ec65bbb5db31de4eba0a3289626beab4e MLKitCommon: afcd11b6c0735066a0dde8b4bf2331f6197cbca2 MLKitTextRecognition: 91706574e4db9b48cd2e66cbbcc6b58956156b0a MLKitTextRecognitionChinese: 19d3a70799f17cf2115b6381964b87e874d96dcd @@ -3354,9 +3363,9 @@ SPEC CHECKSUMS: RNWorklets: ab7740c2a152f77ff76a40d52be860d8f128fab1 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 VisionCamera: 47f10351a1b6a8d251b90986ae72ca5f52331954 - VisionCameraMLKit: 218fa89d2b7fc41d4c18e7d02388acfee540f414 + VisionCameraMLKit: 573c04788106c92549faf3a46eea1771f2ac6c49 Yoga: fa23995c18b65978347b096d0836f4f5093df545 -PODFILE CHECKSUM: 41a62ebecc21ff87e3b5d0c2e79112ee681909eb +PODFILE CHECKSUM: 1e33379caa52806ba900154e8eb803a55bbce31c COCOAPODS: 1.16.2 From f101af6cd830bb7ea5325248aa8c252aff1e9083 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Thu, 19 Feb 2026 03:17:39 -0300 Subject: [PATCH 02/45] docs(README): add quote to md line --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e5877e..abf9a00 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ console.log(result.blocks); - `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` - `invertColors?: boolean` -The native bridge normalizes URIs (`file://` is removed on iOS and added on Android if missing). Supported formats: JPEG, PNG, WebP. +> The native bridge normalizes URIs (`file://` is removed on iOS and added on Android if missing). Supported formats: JPEG, PNG, WebP. ### Feature Utilities From 137aef9242456c728a79c04fd843eb3d0911c94c Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Thu, 19 Feb 2026 03:20:46 -0300 Subject: [PATCH 03/45] docs(README): sorted features roadmap --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index abf9a00..59cf122 100644 --- a/README.md +++ b/README.md @@ -248,13 +248,13 @@ The library works correctly on **physical iOS devices** and on the **iOS Simulat | # | Feature | Status | Platform | | --- | --------------------------------- | ------------------------------------------ | ------------------------------------------------- | | 0 | **Text recognition v2** | [![complete][complete]][complete] | [![android][android]][android] [![ios][ios]][ios] | -| 1 | **Face detection** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | -| 2 | **Face mesh detection** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] | -| 3 | **Pose detection** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | -| 4 | **Selfie segmentation** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | -| 5 | **Subject segmentation** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] | -| 6 | **Document scanner** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] | -| 7 | **Barcode scanning** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | +| 1 | **Barcode scanning** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | +| 2 | **Face detection** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | +| 3 | **Face mesh detection** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] | +| 4 | **Pose detection** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | +| 5 | **Selfie segmentation** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | +| 6 | **Subject segmentation** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] | +| 7 | **Document scanner** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] | | 8 | **Image labeling** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | | 9 | **Object detection and tracking** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | | 10 | **Digital ink recognition** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | From b9e8a1480f7a4c62ef04f4cd22d55277036ced8c Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Thu, 19 Feb 2026 03:23:21 -0300 Subject: [PATCH 04/45] feat(example/HomeScreen): improve UI by sorting list by status complete --- example/src/screens/HomeScreen.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/example/src/screens/HomeScreen.tsx b/example/src/screens/HomeScreen.tsx index 183503c..2babf02 100644 --- a/example/src/screens/HomeScreen.tsx +++ b/example/src/screens/HomeScreen.tsx @@ -155,12 +155,23 @@ const HomeScreen = () => { const deferredSearchQuery = useDeferredValue(searchQuery); const filteredData = useMemo(() => { + const sortedData = [...data].sort((left, right) => { + const leftIsComplete = left.status === 'complete'; + const rightIsComplete = right.status === 'complete'; + + if (leftIsComplete === rightIsComplete) { + return 0; + } + + return leftIsComplete ? -1 : 1; + }); + if (!deferredSearchQuery.trim()) { - return data; + return sortedData; } const query = deferredSearchQuery.toLowerCase(); - return data.filter( + return sortedData.filter( (item) => item.title.toLowerCase().includes(query) || item.description?.toLowerCase().includes(query) From b5769485d899683566034f7c3ba68201816878e7 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Thu, 19 Feb 2026 03:34:14 -0300 Subject: [PATCH 05/45] feat(SectionPicker): add multiple options for picker --- example/src/components/ui/SectionPicker.tsx | 74 +++++++++++++++------ 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/example/src/components/ui/SectionPicker.tsx b/example/src/components/ui/SectionPicker.tsx index 7d5dd65..d8563f8 100644 --- a/example/src/components/ui/SectionPicker.tsx +++ b/example/src/components/ui/SectionPicker.tsx @@ -17,30 +17,61 @@ type SectionPickerOption = { value: T; }; -type SectionPickerProps = { - label: string; - description?: string; - value: T; - options: SectionPickerOption[]; - onValueChange: (value: T) => void; - disabled?: boolean; -}; +type SectionPickerProps = + | { + label: string; + description?: string; + value: T; + options: SectionPickerOption[]; + onValueChange: (value: T) => void; + disabled?: boolean; + multiple?: false; + } + | { + label: string; + description?: string; + value: T[]; + options: SectionPickerOption[]; + onValueChange: (value: T[]) => void; + disabled?: boolean; + multiple: true; + }; -const SectionPicker = ({ - label, - description, - value, - options, - onValueChange, - disabled = false, -}: SectionPickerProps) => { +const SectionPicker = (props: SectionPickerProps) => { + const { label, description, options, disabled = false } = props; + const isMultiple = props.multiple === true; const { theme } = useTheme(); const [isPickerVisible, setPickerVisible] = useState(false); - const selectedOption = options.find((opt) => opt.value === value); + const selectedValues = Array.isArray(props.value) + ? props.value + : [props.value]; + const selectedOptions = options.filter((opt) => + selectedValues.includes(opt.value) + ); + + const selectedLabel = (() => { + if (isMultiple) { + if (selectedOptions.length === 0) return 'Select'; + if (selectedOptions.length <= 2) { + return selectedOptions.map((opt) => opt.label).join(', '); + } + return `${selectedOptions.length} selected`; + } + return selectedOptions[0]?.label; + })(); const handleSelect = (selectedValue: T) => { - onValueChange(selectedValue); + if (isMultiple) { + const currentValues = Array.isArray(props.value) ? props.value : []; + const isSelected = currentValues.includes(selectedValue); + const nextValues = isSelected + ? currentValues.filter((item) => item !== selectedValue) + : [...currentValues, selectedValue]; + props.onValueChange(nextValues); + return; + } + props.onValueChange(selectedValue as T); setPickerVisible(false); }; @@ -85,7 +116,7 @@ const SectionPicker = ({ variant="sm" style={[styles.valueText, { color: theme.colors.primary }]} > - {selectedOption?.label} + {selectedLabel} @@ -129,7 +160,7 @@ const SectionPicker = ({ {options.map((option) => { - const isSelected = option.value === value; + const isSelected = selectedValues.includes(option.value); return ( Date: Thu, 19 Feb 2026 03:35:00 -0300 Subject: [PATCH 06/45] perf(UI): optimize disabled visibility --- example/src/components/ui/SectionImageInput.tsx | 2 +- example/src/components/ui/SectionSlider.tsx | 3 +-- example/src/components/ui/SectionSwitch.tsx | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/example/src/components/ui/SectionImageInput.tsx b/example/src/components/ui/SectionImageInput.tsx index 36596d7..26c94d8 100644 --- a/example/src/components/ui/SectionImageInput.tsx +++ b/example/src/components/ui/SectionImageInput.tsx @@ -144,7 +144,7 @@ const styles = StyleSheet.create({ marginVertical: 12, }, disabled: { - opacity: 0.8, + opacity: 0.6, }, }); diff --git a/example/src/components/ui/SectionSlider.tsx b/example/src/components/ui/SectionSlider.tsx index 84f2b4a..02886cd 100644 --- a/example/src/components/ui/SectionSlider.tsx +++ b/example/src/components/ui/SectionSlider.tsx @@ -108,8 +108,7 @@ const styles = StyleSheet.create({ height: 32, }, disabled: { - opacity: 0.8, - filter: 'brightness(0.9)', + opacity: 0.6, }, }); diff --git a/example/src/components/ui/SectionSwitch.tsx b/example/src/components/ui/SectionSwitch.tsx index 73adeed..8f4cd1c 100644 --- a/example/src/components/ui/SectionSwitch.tsx +++ b/example/src/components/ui/SectionSwitch.tsx @@ -76,8 +76,7 @@ const styles = StyleSheet.create({ marginTop: 2, }, disabled: { - opacity: 0.8, - filter: 'brightness(0.9)', + opacity: 0.6, }, }); From ebb9e4d07a082296d78749613ac0e4835f1200f1 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Thu, 19 Feb 2026 03:36:23 -0300 Subject: [PATCH 07/45] perf: optimize types and interfaces --- example/src/components/views/CameraView.tsx | 9 +++++---- example/src/screens/ImageScreen.tsx | 3 ++- example/src/screens/SettingsScreen.tsx | 4 +++- example/src/stores/imageProcessingOptionsStore.ts | 8 ++------ example/src/stores/pluginOptionsStore.ts | 8 ++------ 5 files changed, 14 insertions(+), 18 deletions(-) diff --git a/example/src/components/views/CameraView.tsx b/example/src/components/views/CameraView.tsx index e4eb7f9..388dbb4 100644 --- a/example/src/components/views/CameraView.tsx +++ b/example/src/components/views/CameraView.tsx @@ -155,11 +155,12 @@ const CameraView = forwardRef( let resultObject: any = null; - if (pluginId === PLUGIN_ID.TEXT_RECOGNITION) { - const withArguments = { - outputOrientation: frameOutputOrientation.value, - }; + // Shared arguments for all plugins + const withArguments = { + outputOrientation: frameOutputOrientation.value, + }; + if (pluginId === PLUGIN_ID.TEXT_RECOGNITION) { resultObject = textRecognitionPlugin.textRecognition( frame, withArguments diff --git a/example/src/screens/ImageScreen.tsx b/example/src/screens/ImageScreen.tsx index 07a88cd..fe36e78 100644 --- a/example/src/screens/ImageScreen.tsx +++ b/example/src/screens/ImageScreen.tsx @@ -183,7 +183,8 @@ const ImageScreen = () => { label="Language" description="Select the language for text recognition." value={ - imageProcessingOptions[PLUGIN_ID.TEXT_RECOGNITION].language + imageProcessingOptions[PLUGIN_ID.TEXT_RECOGNITION].language ?? + 'LATIN' } options={[ { label: 'Latin', value: 'LATIN' }, diff --git a/example/src/screens/SettingsScreen.tsx b/example/src/screens/SettingsScreen.tsx index 893384d..8988225 100644 --- a/example/src/screens/SettingsScreen.tsx +++ b/example/src/screens/SettingsScreen.tsx @@ -96,7 +96,9 @@ const SettingsScreen = () => { ; }; type ImageProcessingOptionsState = { diff --git a/example/src/stores/pluginOptionsStore.ts b/example/src/stores/pluginOptionsStore.ts index eb3bb39..17e898c 100644 --- a/example/src/stores/pluginOptionsStore.ts +++ b/example/src/stores/pluginOptionsStore.ts @@ -1,16 +1,12 @@ import type { MLKitBaseOptions as SharedPluginOptions, - TextRecognitionLanguage, + TextRecognitionOptions, } from 'react-native-vision-camera-mlkit'; import { create } from 'zustand'; import { PLUGIN_ID } from '../constants/PLUGINS'; -type TextRecognitionOptions = { - language: TextRecognitionLanguage; -}; - type SpecificPluginOptions = { - [PLUGIN_ID.TEXT_RECOGNITION]: TextRecognitionOptions; + [PLUGIN_ID.TEXT_RECOGNITION]: Pick; }; type PluginOptionsState = { From 3b153683a46eef1de4d24d74573e219542035a34 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Thu, 19 Feb 2026 03:53:39 -0300 Subject: [PATCH 08/45] fix(android): kotlin code quality fixes --- .../handlers/StaticTextRecognitionHandler.kt | 5 ----- .../bridge/plugins/BaseMLKitPlugin.kt | 1 + .../bridge/registry/PluginRegistry.kt | 3 --- .../visioncameramlkit/domain/models/Orientation.kt | 1 + .../modules/VisionCameraMLKitModule.kt | 14 +++++++++++--- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt b/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt index 2f32c28..033703f 100644 --- a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt +++ b/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt @@ -53,18 +53,14 @@ class StaticTextRecognitionHandler( return@launch } - // Parse options val imageOptions = parseImageOptions(options) - // Create text recognition options val languageString = options.getString("language") ?: "LATIN" val language = parseLanguage(languageString) val textOptions = TextRecognitionOptions(language = language) - // Get use case for the language val recognizeTextUseCase = getRecognizeTextUseCase(language) - // Process the image val result = recognizeTextUseCase.execute( imageFile, @@ -72,7 +68,6 @@ class StaticTextRecognitionHandler( textOptions, ) - // Serialize result val serializedResult = TextRecognitionSerializer.toWritableMap(result) promise.resolve(serializedResult) diff --git a/android/src/main/java/com/visioncameramlkit/bridge/plugins/BaseMLKitPlugin.kt b/android/src/main/java/com/visioncameramlkit/bridge/plugins/BaseMLKitPlugin.kt index 66fb982..0cb28b7 100644 --- a/android/src/main/java/com/visioncameramlkit/bridge/plugins/BaseMLKitPlugin.kt +++ b/android/src/main/java/com/visioncameramlkit/bridge/plugins/BaseMLKitPlugin.kt @@ -9,6 +9,7 @@ import com.mrousavy.camera.frameprocessors.VisionCameraProxy @Suppress("MagicNumber") abstract class BaseMLKitPlugin( + @Suppress("unused") protected val proxy: VisionCameraProxy, protected val options: Map?, ) : FrameProcessorPlugin() { diff --git a/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt b/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt index 8ce35eb..2fa2eba 100644 --- a/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt +++ b/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt @@ -23,8 +23,6 @@ object PluginRegistry { private fun registerTextRecognitionPlugin() { try { - // Dynamically load the plugin class to avoid compilation errors - // when the MLKit dependencies are not included val pluginClass = Class.forName("com.visioncameramlkit.bridge.plugins.TextRecognitionPlugin") @@ -42,7 +40,6 @@ object PluginRegistry { ) as com.mrousavy.camera.frameprocessors.FrameProcessorPlugin } } catch (e: ClassNotFoundException) { - // Plugin class not found - feature not enabled android.util.Log.w("VisionCameraMLKit", "TextRecognitionPlugin not available: ${e.message}") } catch ( @Suppress("TooGenericExceptionCaught") e: Exception, diff --git a/android/src/main/java/com/visioncameramlkit/domain/models/Orientation.kt b/android/src/main/java/com/visioncameramlkit/domain/models/Orientation.kt index 9b52e0f..017edcc 100644 --- a/android/src/main/java/com/visioncameramlkit/domain/models/Orientation.kt +++ b/android/src/main/java/com/visioncameramlkit/domain/models/Orientation.kt @@ -1,5 +1,6 @@ package com.visioncameramlkit.domain.models +@Suppress("unused") enum class Orientation { PORTRAIT, PORTRAIT_UPSIDE_DOWN, diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt index 1a9bef6..ce552d3 100644 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt @@ -7,6 +7,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod import com.facebook.react.bridge.ReadableMap import com.facebook.react.module.annotations.ReactModule +import com.visioncameramlkit.BuildConfig import com.visioncameramlkit.bridge.constants.MLKitFeatureKeys import com.visioncameramlkit.bridge.handlers.IStaticImageHandler import com.visioncameramlkit.bridge.handlers.StaticTextRecognitionHandler @@ -20,13 +21,20 @@ class VisionCameraMLKitModule( } private val handlers by lazy { - mapOf( - MLKitFeatureKeys.TEXT_RECOGNITION to StaticTextRecognitionHandler(reactApplicationContext), - ) + val handlers = mutableMapOf() + + @Suppress("KotlinConstantConditions") + if (BuildConfig.MLKIT_TEXT_RECOGNITION) { + handlers[MLKitFeatureKeys.TEXT_RECOGNITION] = + StaticTextRecognitionHandler(reactApplicationContext) + } + + handlers } override fun getName(): String = NAME + @Suppress("unused") @ReactMethod fun processImage( feature: String, From 96a658527e73f3c39536a4993b6e407712d83c6d Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Thu, 19 Feb 2026 04:03:57 -0300 Subject: [PATCH 09/45] fix(swift): swift code quality fixes --- .../UseCases/RecognizeTextUseCase.swift | 123 ++-- .../StaticTextRecognitionHandler.swift | 250 ++++---- ios/Bridge/Plugins/BaseMLKitPlugin.swift | 109 ++-- .../Plugins/TextRecognitionPlugin.swift | 102 ++-- ios/Bridge/Registry/PluginRegistry.swift | 17 +- .../Models/ImagePreprocessingOptions.swift | 2 +- ios/Domain/Models/Orientation.swift | 2 +- ios/Domain/Models/OutputOrientation.swift | 2 +- ios/Domain/Models/ProcessedImage.swift | 25 +- .../Models/TextRecognitionOptions.swift | 1 - ios/Domain/Services/IImagePreprocessor.swift | 17 +- .../Image/ImagePreprocessor.swift | 563 +++++++++--------- .../MLKit/Adapters/MLKitTextAdapter.swift | 135 +++-- .../Modules/VisionCameraMLKitModule.swift | 12 +- .../TextRecognitionSerializer.swift | 6 +- 15 files changed, 695 insertions(+), 671 deletions(-) diff --git a/ios/Application/UseCases/RecognizeTextUseCase.swift b/ios/Application/UseCases/RecognizeTextUseCase.swift index 6be6323..05555cf 100644 --- a/ios/Application/UseCases/RecognizeTextUseCase.swift +++ b/ios/Application/UseCases/RecognizeTextUseCase.swift @@ -1,69 +1,74 @@ import Foundation -import VisionCamera -class RecognizeTextUseCase { - private let imagePreprocessor: IImagePreprocessor - private let recognitionService: MLKitTextRecognitionService +#if MLKIT_TEXT_RECOGNITION + #if canImport(VisionCamera) + import VisionCamera - init( - imagePreprocessor: IImagePreprocessor, - recognitionService: MLKitTextRecognitionService - ) { - self.imagePreprocessor = imagePreprocessor - self.recognitionService = recognitionService - } + class RecognizeTextUseCase { + private let imagePreprocessor: IImagePreprocessor + private let recognitionService: MLKitTextRecognitionService - func execute(frame: Frame, options: TextRecognitionOptions) throws - -> TextRecognitionResult - { - // autoreleasepool at frame boundary to drain autoreleased Obj-C objects - // after each frame is processed, preventing buildup across multiple frames - return try autoreleasepool { - let preprocessingOptions = ImagePreprocessingOptions( - invertColors: options.invertColors, - outputOrientation: options.outputOrientation, - scaleFactor: options.scaleFactor - ) - - guard - let processedImage = imagePreprocessor.preprocessFrame( - frame: frame, - options: preprocessingOptions - ) - else { - throw NSError( - domain: "RecognizeTextUseCase", - code: -1, - userInfo: [NSLocalizedDescriptionKey: "Failed to preprocess image"] - ) + init( + imagePreprocessor: IImagePreprocessor, + recognitionService: MLKitTextRecognitionService + ) { + self.imagePreprocessor = imagePreprocessor + self.recognitionService = recognitionService } - return try recognitionService.recognize(image: processedImage) - } - } + func execute(frame: Frame, options: TextRecognitionOptions) throws + -> TextRecognitionResult + { + // autoreleasepool at frame boundary to drain autoreleased Obj-C objects + // after each frame is processed, preventing buildup across multiple frames + return try autoreleasepool { + let preprocessingOptions = ImagePreprocessingOptions( + invertColors: options.invertColors, + outputOrientation: options.outputOrientation, + scaleFactor: options.scaleFactor + ) + + guard + let processedImage = imagePreprocessor.preprocessFrame( + frame: frame, + options: preprocessingOptions + ) + else { + throw NSError( + domain: "RecognizeTextUseCase", + code: -1, + userInfo: [NSLocalizedDescriptionKey: "Failed to preprocess image"] + ) + } - func execute( - imageFile: URL, - imageOptions: ImagePreprocessingOptions, - textOptions: TextRecognitionOptions - ) throws -> TextRecognitionResult { - // autoreleasepool here ensures predictable memory cleanup, - // especially if this method is called repeatedly or in batches - return try autoreleasepool { - guard - let processedImage = imagePreprocessor.preprocessImage( - imageFile: imageFile, - options: imageOptions - ) - else { - throw NSError( - domain: "RecognizeTextUseCase", - code: -1, - userInfo: [NSLocalizedDescriptionKey: "Failed to preprocess image"] - ) + return try recognitionService.recognize(image: processedImage) + } } - return try recognitionService.recognize(image: processedImage) + func execute( + imageFile: URL, + imageOptions: ImagePreprocessingOptions, + textOptions: TextRecognitionOptions + ) throws -> TextRecognitionResult { + // autoreleasepool here ensures predictable memory cleanup, + // especially if this method is called repeatedly or in batches + return try autoreleasepool { + guard + let processedImage = imagePreprocessor.preprocessImage( + imageFile: imageFile, + options: imageOptions + ) + else { + throw NSError( + domain: "RecognizeTextUseCase", + code: -1, + userInfo: [NSLocalizedDescriptionKey: "Failed to preprocess image"] + ) + } + + return try recognitionService.recognize(image: processedImage) + } + } } - } -} + #endif +#endif diff --git a/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift b/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift index b646a9a..6982e03 100644 --- a/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift +++ b/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift @@ -1,157 +1,153 @@ import Foundation -class StaticTextRecognitionHandler: IStaticImageHandler { - private let textRecognitionSerializer = TextRecognitionSerializer() - private var cachedUseCase: RecognizeTextUseCase? - - private func getRecognizeTextUseCase(for language: TextRecognitionLanguage) - -> RecognizeTextUseCase - { - if let useCase = cachedUseCase { - return useCase - } - let newUseCase = RecognizeTextUseCase( - imagePreprocessor: ImagePreprocessor(), - recognitionService: TextRecognitionServiceFactory.create( - language: language +#if MLKIT_TEXT_RECOGNITION + class StaticTextRecognitionHandler: IStaticImageHandler { + private let textRecognitionSerializer = TextRecognitionSerializer() + private var cachedUseCase: RecognizeTextUseCase? + + private func getRecognizeTextUseCase(for language: TextRecognitionLanguage) + -> RecognizeTextUseCase + { + if let useCase = cachedUseCase { + return useCase + } + let newUseCase = RecognizeTextUseCase( + imagePreprocessor: ImagePreprocessor(), + recognitionService: TextRecognitionServiceFactory.create( + language: language + ) ) - ) - cachedUseCase = newUseCase - return newUseCase - } + cachedUseCase = newUseCase + return newUseCase + } - func process( - path: String, - options: [String: Any], - resolver: @escaping PromiseResolver, - rejecter: @escaping PromiseRejecter - ) { - Task { - do { - guard let fileURL = resolveFileURL(from: path) else { - rejecter( - "IMAGE_NOT_FOUND_ERROR", - "Unsupported image URI: \(path)", - nil - ) - return - } + func process( + path: String, + options: [String: Any], + resolver: @escaping PromiseResolver, + rejecter: @escaping PromiseRejecter + ) { + Task { + do { + guard let fileURL = resolveFileURL(from: path) else { + rejecter( + "IMAGE_NOT_FOUND_ERROR", + "Unsupported image URI: \(path)", + nil + ) + return + } - // Check if file exists - if fileURL.isFileURL - && !FileManager.default.fileExists(atPath: fileURL.path) - { - rejecter( - "IMAGE_NOT_FOUND_ERROR", - "Image file not found at path: \(path)", - nil - ) - return - } + if fileURL.isFileURL + && !FileManager.default.fileExists(atPath: fileURL.path) + { + rejecter( + "IMAGE_NOT_FOUND_ERROR", + "Image file not found at path: \(path)", + nil + ) + return + } - // Parse options - let imageOptions = parseImageOptions(options) + let imageOptions = parseImageOptions(options) - // Create text recognition options - let languageString = options["language"] as? String ?? "LATIN" - let language = parseLanguage(languageString) - let textOptions = TextRecognitionOptions(language: language) + let languageString = options["language"] as? String ?? "LATIN" + let language = parseLanguage(languageString) + let textOptions = TextRecognitionOptions(language: language) - // Get use case for the language - let recognizeTextUseCase = getRecognizeTextUseCase(for: language) + let recognizeTextUseCase = getRecognizeTextUseCase(for: language) - // Process the image - let result = try recognizeTextUseCase.execute( - imageFile: fileURL, - imageOptions: imageOptions, - textOptions: textOptions - ) + let result = try recognizeTextUseCase.execute( + imageFile: fileURL, + imageOptions: imageOptions, + textOptions: textOptions + ) - // Serialize result - let serializedResult = TextRecognitionSerializer.toReactNativeMap( - result - ) + let serializedResult = TextRecognitionSerializer.toReactNativeMap( + result + ) - resolver(serializedResult) - } catch { - switch error { - case let nsError as NSError: - if nsError.domain == NSCocoaErrorDomain - && nsError.code == NSFileReadNoSuchFileError - { - rejecter("IMAGE_NOT_FOUND_ERROR", "Image file not found", error) - } else { + resolver(serializedResult) + } catch { + switch error { + case let nsError as NSError: + if nsError.domain == NSCocoaErrorDomain + && nsError.code == NSFileReadNoSuchFileError + { + rejecter("IMAGE_NOT_FOUND_ERROR", "Image file not found", error) + } else { + rejecter( + "IMAGE_PROCESSING_FAILED_ERROR", + error.localizedDescription, + error + ) + } + default: rejecter( "IMAGE_PROCESSING_FAILED_ERROR", error.localizedDescription, error ) } - default: - rejecter( - "IMAGE_PROCESSING_FAILED_ERROR", - error.localizedDescription, - error - ) } } } - } - private func parseImageOptions(_ options: [String: Any]) - -> ImagePreprocessingOptions - { - let invertColors = options["invertColors"] as? Bool ?? false - let orientationString = options["orientation"] as? String ?? "portrait" - let orientation = parseOrientation(orientationString) - - return ImagePreprocessingOptions( - invertColors: invertColors, - orientation: orientation - ) - } + private func parseImageOptions(_ options: [String: Any]) + -> ImagePreprocessingOptions + { + let invertColors = options["invertColors"] as? Bool ?? false + let orientationString = options["orientation"] as? String ?? "portrait" + let orientation = parseOrientation(orientationString) - private func parseOrientation(_ orientation: String) -> Orientation { - switch orientation { - case "portrait": - return .portrait - case "portrait-upside-down": - return .portraitUpsideDown - case "landscape-left": - return .landscapeLeft - case "landscape-right": - return .landscapeRight - default: - return .portrait + return ImagePreprocessingOptions( + invertColors: invertColors, + orientation: orientation + ) } - } - private func parseLanguage(_ language: String) -> TextRecognitionLanguage { - switch language { - case "LATIN": - return .latin - case "CHINESE": - return .chinese - case "DEVANAGARI": - return .devanagari - case "JAPANESE": - return .japanese - case "KOREAN": - return .korean - default: - return .latin + private func parseOrientation(_ orientation: String) -> Orientation { + switch orientation { + case "portrait": + return .portrait + case "portrait-upside-down": + return .portraitUpsideDown + case "landscape-left": + return .landscapeLeft + case "landscape-right": + return .landscapeRight + default: + return .portrait + } } - } - private func resolveFileURL(from path: String) -> URL? { - if path.hasPrefix("file://") { - return URL(string: path) + private func parseLanguage(_ language: String) -> TextRecognitionLanguage { + switch language { + case "LATIN": + return .latin + case "CHINESE": + return .chinese + case "DEVANAGARI": + return .devanagari + case "JAPANESE": + return .japanese + case "KOREAN": + return .korean + default: + return .latin + } } - if let url = URL(string: path), url.scheme != nil { - return url - } + private func resolveFileURL(from path: String) -> URL? { + if path.hasPrefix("file://") { + return URL(string: path) + } + + if let url = URL(string: path), url.scheme != nil { + return url + } - return URL(fileURLWithPath: path) + return URL(fileURLWithPath: path) + } } -} +#endif diff --git a/ios/Bridge/Plugins/BaseMLKitPlugin.swift b/ios/Bridge/Plugins/BaseMLKitPlugin.swift index 611c4f4..6025924 100644 --- a/ios/Bridge/Plugins/BaseMLKitPlugin.swift +++ b/ios/Bridge/Plugins/BaseMLKitPlugin.swift @@ -1,71 +1,74 @@ import Foundation -import VisionCamera -@objc -public class BaseMLKitPlugin: FrameProcessorPlugin { +#if canImport(VisionCamera) + import VisionCamera - let proxy: VisionCameraProxyHolder - let options: [AnyHashable: Any]? + @objc + public class BaseMLKitPlugin: FrameProcessorPlugin { - private let frameProcessInterval: Int - private var frameCounter: Int = 0 - let invertColors: Bool - let scaleFactor: CGFloat + let proxy: VisionCameraProxyHolder + let options: [AnyHashable: Any]? - var outputOrientation: OutputOrientation? + private let frameProcessInterval: Int + private var frameCounter: Int = 0 + let invertColors: Bool + let scaleFactor: CGFloat - public override init( - proxy: VisionCameraProxyHolder, - options: [AnyHashable: Any]? - ) { - self.proxy = proxy - self.options = options - self.frameProcessInterval = options?["frameProcessInterval"] as? Int ?? 0 - self.invertColors = options?["invertColors"] as? Bool ?? false + var outputOrientation: OutputOrientation? - let sf = options?["scaleFactor"] as? NSNumber - let sfValue = sf?.doubleValue ?? 1.0 + public override init( + proxy: VisionCameraProxyHolder, + options: [AnyHashable: Any]? + ) { + self.proxy = proxy + self.options = options + self.frameProcessInterval = options?["frameProcessInterval"] as? Int ?? 0 + self.invertColors = options?["invertColors"] as? Bool ?? false - self.scaleFactor = min(max(CGFloat(sfValue), 0.9), 1.0) + let sf = options?["scaleFactor"] as? NSNumber + let sfValue = sf?.doubleValue ?? 1.0 - super.init(proxy: proxy, options: options) - } + self.scaleFactor = min(max(CGFloat(sfValue), 0.9), 1.0) + + super.init(proxy: proxy, options: options) + } - public override func callback( - _ frame: Frame, - withArguments arguments: [AnyHashable: Any]? - ) -> Any { - // Extract outputOrientation from arguments for rotation support - outputOrientation = OutputOrientation( - string: arguments?["outputOrientation"] as? String - ) + public override func callback( + _ frame: Frame, + withArguments arguments: [AnyHashable: Any]? + ) -> Any { + // Extract outputOrientation from arguments for rotation support + outputOrientation = OutputOrientation( + string: arguments?["outputOrientation"] as? String + ) + + // Process 1 frame, skip N frames, repeat + // Note: Consider using runAtTargetFps() from Vision Camera instead + if frameProcessInterval > 0 { + frameCounter += 1 + if frameCounter <= frameProcessInterval { + return [:] + } + frameCounter = 0 + } - // Process 1 frame, skip N frames, repeat - // Note: Consider using runAtTargetFps() from Vision Camera instead - if frameProcessInterval > 0 { - frameCounter += 1 - if frameCounter <= frameProcessInterval { + do { + return try processFrame(frame, withArguments: arguments) + } catch { + print("[\(pluginName)] Error occurred: \(error.localizedDescription)") return [:] } - frameCounter = 0 } - do { - return try processFrame(frame, withArguments: arguments) - } catch { - print("[\(pluginName)] Error occurred: \(error.localizedDescription)") - return [:] + public func processFrame( + _ frame: Frame, + withArguments arguments: [AnyHashable: Any]? + ) throws -> [String: Any] { + fatalError("processFrame must be overridden by subclass") } - } - - public func processFrame( - _ frame: Frame, - withArguments arguments: [AnyHashable: Any]? - ) throws -> [String: Any] { - fatalError("processFrame must be overridden by subclass") - } - var pluginName: String { - return String(describing: type(of: self)) + var pluginName: String { + return String(describing: type(of: self)) + } } -} +#endif diff --git a/ios/Bridge/Plugins/TextRecognitionPlugin.swift b/ios/Bridge/Plugins/TextRecognitionPlugin.swift index 85157a6..2f36d64 100644 --- a/ios/Bridge/Plugins/TextRecognitionPlugin.swift +++ b/ios/Bridge/Plugins/TextRecognitionPlugin.swift @@ -1,66 +1,68 @@ import Foundation -import VisionCamera #if MLKIT_TEXT_RECOGNITION + #if canImport(VisionCamera) + import VisionCamera - @objc(TextRecognitionPlugin) - public class TextRecognitionPlugin: BaseMLKitPlugin { + @objc(TextRecognitionPlugin) + public class TextRecognitionPlugin: BaseMLKitPlugin { - private let language: TextRecognitionLanguage + private let language: TextRecognitionLanguage - private lazy var recognitionOptions: TextRecognitionOptions = { - TextRecognitionOptions( - language: self.language, - invertColors: self.invertColors, - scaleFactor: self.scaleFactor - ) - }() + private lazy var recognitionOptions: TextRecognitionOptions = { + TextRecognitionOptions( + language: self.language, + invertColors: self.invertColors, + scaleFactor: self.scaleFactor + ) + }() - private lazy var recognizeTextUseCase: RecognizeTextUseCase = { - let textRecognizer = TextRecognitionServiceFactory.createTextRecognizer( - language: self.language - ) - let recognitionService = MLKitTextRecognitionService( - textRecognizer: textRecognizer - ) - let imagePreprocessor = ImagePreprocessor() + private lazy var recognizeTextUseCase: RecognizeTextUseCase = { + let textRecognizer = TextRecognitionServiceFactory.createTextRecognizer( + language: self.language + ) + let recognitionService = MLKitTextRecognitionService( + textRecognizer: textRecognizer + ) + let imagePreprocessor = ImagePreprocessor() - return RecognizeTextUseCase( - imagePreprocessor: imagePreprocessor, - recognitionService: recognitionService - ) - }() + return RecognizeTextUseCase( + imagePreprocessor: imagePreprocessor, + recognitionService: recognitionService + ) + }() - @objc - public override init( - proxy: VisionCameraProxyHolder, - options: [AnyHashable: Any]? = [:] - ) { - let languageString = options?["language"] as? String ?? "LATIN" - self.language = - TextRecognitionLanguage(rawValue: languageString) ?? .latin + @objc + public override init( + proxy: VisionCameraProxyHolder, + options: [AnyHashable: Any]? = [:] + ) { + let languageString = options?["language"] as? String ?? "LATIN" + self.language = + TextRecognitionLanguage(rawValue: languageString) ?? .latin - super.init(proxy: proxy, options: options) - } + super.init(proxy: proxy, options: options) + } - public override func processFrame( - _ frame: Frame, - withArguments arguments: [AnyHashable: Any]? - ) throws -> [String: Any] { - let options = TextRecognitionOptions( - language: recognitionOptions.language, - invertColors: recognitionOptions.invertColors, - outputOrientation: outputOrientation, - scaleFactor: recognitionOptions.scaleFactor - ) + public override func processFrame( + _ frame: Frame, + withArguments arguments: [AnyHashable: Any]? + ) throws -> [String: Any] { + let options = TextRecognitionOptions( + language: recognitionOptions.language, + invertColors: recognitionOptions.invertColors, + outputOrientation: outputOrientation, + scaleFactor: recognitionOptions.scaleFactor + ) - let result = try recognizeTextUseCase.execute( - frame: frame, - options: options - ) + let result = try recognizeTextUseCase.execute( + frame: frame, + options: options + ) - return TextRecognitionSerializer.toReactNativeMap(result) + return TextRecognitionSerializer.toReactNativeMap(result) + } } - } + #endif #endif diff --git a/ios/Bridge/Registry/PluginRegistry.swift b/ios/Bridge/Registry/PluginRegistry.swift index 82917b2..dde1201 100644 --- a/ios/Bridge/Registry/PluginRegistry.swift +++ b/ios/Bridge/Registry/PluginRegistry.swift @@ -1,12 +1,15 @@ import Foundation -import VisionCamera -@objc -public class PluginRegistry: NSObject { +#if canImport(VisionCamera) + import VisionCamera @objc - public static func registerTextRecognitionPlugin() { - // Plugin registration is handled by the Objective-C bridge in VisionCameraMLKit.mm - // This class exists for consistency with Android architecture + public class PluginRegistry: NSObject { + + @objc + public static func registerTextRecognitionPlugin() { + // Plugin registration is handled by the Objective-C bridge in VisionCameraMLKit.mm + // This class exists for consistency with Android architecture + } } -} +#endif diff --git a/ios/Domain/Models/ImagePreprocessingOptions.swift b/ios/Domain/Models/ImagePreprocessingOptions.swift index 5b86b37..ff88aed 100644 --- a/ios/Domain/Models/ImagePreprocessingOptions.swift +++ b/ios/Domain/Models/ImagePreprocessingOptions.swift @@ -1,4 +1,4 @@ -import UIKit +import Foundation public struct ImagePreprocessingOptions { let invertColors: Bool diff --git a/ios/Domain/Models/Orientation.swift b/ios/Domain/Models/Orientation.swift index 27dc518..b12206b 100644 --- a/ios/Domain/Models/Orientation.swift +++ b/ios/Domain/Models/Orientation.swift @@ -1,4 +1,4 @@ -import UIKit +import Foundation @objc public enum Orientation: Int { case portrait diff --git a/ios/Domain/Models/OutputOrientation.swift b/ios/Domain/Models/OutputOrientation.swift index 8d0497b..6708e23 100644 --- a/ios/Domain/Models/OutputOrientation.swift +++ b/ios/Domain/Models/OutputOrientation.swift @@ -1,4 +1,4 @@ -import UIKit +import Foundation @objc public enum OutputOrientation: Int { case portrait diff --git a/ios/Domain/Models/ProcessedImage.swift b/ios/Domain/Models/ProcessedImage.swift index c695c4f..b11fc42 100644 --- a/ios/Domain/Models/ProcessedImage.swift +++ b/ios/Domain/Models/ProcessedImage.swift @@ -1,14 +1,17 @@ import Foundation -import MLKitVision -struct ProcessedImage { - let image: VisionImage - let metadata: ImageMetadata -} +#if canImport(MLKitVision) + import MLKitVision -struct ImageMetadata { - let width: Int - let height: Int - let orientation: UIImage.Orientation - let isInverted: Bool -} + struct ProcessedImage { + let image: VisionImage + let metadata: ImageMetadata + } + + struct ImageMetadata { + let width: Int + let height: Int + let orientation: UIImage.Orientation + let isInverted: Bool + } +#endif diff --git a/ios/Domain/Models/TextRecognitionOptions.swift b/ios/Domain/Models/TextRecognitionOptions.swift index a53cb11..7e396ba 100644 --- a/ios/Domain/Models/TextRecognitionOptions.swift +++ b/ios/Domain/Models/TextRecognitionOptions.swift @@ -1,5 +1,4 @@ import Foundation -import UIKit struct TextRecognitionOptions { let language: TextRecognitionLanguage diff --git a/ios/Domain/Services/IImagePreprocessor.swift b/ios/Domain/Services/IImagePreprocessor.swift index f5781b9..4ac2d39 100644 --- a/ios/Domain/Services/IImagePreprocessor.swift +++ b/ios/Domain/Services/IImagePreprocessor.swift @@ -1,10 +1,13 @@ import Foundation -import VisionCamera -protocol IImagePreprocessor { - func preprocessFrame(frame: Frame, options: ImagePreprocessingOptions) - -> ProcessedImage? +#if canImport(VisionCamera) + import VisionCamera - func preprocessImage(imageFile: URL, options: ImagePreprocessingOptions) - -> ProcessedImage? -} + protocol IImagePreprocessor { + func preprocessFrame(frame: Frame, options: ImagePreprocessingOptions) + -> ProcessedImage? + + func preprocessImage(imageFile: URL, options: ImagePreprocessingOptions) + -> ProcessedImage? + } +#endif diff --git a/ios/Infrastructure/Image/ImagePreprocessor.swift b/ios/Infrastructure/Image/ImagePreprocessor.swift index 5580a43..4b33706 100644 --- a/ios/Infrastructure/Image/ImagePreprocessor.swift +++ b/ios/Infrastructure/Image/ImagePreprocessor.swift @@ -1,332 +1,335 @@ import Foundation -import MLKitVision -import VisionCamera - -class ImagePreprocessor: IImagePreprocessor { - - // Reuse CIContext across frames to avoid repeated allocation overhead - // CIContext creation is expensive (~10-20ms), reusing reduces per-frame cost significantly - private static let sharedContext: CIContext = { - // Use Metal if available for better performance, otherwise use CPU - if let metalDevice = MTLCreateSystemDefaultDevice() { - return CIContext( - mtlDevice: metalDevice, - options: [.useSoftwareRenderer: false] - ) - } else { - return CIContext(options: [.useSoftwareRenderer: false]) - } - }() - // Clamp scaleFactor to safe range for ML accuracy - private func clampScale(_ scaleFactor: CGFloat?) -> CGFloat { - let scale = scaleFactor ?? 1.0 - return min(max(scale, 0.9), 1.0) - } +#if canImport(MLKitVision) && canImport(VisionCamera) + import MLKitVision + import VisionCamera - // Create scaled VisionImage from CIImage using Core Image affine transform - private func createScaledVisionImage( - from ciImage: CIImage, - scaleFactor: CGFloat, - frameOrientation: UIImage.Orientation, - outputOrientation: OutputOrientation? - ) -> VisionImage? { - // Apply scaling using CIAffineTransform - let scaledCIImage = ciImage.transformed( - by: CGAffineTransform(scaleX: scaleFactor, y: scaleFactor) - ) + class ImagePreprocessor: IImagePreprocessor { - // Render to CGImage - guard - let cgImage = ImagePreprocessor.sharedContext.createCGImage( - scaledCIImage, - from: scaledCIImage.extent - ) - else { - return nil + // Reuse CIContext across frames to avoid repeated allocation overhead + // CIContext creation is expensive (~10-20ms), reusing reduces per-frame cost significantly + private static let sharedContext: CIContext = { + // Use Metal if available for better performance, otherwise use CPU + if let metalDevice = MTLCreateSystemDefaultDevice() { + return CIContext( + mtlDevice: metalDevice, + options: [.useSoftwareRenderer: false] + ) + } else { + return CIContext(options: [.useSoftwareRenderer: false]) + } + }() + + // Clamp scaleFactor to safe range for ML accuracy + private func clampScale(_ scaleFactor: CGFloat?) -> CGFloat { + let scale = scaleFactor ?? 1.0 + return min(max(scale, 0.9), 1.0) } - // Create UIImage with .up orientation (orientation handled by VisionImage) - let uiImage = UIImage( - cgImage: cgImage, - scale: 1.0, - orientation: .up - ) + // Create scaled VisionImage from CIImage using Core Image affine transform + private func createScaledVisionImage( + from ciImage: CIImage, + scaleFactor: CGFloat, + frameOrientation: UIImage.Orientation, + outputOrientation: OutputOrientation? + ) -> VisionImage? { + // Apply scaling using CIAffineTransform + let scaledCIImage = ciImage.transformed( + by: CGAffineTransform(scaleX: scaleFactor, y: scaleFactor) + ) - let visionImage = VisionImage(image: uiImage) - visionImage.orientation = getVisionOrientation( - frameOrientation: frameOrientation, - outputOrientation: outputOrientation - ) - return visionImage - } + // Render to CGImage + guard + let cgImage = ImagePreprocessor.sharedContext.createCGImage( + scaledCIImage, + from: scaledCIImage.extent + ) + else { + return nil + } - // Create filter per call to avoid threading issues with shared state - // CIFilter is not thread-safe; creating per-call ensures safe concurrent frame processing - private func createColorInvertFilter() -> CIFilter? { - let filter = CIFilter(name: "CIColorInvert") - filter?.setDefaults() - return filter - } + // Create UIImage with .up orientation (orientation handled by VisionImage) + let uiImage = UIImage( + cgImage: cgImage, + scale: 1.0, + orientation: .up + ) - func preprocessFrame(frame: Frame, options: ImagePreprocessingOptions) - -> ProcessedImage? - { - let effectiveScale = clampScale(options.scaleFactor) + let visionImage = VisionImage(image: uiImage) + visionImage.orientation = getVisionOrientation( + frameOrientation: frameOrientation, + outputOrientation: outputOrientation + ) + return visionImage + } - let image: VisionImage + // Create filter per call to avoid threading issues with shared state + // CIFilter is not thread-safe; creating per-call ensures safe concurrent frame processing + private func createColorInvertFilter() -> CIFilter? { + let filter = CIFilter(name: "CIColorInvert") + filter?.setDefaults() + return filter + } - if options.invertColors { - guard - let invertedImage = createInvertedVisionImageFromFrame( + func preprocessFrame(frame: Frame, options: ImagePreprocessingOptions) + -> ProcessedImage? + { + let effectiveScale = clampScale(options.scaleFactor) + + let image: VisionImage + + if options.invertColors { + guard + let invertedImage = createInvertedVisionImageFromFrame( + frame: frame, + outputOrientation: options.outputOrientation, + scaleFactor: effectiveScale + ) + else { + return nil + } + image = invertedImage + } else { + image = createVisionImageFromFrame( frame: frame, outputOrientation: options.outputOrientation, scaleFactor: effectiveScale ) - else { + } + + let buffer: CMSampleBuffer = frame.buffer + guard let imageBuffer = CMSampleBufferGetImageBuffer(buffer) else { return nil } - image = invertedImage - } else { - image = createVisionImageFromFrame( - frame: frame, - outputOrientation: options.outputOrientation, - scaleFactor: effectiveScale + + // Use scaled dimensions for metadata + let originalWidth = CVPixelBufferGetWidth(imageBuffer) + let originalHeight = CVPixelBufferGetHeight(imageBuffer) + let width = Int(CGFloat(originalWidth) * effectiveScale) + let height = Int(CGFloat(originalHeight) * effectiveScale) + + let metadata = ImageMetadata( + width: width, + height: height, + orientation: frame.orientation, + isInverted: options.invertColors ) - } - let buffer: CMSampleBuffer = frame.buffer - guard let imageBuffer = CMSampleBufferGetImageBuffer(buffer) else { - return nil + return ProcessedImage(image: image, metadata: metadata) } - // Use scaled dimensions for metadata - let originalWidth = CVPixelBufferGetWidth(imageBuffer) - let originalHeight = CVPixelBufferGetHeight(imageBuffer) - let width = Int(CGFloat(originalWidth) * effectiveScale) - let height = Int(CGFloat(originalHeight) * effectiveScale) - - let metadata = ImageMetadata( - width: width, - height: height, - orientation: frame.orientation, - isInverted: options.invertColors - ) - - return ProcessedImage(image: image, metadata: metadata) - } - - private func createVisionImageFromFrame( - frame: Frame, - outputOrientation: OutputOrientation?, - scaleFactor: CGFloat - ) -> VisionImage { - let buffer: CMSampleBuffer = frame.buffer - - // MLKit works more reliably when frames are converted to UIImage - // YUV CMSampleBuffer can have orientation and format handling issues - if let pixelBuffer = CMSampleBufferGetImageBuffer(buffer) { - let pixelFormatType = CVPixelBufferGetPixelFormatType(pixelBuffer) - - // Only use BGRA directly with MLKit - convert everything else - // YUV formats can have issues with CMSampleBuffer, so convert to BGRA for reliability - let needsConversion = - pixelFormatType != kCVPixelFormatType_32BGRA - || pixelFormatType - == kCVPixelFormatType_Lossy_420YpCbCr8BiPlanarVideoRange - || pixelFormatType - == kCVPixelFormatType_Lossy_420YpCbCr8BiPlanarFullRange - || pixelFormatType - == kCVPixelFormatType_Lossless_420YpCbCr8BiPlanarVideoRange - || pixelFormatType - == kCVPixelFormatType_Lossless_420YpCbCr8BiPlanarFullRange - - // Force conversion when scaling is needed to ensure consistent behavior - // Never bypass scaling if scaleFactor < 1.0 - if needsConversion || scaleFactor < 1.0 { - if let convertedImage = convertToSupportedFormat( - pixelBuffer: pixelBuffer, - frameOrientation: frame.orientation, - outputOrientation: outputOrientation, - scaleFactor: scaleFactor - ) { - return convertedImage + private func createVisionImageFromFrame( + frame: Frame, + outputOrientation: OutputOrientation?, + scaleFactor: CGFloat + ) -> VisionImage { + let buffer: CMSampleBuffer = frame.buffer + + // MLKit works more reliably when frames are converted to UIImage + // YUV CMSampleBuffer can have orientation and format handling issues + if let pixelBuffer = CMSampleBufferGetImageBuffer(buffer) { + let pixelFormatType = CVPixelBufferGetPixelFormatType(pixelBuffer) + + // Only use BGRA directly with MLKit - convert everything else + // YUV formats can have issues with CMSampleBuffer, so convert to BGRA for reliability + let needsConversion = + pixelFormatType != kCVPixelFormatType_32BGRA + || pixelFormatType + == kCVPixelFormatType_Lossy_420YpCbCr8BiPlanarVideoRange + || pixelFormatType + == kCVPixelFormatType_Lossy_420YpCbCr8BiPlanarFullRange + || pixelFormatType + == kCVPixelFormatType_Lossless_420YpCbCr8BiPlanarVideoRange + || pixelFormatType + == kCVPixelFormatType_Lossless_420YpCbCr8BiPlanarFullRange + + // Force conversion when scaling is needed to ensure consistent behavior + // Never bypass scaling if scaleFactor < 1.0 + if needsConversion || scaleFactor < 1.0 { + if let convertedImage = convertToSupportedFormat( + pixelBuffer: pixelBuffer, + frameOrientation: frame.orientation, + outputOrientation: outputOrientation, + scaleFactor: scaleFactor + ) { + return convertedImage + } + // Conversion failed - this should not happen for scaling cases + fatalError("Conversion failed when scaling is required") } - // Conversion failed - this should not happen for scaling cases - fatalError("Conversion failed when scaling is required") } + + // Fallback: Use CMSampleBuffer directly (for BGRA and scaleFactor == 1.0) + let image = VisionImage(buffer: buffer) + image.orientation = getVisionOrientation( + frameOrientation: frame.orientation, + outputOrientation: outputOrientation + ) + return image } - // Fallback: Use CMSampleBuffer directly (for BGRA and scaleFactor == 1.0) - let image = VisionImage(buffer: buffer) - image.orientation = getVisionOrientation( - frameOrientation: frame.orientation, - outputOrientation: outputOrientation - ) - return image - } + private func createInvertedVisionImageFromFrame( + frame: Frame, + outputOrientation: OutputOrientation?, + scaleFactor: CGFloat + ) -> VisionImage? { + guard let buffer = CMSampleBufferGetImageBuffer(frame.buffer) else { + return nil + } - private func createInvertedVisionImageFromFrame( - frame: Frame, - outputOrientation: OutputOrientation?, - scaleFactor: CGFloat - ) -> VisionImage? { - guard let buffer = CMSampleBufferGetImageBuffer(frame.buffer) else { - return nil - } + let ciImage = CIImage(cvPixelBuffer: buffer) - let ciImage = CIImage(cvPixelBuffer: buffer) + guard let invertedCIImage = invertCIImageColor(image: ciImage) else { + return nil + } - guard let invertedCIImage = invertCIImageColor(image: ciImage) else { - return nil + // Apply scaling and create VisionImage using the new method + return createScaledVisionImage( + from: invertedCIImage, + scaleFactor: scaleFactor, + frameOrientation: frame.orientation, + outputOrientation: outputOrientation + ) } - // Apply scaling and create VisionImage using the new method - return createScaledVisionImage( - from: invertedCIImage, - scaleFactor: scaleFactor, - frameOrientation: frame.orientation, - outputOrientation: outputOrientation + private func getVisionOrientation( + frameOrientation: UIImage.Orientation, + outputOrientation: OutputOrientation? ) - } - - private func getVisionOrientation( - frameOrientation: UIImage.Orientation, - outputOrientation: OutputOrientation? - ) - -> UIImage.Orientation - { - if let outputOrientation = outputOrientation { - return outputOrientation.asUIImageOrientation - } - // Remap for iOS camera sensor orientation quirks when no outputOrientation provided. - switch frameOrientation { - case .left: - return .right - case .right: - return .left - default: - return frameOrientation + -> UIImage.Orientation + { + if let outputOrientation = outputOrientation { + return outputOrientation.asUIImageOrientation + } + // Remap for iOS camera sensor orientation quirks when no outputOrientation provided. + switch frameOrientation { + case .left: + return .right + case .right: + return .left + default: + return frameOrientation + } } - } - private func invertCIImageColor(image: CIImage) -> CIImage? { - guard let filter = createColorInvertFilter() else { return nil } - filter.setValue(image, forKey: kCIInputImageKey) - return filter.outputImage - } - - private func convertToSupportedFormat( - pixelBuffer: CVPixelBuffer, - frameOrientation: UIImage.Orientation, - outputOrientation: OutputOrientation?, - scaleFactor: CGFloat - ) -> VisionImage? { - // Convert to CIImage - let ciImage = CIImage(cvPixelBuffer: pixelBuffer) - - // Apply scaling and create VisionImage using the new method - return createScaledVisionImage( - from: ciImage, - scaleFactor: scaleFactor, - frameOrientation: frameOrientation, - outputOrientation: outputOrientation - ) - } + private func invertCIImageColor(image: CIImage) -> CIImage? { + guard let filter = createColorInvertFilter() else { return nil } + filter.setValue(image, forKey: kCIInputImageKey) + return filter.outputImage + } - func preprocessImage(imageFile: URL, options: ImagePreprocessingOptions) - -> ProcessedImage? - { - guard let imageData = try? Data(contentsOf: imageFile), - let uiImage = UIImage(data: imageData) - else { - return nil + private func convertToSupportedFormat( + pixelBuffer: CVPixelBuffer, + frameOrientation: UIImage.Orientation, + outputOrientation: OutputOrientation?, + scaleFactor: CGFloat + ) -> VisionImage? { + // Convert to CIImage + let ciImage = CIImage(cvPixelBuffer: pixelBuffer) + + // Apply scaling and create VisionImage using the new method + return createScaledVisionImage( + from: ciImage, + scaleFactor: scaleFactor, + frameOrientation: frameOrientation, + outputOrientation: outputOrientation + ) } - // Rotate image based on orientation - let rotatedImage = rotateUIImage(uiImage, orientation: options.orientation) + func preprocessImage(imageFile: URL, options: ImagePreprocessingOptions) + -> ProcessedImage? + { + guard let imageData = try? Data(contentsOf: imageFile), + let uiImage = UIImage(data: imageData) + else { + return nil + } - // Apply color inversion if needed - let processedImage: UIImage - if options.invertColors { - processedImage = invertUIImageColors(rotatedImage) - } else { - processedImage = rotatedImage - } + // Rotate image based on orientation + let rotatedImage = rotateUIImage(uiImage, orientation: options.orientation) - // Create VisionImage - let visionImage = VisionImage(image: processedImage) - visionImage.orientation = options.orientation.asUIImageOrientation + // Apply color inversion if needed + let processedImage: UIImage + if options.invertColors { + processedImage = invertUIImageColors(rotatedImage) + } else { + processedImage = rotatedImage + } - let metadata = ImageMetadata( - width: Int(processedImage.size.width), - height: Int(processedImage.size.height), - orientation: options.orientation.asUIImageOrientation, - isInverted: options.invertColors - ) + // Create VisionImage + let visionImage = VisionImage(image: processedImage) + visionImage.orientation = options.orientation.asUIImageOrientation - return ProcessedImage(image: visionImage, metadata: metadata) - } + let metadata = ImageMetadata( + width: Int(processedImage.size.width), + height: Int(processedImage.size.height), + orientation: options.orientation.asUIImageOrientation, + isInverted: options.invertColors + ) - private func rotateUIImage(_ image: UIImage, orientation: Orientation) - -> UIImage - { - let degrees: CGFloat - switch orientation { - case .portrait: - degrees = 0 - case .portraitUpsideDown: - degrees = 180 - case .landscapeLeft: - degrees = 90 - case .landscapeRight: - degrees = 270 + return ProcessedImage(image: visionImage, metadata: metadata) } - let radians = degrees * .pi / 180 - let rotatedSize = CGRect(origin: .zero, size: image.size) - .applying(CGAffineTransform(rotationAngle: radians)) - .integral + private func rotateUIImage(_ image: UIImage, orientation: Orientation) + -> UIImage + { + let degrees: CGFloat + switch orientation { + case .portrait: + degrees = 0 + case .portraitUpsideDown: + degrees = 180 + case .landscapeLeft: + degrees = 90 + case .landscapeRight: + degrees = 270 + } - UIGraphicsBeginImageContext(rotatedSize.size) - defer { UIGraphicsEndImageContext() } + let radians = degrees * .pi / 180 + let rotatedSize = CGRect(origin: .zero, size: image.size) + .applying(CGAffineTransform(rotationAngle: radians)) + .integral - guard let context = UIGraphicsGetCurrentContext() else { return image } + UIGraphicsBeginImageContext(rotatedSize.size) + defer { UIGraphicsEndImageContext() } - context.translateBy( - x: rotatedSize.size.width / 2, - y: rotatedSize.size.height / 2 - ) - context.rotate(by: radians) - image.draw( - in: CGRect( - x: -image.size.width / 2, - y: -image.size.height / 2, - width: image.size.width, - height: image.size.height - ) - ) + guard let context = UIGraphicsGetCurrentContext() else { return image } - return UIGraphicsGetImageFromCurrentImageContext() ?? image - } + context.translateBy( + x: rotatedSize.size.width / 2, + y: rotatedSize.size.height / 2 + ) + context.rotate(by: radians) + image.draw( + in: CGRect( + x: -image.size.width / 2, + y: -image.size.height / 2, + width: image.size.width, + height: image.size.height + ) + ) - private func invertUIImageColors(_ image: UIImage) -> UIImage { - guard let filter = createColorInvertFilter(), - let ciImage = CIImage(image: image) - else { - return image + return UIGraphicsGetImageFromCurrentImageContext() ?? image } - filter.setValue(ciImage, forKey: kCIInputImageKey) - guard let outputImage = filter.outputImage, - let cgImage = ImagePreprocessor.sharedContext.createCGImage( - outputImage, - from: outputImage.extent - ) - else { - return image - } + private func invertUIImageColors(_ image: UIImage) -> UIImage { + guard let filter = createColorInvertFilter(), + let ciImage = CIImage(image: image) + else { + return image + } - return UIImage(cgImage: cgImage) + filter.setValue(ciImage, forKey: kCIInputImageKey) + guard let outputImage = filter.outputImage, + let cgImage = ImagePreprocessor.sharedContext.createCGImage( + outputImage, + from: outputImage.extent + ) + else { + return image + } + + return UIImage(cgImage: cgImage) + } } -} +#endif diff --git a/ios/Infrastructure/MLKit/Adapters/MLKitTextAdapter.swift b/ios/Infrastructure/MLKit/Adapters/MLKitTextAdapter.swift index 17135c9..0e2d8fe 100644 --- a/ios/Infrastructure/MLKit/Adapters/MLKitTextAdapter.swift +++ b/ios/Infrastructure/MLKit/Adapters/MLKitTextAdapter.swift @@ -1,77 +1,80 @@ import Foundation -import MLKitTextRecognition -class MLKitTextAdapter { +#if MLKIT_TEXT_RECOGNITION + import MLKitTextRecognition - static func toDomain(_ mlkitText: Text?) -> TextRecognitionResult { - return TextRecognitionResult( - text: mlkitText?.text, - blocks: mlkitText?.blocks.map { toTextBlock($0) } ?? [] - ) - } + class MLKitTextAdapter { - private static func toTextBlock(_ block: MLKitTextRecognition.TextBlock) - -> TextBlock - { - return TextBlock( - text: block.text, - bounds: toBoundingBox(block.frame), - corners: block.cornerPoints.map { toCorner($0.cgPointValue) }, - lines: block.lines.map { toTextLine($0) }, - languages: block.recognizedLanguages.map { $0.languageCode ?? "" } - ) - } + static func toDomain(_ mlkitText: Text?) -> TextRecognitionResult { + return TextRecognitionResult( + text: mlkitText?.text, + blocks: mlkitText?.blocks.map { toTextBlock($0) } ?? [] + ) + } - private static func toTextLine(_ line: MLKitTextRecognition.TextLine) - -> TextLine - { - return TextLine( - text: line.text, - bounds: toBoundingBox(line.frame), - corners: line.cornerPoints.map { toCorner($0.cgPointValue) }, - elements: line.elements.map { toTextElement($0) }, - confidence: nil, - angle: nil, - languages: line.recognizedLanguages.map { $0.languageCode ?? "" } - ) - } + private static func toTextBlock(_ block: MLKitTextRecognition.TextBlock) + -> TextBlock + { + return TextBlock( + text: block.text, + bounds: toBoundingBox(block.frame), + corners: block.cornerPoints.map { toCorner($0.cgPointValue) }, + lines: block.lines.map { toTextLine($0) }, + languages: block.recognizedLanguages.map { $0.languageCode ?? "" } + ) + } - private static func toTextElement(_ element: MLKitTextRecognition.TextElement) - -> TextElement - { - return TextElement( - text: element.text, - bounds: toBoundingBox(element.frame), - corners: element.cornerPoints.map { toCorner($0.cgPointValue) }, - symbols: [], - confidence: nil, - angle: nil, - languages: element.recognizedLanguages.map { $0.languageCode ?? "" } - ) - } + private static func toTextLine(_ line: MLKitTextRecognition.TextLine) + -> TextLine + { + return TextLine( + text: line.text, + bounds: toBoundingBox(line.frame), + corners: line.cornerPoints.map { toCorner($0.cgPointValue) }, + elements: line.elements.map { toTextElement($0) }, + confidence: nil, + angle: nil, + languages: line.recognizedLanguages.map { $0.languageCode ?? "" } + ) + } - private static func toBoundingBox(_ bounds: CGRect) -> BoundingBox { - let offsetX = (bounds.midX - ceil(bounds.width)) / 2.0 - let offsetY = (bounds.midY - ceil(bounds.height)) / 2.0 + private static func toTextElement(_ element: MLKitTextRecognition.TextElement) + -> TextElement + { + return TextElement( + text: element.text, + bounds: toBoundingBox(element.frame), + corners: element.cornerPoints.map { toCorner($0.cgPointValue) }, + symbols: [], + confidence: nil, + angle: nil, + languages: element.recognizedLanguages.map { $0.languageCode ?? "" } + ) + } - let x = bounds.maxX + offsetX - let y = bounds.minY + offsetY + private static func toBoundingBox(_ bounds: CGRect) -> BoundingBox { + let offsetX = (bounds.midX - ceil(bounds.width)) / 2.0 + let offsetY = (bounds.midY - ceil(bounds.height)) / 2.0 - return BoundingBox( - x: bounds.midX + (bounds.midX - x), - y: bounds.midY + (y - bounds.midY), - centerX: bounds.midX, - centerY: bounds.midY, - width: bounds.width, - height: bounds.height, - top: bounds.maxY, - left: bounds.minX, - bottom: bounds.minY, - right: bounds.maxX - ) - } + let x = bounds.maxX + offsetX + let y = bounds.minY + offsetY + + return BoundingBox( + x: bounds.midX + (bounds.midX - x), + y: bounds.midY + (y - bounds.midY), + centerX: bounds.midX, + centerY: bounds.midY, + width: bounds.width, + height: bounds.height, + top: bounds.maxY, + left: bounds.minX, + bottom: bounds.minY, + right: bounds.maxX + ) + } - private static func toCorner(_ point: CGPoint) -> Corner { - return Corner(x: Double(point.x), y: Double(point.y)) + private static func toCorner(_ point: CGPoint) -> Corner { + return Corner(x: Double(point.x), y: Double(point.y)) + } } -} +#endif diff --git a/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift b/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift index 3ae6c6f..ada4980 100644 --- a/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift +++ b/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift @@ -3,9 +3,15 @@ import React @objc(VisionCameraMLKitModule) class VisionCameraMLKitModule: NSObject { - private lazy var handlers: [String: IStaticImageHandler] = [ - MLKitFeatureKeys.TEXT_RECOGNITION: StaticTextRecognitionHandler() - ] + private lazy var handlers: [String: IStaticImageHandler] = { + var handlers: [String: IStaticImageHandler] = [:] + + #if MLKIT_TEXT_RECOGNITION + handlers[MLKitFeatureKeys.TEXT_RECOGNITION] = StaticTextRecognitionHandler() + #endif + + return handlers + }() @objc func processImage( diff --git a/ios/Infrastructure/Serializers/TextRecognitionSerializer.swift b/ios/Infrastructure/Serializers/TextRecognitionSerializer.swift index 8112f52..143ca64 100644 --- a/ios/Infrastructure/Serializers/TextRecognitionSerializer.swift +++ b/ios/Infrastructure/Serializers/TextRecognitionSerializer.swift @@ -2,8 +2,7 @@ import Foundation class TextRecognitionSerializer { - static func toReactNativeMap(_ result: TextRecognitionResult) -> [String: Any] - { + static func toReactNativeMap(_ result: TextRecognitionResult) -> [String: Any] { var map = [String: Any]() map["text"] = result.text map["blocks"] = toBlocksArray(result.blocks) @@ -100,8 +99,7 @@ class TextRecognitionSerializer { ] } - private static func toCornersArray(_ corners: [Corner]) -> [[String: Double]] - { + private static func toCornersArray(_ corners: [Corner]) -> [[String: Double]] { return corners.map { toCornerMap($0) } } From 63385067fb9bb3c06cbbfa369c548161e046a621 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Fri, 20 Feb 2026 02:20:44 -0300 Subject: [PATCH 10/45] fix(text-recognition): compile shared pipeline when any ML Kit variant is enabled --- VisionCameraMLKit.podspec | 12 ++- android/build.gradle | 12 ++- .../bridge/registry/PluginRegistry.kt | 2 +- .../TextRecognitionServiceFactory.kt | 96 +++++++++++++++++-- .../modules/VisionCameraMLKitModule.kt | 2 +- example/ios/Podfile.lock | 2 +- .../UseCases/RecognizeTextUseCase.swift | 4 +- .../StaticTextRecognitionHandler.swift | 4 +- .../Plugins/TextRecognitionPlugin.swift | 4 +- .../MLKit/Adapters/MLKitTextAdapter.swift | 4 +- .../TextRecognitionServiceFactory.swift | 6 +- .../MLKit/MLKitTextRecognitionService.swift | 6 +- .../Modules/VisionCameraMLKitModule.swift | 2 +- ios/VisionCameraMLKit.mm | 2 +- package.json | 2 +- 15 files changed, 128 insertions(+), 32 deletions(-) diff --git a/VisionCameraMLKit.podspec b/VisionCameraMLKit.podspec index 63ab2c3..a5236fb 100644 --- a/VisionCameraMLKit.podspec +++ b/VisionCameraMLKit.podspec @@ -45,8 +45,16 @@ Pod::Spec.new do |s| s.dependency "VisionCamera" + # Compute whether any text recognition variant is enabled + any_text_recognition = mlkit_config['textRecognition'] || + mlkit_config['textRecognitionChinese'] || + mlkit_config['textRecognitionDevanagari'] || + mlkit_config['textRecognitionJapanese'] || + mlkit_config['textRecognitionKorean'] + # Generate Swift compiler flags for conditional compilation swift_flags = [] + swift_flags << "MLKIT_TEXT_RECOGNITION_ANY" if any_text_recognition swift_flags << "MLKIT_TEXT_RECOGNITION" if mlkit_config['textRecognition'] swift_flags << "MLKIT_TEXT_RECOGNITION_CHINESE" if mlkit_config['textRecognitionChinese'] swift_flags << "MLKIT_TEXT_RECOGNITION_DEVANAGARI" if mlkit_config['textRecognitionDevanagari'] @@ -74,7 +82,9 @@ Pod::Spec.new do |s| # Text recognition v2 # https://developers.google.com/ml-kit/vision/text-recognition/v2/ios - if mlkit_config['textRecognition'] + # Base TextRecognition is required whenever any text recognition variant is enabled, + # as it provides shared types (TextRecognizer, CommonTextRecognizerOptions, Text, etc.) + if any_text_recognition s.dependency "GoogleMLKit/TextRecognition" end if mlkit_config['textRecognitionChinese'] diff --git a/android/build.gradle b/android/build.gradle index f7cc6c2..0d2bb35 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -69,6 +69,13 @@ def getMLKitConfig() { def mlkitConfig = getMLKitConfig() +// Compute whether any text recognition variant is enabled +def anyTextRecognition = mlkitConfig.textRecognition || + mlkitConfig.textRecognitionChinese || + mlkitConfig.textRecognitionDevanagari || + mlkitConfig.textRecognitionJapanese || + mlkitConfig.textRecognitionKorean + android { namespace "com.visioncameramlkit" @@ -79,6 +86,7 @@ android { targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") // Add build config fields for conditional compilation + buildConfigField "boolean", "MLKIT_TEXT_RECOGNITION_ANY", "${anyTextRecognition}" buildConfigField "boolean", "MLKIT_TEXT_RECOGNITION", "${mlkitConfig.textRecognition}" buildConfigField "boolean", "MLKIT_TEXT_RECOGNITION_CHINESE", "${mlkitConfig.textRecognitionChinese}" buildConfigField "boolean", "MLKIT_TEXT_RECOGNITION_DEVANAGARI", "${mlkitConfig.textRecognitionDevanagari}" @@ -144,7 +152,9 @@ dependencies { // Text recognition v2 // https://developers.google.com/ml-kit/vision/text-recognition/v2/android - if (mlkitConfig.textRecognition) { + // Base text-recognition is required whenever any text recognition variant is enabled, + // as it provides shared types (TextRecognizer, TextRecognizerOptions, etc.) + if (anyTextRecognition) { implementation "com.google.mlkit:text-recognition:16.0.1" } if (mlkitConfig.textRecognitionChinese) { diff --git a/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt b/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt index 2fa2eba..13a7122 100644 --- a/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt +++ b/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt @@ -16,7 +16,7 @@ object PluginRegistry { private fun registerPlugins() { @Suppress("KotlinConstantConditions") - if (BuildConfig.MLKIT_TEXT_RECOGNITION) { + if (BuildConfig.MLKIT_TEXT_RECOGNITION_ANY) { registerTextRecognitionPlugin() } } diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt index bc124c6..41584d9 100644 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt @@ -2,28 +2,104 @@ package com.visioncameramlkit.infrastructure.mlkit.factories import com.google.mlkit.vision.text.TextRecognition import com.google.mlkit.vision.text.TextRecognizer -import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions -import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions -import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions -import com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions +import com.google.mlkit.vision.text.TextRecognizerOptionsInterface import com.google.mlkit.vision.text.latin.TextRecognizerOptions +import com.visioncameramlkit.BuildConfig import com.visioncameramlkit.domain.models.TextRecognitionLanguage import com.visioncameramlkit.infrastructure.mlkit.MLKitTextRecognitionService object TextRecognitionServiceFactory { + @Suppress("LongMethod", "ThrowsCount") fun createTextRecognizer(language: TextRecognitionLanguage): TextRecognizer { - val options = + val options: TextRecognizerOptionsInterface = when (language) { - TextRecognitionLanguage.LATIN -> TextRecognizerOptions.DEFAULT_OPTIONS - TextRecognitionLanguage.CHINESE -> ChineseTextRecognizerOptions.Builder().build() - TextRecognitionLanguage.DEVANAGARI -> DevanagariTextRecognizerOptions.Builder().build() - TextRecognitionLanguage.JAPANESE -> JapaneseTextRecognizerOptions.Builder().build() - TextRecognitionLanguage.KOREAN -> KoreanTextRecognizerOptions.Builder().build() + TextRecognitionLanguage.LATIN -> { + @Suppress("KotlinConstantConditions") + if (!BuildConfig.MLKIT_TEXT_RECOGNITION) { + throw IllegalStateException( + "Text recognition for Latin is not enabled. " + + "Set textRecognition to true in your MLKit configuration.", + ) + } + TextRecognizerOptions.DEFAULT_OPTIONS + } + + TextRecognitionLanguage.CHINESE -> { + @Suppress("KotlinConstantConditions") + if (!BuildConfig.MLKIT_TEXT_RECOGNITION_CHINESE) { + throw IllegalStateException( + "Text recognition for Chinese is not enabled. " + + "Set textRecognitionChinese to true in your MLKit configuration.", + ) + } + createOptionsViaReflection( + "com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions", + ) + } + + TextRecognitionLanguage.DEVANAGARI -> { + @Suppress("KotlinConstantConditions") + if (!BuildConfig.MLKIT_TEXT_RECOGNITION_DEVANAGARI) { + throw IllegalStateException( + "Text recognition for Devanagari is not enabled. " + + "Set textRecognitionDevanagari to true in your MLKit configuration.", + ) + } + createOptionsViaReflection( + "com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions", + ) + } + + TextRecognitionLanguage.JAPANESE -> { + @Suppress("KotlinConstantConditions") + if (!BuildConfig.MLKIT_TEXT_RECOGNITION_JAPANESE) { + throw IllegalStateException( + "Text recognition for Japanese is not enabled. " + + "Set textRecognitionJapanese to true in your MLKit configuration.", + ) + } + createOptionsViaReflection( + "com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions", + ) + } + + TextRecognitionLanguage.KOREAN -> { + @Suppress("KotlinConstantConditions") + if (!BuildConfig.MLKIT_TEXT_RECOGNITION_KOREAN) { + throw IllegalStateException( + "Text recognition for Korean is not enabled. " + + "Set textRecognitionKorean to true in your MLKit configuration.", + ) + } + createOptionsViaReflection( + "com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions", + ) + } } return TextRecognition.getClient(options) } + /** + * Creates language-specific TextRecognizerOptions via reflection to avoid hard + * class dependencies on variant modules that may not be included in the build. + * Each variant options class follows the Builder pattern: OptionsClass.Builder().build() + */ + private fun createOptionsViaReflection(className: String): TextRecognizerOptionsInterface { + try { + val builderClass = Class.forName("$className\$Builder") + val builder = builderClass.getDeclaredConstructor().newInstance() + val buildMethod = builderClass.getMethod("build") + return buildMethod.invoke(builder) as TextRecognizerOptionsInterface + } catch (e: ClassNotFoundException) { + throw IllegalStateException( + "MLKit class $className not found. " + + "Ensure the corresponding text recognition dependency is included.", + e, + ) + } + } + fun create(language: TextRecognitionLanguage = TextRecognitionLanguage.LATIN): MLKitTextRecognitionService { val textRecognizer = createTextRecognizer(language) return MLKitTextRecognitionService(textRecognizer) diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt index ce552d3..f07b26a 100644 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt @@ -24,7 +24,7 @@ class VisionCameraMLKitModule( val handlers = mutableMapOf() @Suppress("KotlinConstantConditions") - if (BuildConfig.MLKIT_TEXT_RECOGNITION) { + if (BuildConfig.MLKIT_TEXT_RECOGNITION_ANY) { handlers[MLKitFeatureKeys.TEXT_RECOGNITION] = StaticTextRecognitionHandler(reactApplicationContext) } diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index e77f223..85ff1c8 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -3363,7 +3363,7 @@ SPEC CHECKSUMS: RNWorklets: ab7740c2a152f77ff76a40d52be860d8f128fab1 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 VisionCamera: 47f10351a1b6a8d251b90986ae72ca5f52331954 - VisionCameraMLKit: 573c04788106c92549faf3a46eea1771f2ac6c49 + VisionCameraMLKit: 5d0ba97a6fd96b6a497d91cdb0a62190e0a2ba71 Yoga: fa23995c18b65978347b096d0836f4f5093df545 PODFILE CHECKSUM: 1e33379caa52806ba900154e8eb803a55bbce31c diff --git a/ios/Application/UseCases/RecognizeTextUseCase.swift b/ios/Application/UseCases/RecognizeTextUseCase.swift index 05555cf..8c9f6f6 100644 --- a/ios/Application/UseCases/RecognizeTextUseCase.swift +++ b/ios/Application/UseCases/RecognizeTextUseCase.swift @@ -1,6 +1,6 @@ import Foundation -#if MLKIT_TEXT_RECOGNITION +#if MLKIT_TEXT_RECOGNITION_ANY #if canImport(VisionCamera) import VisionCamera @@ -71,4 +71,4 @@ import Foundation } } #endif -#endif +#endif // MLKIT_TEXT_RECOGNITION_ANY diff --git a/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift b/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift index 6982e03..6e2ebdb 100644 --- a/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift +++ b/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift @@ -1,6 +1,6 @@ import Foundation -#if MLKIT_TEXT_RECOGNITION +#if MLKIT_TEXT_RECOGNITION_ANY class StaticTextRecognitionHandler: IStaticImageHandler { private let textRecognitionSerializer = TextRecognitionSerializer() private var cachedUseCase: RecognizeTextUseCase? @@ -150,4 +150,4 @@ import Foundation return URL(fileURLWithPath: path) } } -#endif +#endif // MLKIT_TEXT_RECOGNITION_ANY diff --git a/ios/Bridge/Plugins/TextRecognitionPlugin.swift b/ios/Bridge/Plugins/TextRecognitionPlugin.swift index 2f36d64..2136941 100644 --- a/ios/Bridge/Plugins/TextRecognitionPlugin.swift +++ b/ios/Bridge/Plugins/TextRecognitionPlugin.swift @@ -1,6 +1,6 @@ import Foundation -#if MLKIT_TEXT_RECOGNITION +#if MLKIT_TEXT_RECOGNITION_ANY #if canImport(VisionCamera) import VisionCamera @@ -65,4 +65,4 @@ import Foundation } #endif -#endif +#endif // MLKIT_TEXT_RECOGNITION_ANY diff --git a/ios/Infrastructure/MLKit/Adapters/MLKitTextAdapter.swift b/ios/Infrastructure/MLKit/Adapters/MLKitTextAdapter.swift index 0e2d8fe..0f50c9d 100644 --- a/ios/Infrastructure/MLKit/Adapters/MLKitTextAdapter.swift +++ b/ios/Infrastructure/MLKit/Adapters/MLKitTextAdapter.swift @@ -1,6 +1,6 @@ import Foundation -#if MLKIT_TEXT_RECOGNITION +#if MLKIT_TEXT_RECOGNITION_ANY import MLKitTextRecognition class MLKitTextAdapter { @@ -77,4 +77,4 @@ import Foundation return Corner(x: Double(point.x), y: Double(point.y)) } } -#endif +#endif // MLKIT_TEXT_RECOGNITION_ANY diff --git a/ios/Infrastructure/MLKit/Factories/TextRecognitionServiceFactory.swift b/ios/Infrastructure/MLKit/Factories/TextRecognitionServiceFactory.swift index 6cf5717..435d610 100644 --- a/ios/Infrastructure/MLKit/Factories/TextRecognitionServiceFactory.swift +++ b/ios/Infrastructure/MLKit/Factories/TextRecognitionServiceFactory.swift @@ -1,6 +1,6 @@ import Foundation -#if MLKIT_TEXT_RECOGNITION +#if MLKIT_TEXT_RECOGNITION_ANY import MLKitTextRecognition #endif @@ -20,7 +20,7 @@ import Foundation import MLKitTextRecognitionKorean #endif -#if MLKIT_TEXT_RECOGNITION +#if MLKIT_TEXT_RECOGNITION_ANY class TextRecognitionServiceFactory { static func createTextRecognizer(language: TextRecognitionLanguage) @@ -61,4 +61,4 @@ import Foundation return MLKitTextRecognitionService(textRecognizer: textRecognizer) } } -#endif +#endif // MLKIT_TEXT_RECOGNITION_ANY diff --git a/ios/Infrastructure/MLKit/MLKitTextRecognitionService.swift b/ios/Infrastructure/MLKit/MLKitTextRecognitionService.swift index a2bfad0..1acfb10 100644 --- a/ios/Infrastructure/MLKit/MLKitTextRecognitionService.swift +++ b/ios/Infrastructure/MLKit/MLKitTextRecognitionService.swift @@ -1,6 +1,6 @@ import Foundation -#if MLKIT_TEXT_RECOGNITION +#if MLKIT_TEXT_RECOGNITION_ANY import MLKitTextRecognition #endif @@ -20,7 +20,7 @@ import Foundation import MLKitTextRecognitionKorean #endif -#if MLKIT_TEXT_RECOGNITION +#if MLKIT_TEXT_RECOGNITION_ANY class MLKitTextRecognitionService: IRecognitionService { typealias ResultType = TextRecognitionResult @@ -35,4 +35,4 @@ import Foundation return MLKitTextAdapter.toDomain(text) } } -#endif +#endif // MLKIT_TEXT_RECOGNITION_ANY diff --git a/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift b/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift index ada4980..1096fcf 100644 --- a/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift +++ b/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift @@ -6,7 +6,7 @@ class VisionCameraMLKitModule: NSObject { private lazy var handlers: [String: IStaticImageHandler] = { var handlers: [String: IStaticImageHandler] = [:] - #if MLKIT_TEXT_RECOGNITION + #if MLKIT_TEXT_RECOGNITION_ANY handlers[MLKitFeatureKeys.TEXT_RECOGNITION] = StaticTextRecognitionHandler() #endif diff --git a/ios/VisionCameraMLKit.mm b/ios/VisionCameraMLKit.mm index 0e49dce..0577bf2 100644 --- a/ios/VisionCameraMLKit.mm +++ b/ios/VisionCameraMLKit.mm @@ -32,6 +32,6 @@ +(void)load { \ } \ @end -#ifdef MLKIT_TEXT_RECOGNITION +#ifdef MLKIT_TEXT_RECOGNITION_ANY REGISTER_PLUGIN(TextRecognitionPlugin, @"TextRecognition") #endif diff --git a/package.json b/package.json index 356868e..f7ffed9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-vision-camera-mlkit", - "version": "1.0.0", + "version": "1.0.1", "description": "A powerful React Native Vision Camera plugin delivering high-performance Google ML Kit frame processor features—including text recognition (OCR), face detection, barcode scanning, pose detection, and more. Seamlessly bridges native ML Kit capabilities for real-time, on-device computer vision in your React Native apps.", "main": "./lib/module/index.js", "types": "./lib/typescript/src/index.d.ts", From ba5d0c1843a926c0eaa8d9d67f4f5b9e351c57c0 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Fri, 13 Mar 2026 21:24:39 -0300 Subject: [PATCH 11/45] feat(StaticImageProcessing): making orientation optional and defaulting to EXIF --- android/build.gradle | 1 + .../handlers/StaticTextRecognitionHandler.kt | 8 +++--- .../models/ImagePreprocessingOptions.kt | 2 +- .../infrastructure/image/ImagePreprocessor.kt | 27 ++++++++++++++++++- example/src/screens/ImageScreen.tsx | 12 ++++++--- .../src/stores/imageProcessingOptionsStore.ts | 2 +- .../StaticTextRecognitionHandler.swift | 18 +------------ .../Models/ImagePreprocessingOptions.swift | 4 +-- ios/Domain/Models/Orientation.swift | 12 +++++++++ .../Image/ImagePreprocessor.swift | 12 ++++++--- src/core/types.ts | 6 +++-- 11 files changed, 69 insertions(+), 35 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 0d2bb35..8fc71e8 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -146,6 +146,7 @@ dependencies { api project(":react-native-vision-camera") implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "androidx.camera:camera-core:1.5.2" + implementation "androidx.exifinterface:exifinterface:1.3.7" // ===== GoogleMLKit Vision ===== // Only include dependencies for enabled features diff --git a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt b/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt index 033703f..c033532 100644 --- a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt +++ b/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt @@ -133,18 +133,16 @@ class StaticTextRecognitionHandler( private fun parseImageOptions(options: ReadableMap): ImagePreprocessingOptions = ImagePreprocessingOptions( invertColors = options.getBoolean("invertColors"), - orientation = - options.getString("orientation")?.let { parseOrientation(it) } - ?: Orientation.PORTRAIT, + orientation = options.getString("orientation")?.let { parseOrientation(it) }, ) - private fun parseOrientation(orientation: String): Orientation = + private fun parseOrientation(orientation: String): Orientation? = when (orientation) { "portrait" -> Orientation.PORTRAIT "portrait-upside-down" -> Orientation.PORTRAIT_UPSIDE_DOWN "landscape-left" -> Orientation.LANDSCAPE_LEFT "landscape-right" -> Orientation.LANDSCAPE_RIGHT - else -> Orientation.PORTRAIT + else -> null } private fun parseLanguage(language: String): TextRecognitionLanguage = diff --git a/android/src/main/java/com/visioncameramlkit/domain/models/ImagePreprocessingOptions.kt b/android/src/main/java/com/visioncameramlkit/domain/models/ImagePreprocessingOptions.kt index 496e483..1bf9bda 100644 --- a/android/src/main/java/com/visioncameramlkit/domain/models/ImagePreprocessingOptions.kt +++ b/android/src/main/java/com/visioncameramlkit/domain/models/ImagePreprocessingOptions.kt @@ -5,5 +5,5 @@ data class ImagePreprocessingOptions( // Android handles rotation automatically, so outputOrientation is ignored. Added for iOS parity. val outputOrientation: OutputOrientation? = null, val scaleFactor: Float = 1.0f, - val orientation: Orientation = Orientation.PORTRAIT, + val orientation: Orientation? = null, ) diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt index 52a5a88..493c802 100644 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt @@ -10,6 +10,7 @@ import android.graphics.Matrix import android.graphics.Paint import android.media.Image import androidx.core.graphics.scale +import androidx.exifinterface.media.ExifInterface import com.google.mlkit.vision.common.InputImage import com.google.mlkit.vision.common.internal.ImageConvertUtils import com.mrousavy.camera.frameprocessors.Frame @@ -151,7 +152,11 @@ class ImagePreprocessor : IImagePreprocessor { BitmapFactory.decodeFile(imageFile.absolutePath) ?: throw UnsupportedOperationException("Failed to decode image file") - val rotatedBitmap = rotateBitmap(bitmap, options.orientation) + // Use the user-provided orientation override, or fall back to the image's + // embedded EXIF orientation metadata. + val effectiveOrientation = options.orientation ?: readExifOrientation(imageFile) + + val rotatedBitmap = rotateBitmap(bitmap, effectiveOrientation) val effectiveScale = clampScale(options.scaleFactor) val processedBitmap = @@ -192,6 +197,26 @@ class ImagePreprocessor : IImagePreprocessor { return ProcessedImage(inputImage, metadata) } + /** + * Reads EXIF orientation from the image file and maps it to an [Orientation]. + * Falls back to [Orientation.PORTRAIT] if EXIF data is unavailable or unrecognized. + */ + private fun readExifOrientation(imageFile: File): Orientation = + try { + val exif = ExifInterface(imageFile.absolutePath) + when (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)) { + ExifInterface.ORIENTATION_NORMAL -> Orientation.PORTRAIT + ExifInterface.ORIENTATION_ROTATE_180 -> Orientation.PORTRAIT_UPSIDE_DOWN + ExifInterface.ORIENTATION_ROTATE_90 -> Orientation.LANDSCAPE_LEFT + ExifInterface.ORIENTATION_ROTATE_270 -> Orientation.LANDSCAPE_RIGHT + else -> Orientation.PORTRAIT + } + } catch ( + @Suppress("TooGenericExceptionCaught") e: Exception, + ) { + Orientation.PORTRAIT + } + private fun rotateBitmap( bitmap: Bitmap, orientation: Orientation, diff --git a/example/src/screens/ImageScreen.tsx b/example/src/screens/ImageScreen.tsx index fe36e78..560f613 100644 --- a/example/src/screens/ImageScreen.tsx +++ b/example/src/screens/ImageScreen.tsx @@ -168,15 +168,21 @@ const ImageScreen = () => { /> setSharedOption('orientation', value)} + onValueChange={(value) => + setSharedOption( + 'orientation', + value === 'auto' ? undefined : value + ) + } /> {pluginId === PLUGIN_ID.TEXT_RECOGNITION && ( ((set) => ({ sharedOptions: { invertColors: false, - orientation: 'portrait', + orientation: undefined, }, imageProcessingOptions: { [PLUGIN_ID.TEXT_RECOGNITION]: { diff --git a/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift b/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift index 6e2ebdb..336a641 100644 --- a/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift +++ b/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift @@ -97,8 +97,7 @@ import Foundation -> ImagePreprocessingOptions { let invertColors = options["invertColors"] as? Bool ?? false - let orientationString = options["orientation"] as? String ?? "portrait" - let orientation = parseOrientation(orientationString) + let orientation = (options["orientation"] as? String).flatMap { Orientation(string: $0) } return ImagePreprocessingOptions( invertColors: invertColors, @@ -106,21 +105,6 @@ import Foundation ) } - private func parseOrientation(_ orientation: String) -> Orientation { - switch orientation { - case "portrait": - return .portrait - case "portrait-upside-down": - return .portraitUpsideDown - case "landscape-left": - return .landscapeLeft - case "landscape-right": - return .landscapeRight - default: - return .portrait - } - } - private func parseLanguage(_ language: String) -> TextRecognitionLanguage { switch language { case "LATIN": diff --git a/ios/Domain/Models/ImagePreprocessingOptions.swift b/ios/Domain/Models/ImagePreprocessingOptions.swift index ff88aed..cfceae6 100644 --- a/ios/Domain/Models/ImagePreprocessingOptions.swift +++ b/ios/Domain/Models/ImagePreprocessingOptions.swift @@ -4,13 +4,13 @@ public struct ImagePreprocessingOptions { let invertColors: Bool let outputOrientation: OutputOrientation? let scaleFactor: CGFloat - let orientation: Orientation + let orientation: Orientation? public init( invertColors: Bool = false, outputOrientation: OutputOrientation? = nil, scaleFactor: CGFloat = 1.0, - orientation: Orientation = .portrait + orientation: Orientation? = nil ) { self.invertColors = invertColors self.outputOrientation = outputOrientation diff --git a/ios/Domain/Models/Orientation.swift b/ios/Domain/Models/Orientation.swift index b12206b..05460ba 100644 --- a/ios/Domain/Models/Orientation.swift +++ b/ios/Domain/Models/Orientation.swift @@ -16,6 +16,18 @@ import Foundation } } + /// Maps a UIImage.Orientation (typically from EXIF metadata) back to an Orientation value. + public static func fromUIImageOrientation(_ imageOrientation: UIImage.Orientation) -> Orientation + { + switch imageOrientation { + case .up: return .portrait + case .down: return .portraitUpsideDown + case .left: return .landscapeLeft + case .right: return .landscapeRight + default: return .portrait + } + } + public var asUIImageOrientation: UIImage.Orientation { switch self { case .portrait: return .up diff --git a/ios/Infrastructure/Image/ImagePreprocessor.swift b/ios/Infrastructure/Image/ImagePreprocessor.swift index 4b33706..55230cf 100644 --- a/ios/Infrastructure/Image/ImagePreprocessor.swift +++ b/ios/Infrastructure/Image/ImagePreprocessor.swift @@ -245,8 +245,14 @@ import Foundation return nil } + // Use the user-provided orientation override, or fall back to the image's + // embedded metadata orientation (EXIF). UIImage(data:) already decodes + // EXIF orientation into uiImage.imageOrientation. + let effectiveOrientation = + options.orientation ?? Orientation.fromUIImageOrientation(uiImage.imageOrientation) + // Rotate image based on orientation - let rotatedImage = rotateUIImage(uiImage, orientation: options.orientation) + let rotatedImage = rotateUIImage(uiImage, orientation: effectiveOrientation) // Apply color inversion if needed let processedImage: UIImage @@ -258,12 +264,12 @@ import Foundation // Create VisionImage let visionImage = VisionImage(image: processedImage) - visionImage.orientation = options.orientation.asUIImageOrientation + visionImage.orientation = effectiveOrientation.asUIImageOrientation let metadata = ImageMetadata( width: Int(processedImage.size.width), height: Int(processedImage.size.height), - orientation: options.orientation.asUIImageOrientation, + orientation: effectiveOrientation.asUIImageOrientation, isInverted: options.invertColors ) diff --git a/src/core/types.ts b/src/core/types.ts index 4c00467..e148674 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -78,8 +78,10 @@ export type MLKitBaseArguments = { export type ImageProcessingBaseOptions = { /** - * The orientation of the image. Pass this to support rotation in text recognition. - * @default 'portrait' + * Override the image orientation for processing. + * When omitted, the orientation is automatically detected from the image's + * EXIF metadata. Provide an explicit value only if the metadata is missing + * or incorrect. */ orientation?: Orientation; /** From 5c42e94f9a872235041ced7578d5098a1d765b36 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Fri, 13 Mar 2026 21:27:24 -0300 Subject: [PATCH 12/45] fix(static-image): auto-detect orientation from EXIF and handle mirrored metadata --- .../infrastructure/image/ImagePreprocessor.kt | 20 ++++++++++--- ios/Domain/Models/Orientation.swift | 29 +++++++++++++++---- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt index 493c802..4e169f6 100644 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt @@ -205,10 +205,22 @@ class ImagePreprocessor : IImagePreprocessor { try { val exif = ExifInterface(imageFile.absolutePath) when (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)) { - ExifInterface.ORIENTATION_NORMAL -> Orientation.PORTRAIT - ExifInterface.ORIENTATION_ROTATE_180 -> Orientation.PORTRAIT_UPSIDE_DOWN - ExifInterface.ORIENTATION_ROTATE_90 -> Orientation.LANDSCAPE_LEFT - ExifInterface.ORIENTATION_ROTATE_270 -> Orientation.LANDSCAPE_RIGHT + ExifInterface.ORIENTATION_NORMAL, + ExifInterface.ORIENTATION_FLIP_HORIZONTAL, + -> Orientation.PORTRAIT + + ExifInterface.ORIENTATION_ROTATE_180, + ExifInterface.ORIENTATION_FLIP_VERTICAL, + -> Orientation.PORTRAIT_UPSIDE_DOWN + + ExifInterface.ORIENTATION_ROTATE_90, + ExifInterface.ORIENTATION_TRANSPOSE, + -> Orientation.LANDSCAPE_LEFT + + ExifInterface.ORIENTATION_ROTATE_270, + ExifInterface.ORIENTATION_TRANSVERSE, + -> Orientation.LANDSCAPE_RIGHT + else -> Orientation.PORTRAIT } } catch ( diff --git a/ios/Domain/Models/Orientation.swift b/ios/Domain/Models/Orientation.swift index 05460ba..1b099ad 100644 --- a/ios/Domain/Models/Orientation.swift +++ b/ios/Domain/Models/Orientation.swift @@ -1,5 +1,21 @@ import Foundation +#if canImport(UIKit) + import UIKit + public typealias NativeImageOrientation = UIImage.Orientation +#else + public enum NativeImageOrientation { + case up + case down + case left + case right + case upMirrored + case downMirrored + case leftMirrored + case rightMirrored + } +#endif + @objc public enum Orientation: Int { case portrait case portraitUpsideDown @@ -17,18 +33,19 @@ import Foundation } /// Maps a UIImage.Orientation (typically from EXIF metadata) back to an Orientation value. - public static func fromUIImageOrientation(_ imageOrientation: UIImage.Orientation) -> Orientation + public static func fromUIImageOrientation(_ imageOrientation: NativeImageOrientation) + -> Orientation { switch imageOrientation { - case .up: return .portrait - case .down: return .portraitUpsideDown - case .left: return .landscapeLeft - case .right: return .landscapeRight + case .up, .upMirrored: return .portrait + case .down, .downMirrored: return .portraitUpsideDown + case .left, .leftMirrored: return .landscapeLeft + case .right, .rightMirrored: return .landscapeRight default: return .portrait } } - public var asUIImageOrientation: UIImage.Orientation { + public var asUIImageOrientation: NativeImageOrientation { switch self { case .portrait: return .up case .portraitUpsideDown: return .down From bcb9940beb9851ea9bcb9fd37a04e8a04b1e7b4e Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sat, 21 Mar 2026 04:26:22 -0300 Subject: [PATCH 13/45] feat(barcode): add barcode mlkit api integration --- README.md | 120 ++++++- .../usecases/RecognizeBarcodesUseCase.kt | 45 +++ .../handlers/StaticBarcodeScanningHandler.kt | 169 +++++++++ .../parsers/BarcodeScanningOptionParser.kt | 41 +++ .../bridge/plugins/BarcodeScanningPlugin.kt | 47 +++ .../bridge/registry/PluginRegistry.kt | 32 ++ .../domain/models/BarcodeScanningOptions.kt | 28 ++ .../domain/models/BarcodeScanningResult.kt | 24 ++ .../mlkit/MLKitBarcodeScanningService.kt | 19 ++ .../mlkit/adapters/MLKitBarcodeAdapter.kt | 320 ++++++++++++++++++ .../BarcodeScanningServiceFactory.kt | 59 ++++ .../modules/VisionCameraMLKitModule.kt | 7 + .../serializers/BarcodeScanningSerializer.kt | 190 +++++++++++ docs/barcode-scanning.md | 130 +++++++ docs/text-recognition.md | 92 +++++ example/ios/Podfile.lock | 4 +- .../project.pbxproj | 64 ++-- example/src/components/views/CameraView.tsx | 14 + example/src/screens/HomeScreen.tsx | 2 +- example/src/screens/ImageScreen.tsx | 59 +++- example/src/screens/SettingsScreen.tsx | 52 +++ .../src/stores/imageProcessingOptionsStore.ts | 9 + example/src/stores/pluginOptionsStore.ts | 9 + .../UseCases/RecognizeBarcodesUseCase.swift | 77 +++++ .../StaticBarcodeScanningHandler.swift | 126 +++++++ .../Parsers/BarcodeScanningOptionParser.swift | 21 ++ .../Plugins/BarcodeScanningPlugin.swift | 50 +++ .../Models/BarcodeScanningOptions.swift | 43 +++ ios/Domain/Models/BarcodeScanningResult.swift | 26 ++ .../Image/ImagePreprocessor.swift | 74 ++-- .../MLKit/Adapters/MLKitBarcodeAdapter.swift | 274 +++++++++++++++ .../BarcodeScanningServiceFactory.swift | 65 ++++ .../MLKit/MLKitBarcodeScanningService.swift | 20 ++ .../Modules/VisionCameraMLKitModule.swift | 4 + .../BarcodeScanningSerializer.swift | 89 +++++ ios/VisionCameraMLKit.mm | 4 + src/features/barcode-scanning/index.ts | 52 +++ src/features/barcode-scanning/types.ts | 229 +++++++++++++ src/index.ts | 1 + 39 files changed, 2606 insertions(+), 85 deletions(-) create mode 100644 android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt create mode 100644 android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticBarcodeScanningHandler.kt create mode 100644 android/src/main/java/com/visioncameramlkit/bridge/parsers/BarcodeScanningOptionParser.kt create mode 100644 android/src/main/java/com/visioncameramlkit/bridge/plugins/BarcodeScanningPlugin.kt create mode 100644 android/src/main/java/com/visioncameramlkit/domain/models/BarcodeScanningOptions.kt create mode 100644 android/src/main/java/com/visioncameramlkit/domain/models/BarcodeScanningResult.kt create mode 100644 android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt create mode 100644 android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitBarcodeAdapter.kt create mode 100644 android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt create mode 100644 android/src/main/java/com/visioncameramlkit/infrastructure/serializers/BarcodeScanningSerializer.kt create mode 100644 docs/barcode-scanning.md create mode 100644 docs/text-recognition.md create mode 100644 ios/Application/UseCases/RecognizeBarcodesUseCase.swift create mode 100644 ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift create mode 100644 ios/Bridge/Parsers/BarcodeScanningOptionParser.swift create mode 100644 ios/Bridge/Plugins/BarcodeScanningPlugin.swift create mode 100644 ios/Domain/Models/BarcodeScanningOptions.swift create mode 100644 ios/Domain/Models/BarcodeScanningResult.swift create mode 100644 ios/Infrastructure/MLKit/Adapters/MLKitBarcodeAdapter.swift create mode 100644 ios/Infrastructure/MLKit/Factories/BarcodeScanningServiceFactory.swift create mode 100644 ios/Infrastructure/MLKit/MLKitBarcodeScanningService.swift create mode 100644 ios/Infrastructure/Serializers/BarcodeScanningSerializer.swift create mode 100644 src/features/barcode-scanning/index.ts create mode 100644 src/features/barcode-scanning/types.ts diff --git a/README.md b/README.md index 59cf122..4b2bc93 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,11 @@ Android-only keys: `faceMeshDetection`, `subjectSegmentation`, `documentScanner` ## Usage +### API Docs + +- [Text Recognition API](docs/text-recognition.md) +- [Barcode Scanning API](docs/barcode-scanning.md) + ### Text Recognition (Frame Processor) ```ts @@ -188,6 +193,117 @@ console.log(result.blocks); > The native bridge normalizes URIs (`file://` is removed on iOS and added on Android if missing). Supported formats: JPEG, PNG, WebP. +### Barcode Scanning (Frame Processor) + +```ts +import { + useFrameProcessor, + runAsync, + runAtTargetFps, +} from 'react-native-vision-camera'; +import { useBarcodeScanning } from 'react-native-vision-camera-mlkit'; + +const { barcodeScanning } = useBarcodeScanning({ + formats: ['QR_CODE', 'CODE_128'], + enableAllPotentialBarcodes: true, + scaleFactor: 1, + invertColors: false, +}); + +const frameProcessor = useFrameProcessor( + (frame) => { + 'worklet'; + + runAtTargetFps(10, () => { + 'worklet'; + runAsync(frame, () => { + 'worklet'; + const result = barcodeScanning(frame, { + outputOrientation: 'portrait', + }); + + for (const barcode of result.barcodes) { + console.log( + barcode.formatName, + barcode.valueTypeName, + barcode.rawValue + ); + if (barcode.value?.type === 'TYPE_URL') { + console.log(barcode.value.data.url); + } + } + }); + }); + }, + [barcodeScanning] +); +``` + +`BarcodeScanningOptions`: + +- `formats?: ('UNKNOWN' | 'ALL_FORMATS' | 'CODE_128' | 'CODE_39' | 'CODE_93' | 'CODABAR' | 'DATA_MATRIX' | 'EAN_13' | 'EAN_8' | 'ITF' | 'QR_CODE' | 'UPC_A' | 'UPC_E' | 'PDF417' | 'AZTEC')[]` +- `enableAllPotentialBarcodes?: boolean` (Android only) +- `scaleFactor?: number` (0.9-1.0) +- `invertColors?: boolean` +- `frameProcessInterval?: number` (deprecated, use `runAtTargetFps`) + +Supported `formats` values: + +| Value | Symbology | +| ------------- | ----------- | +| `ALL_FORMATS` | All types | +| `QR_CODE` | QR Code | +| `AZTEC` | Aztec | +| `PDF417` | PDF417 | +| `DATA_MATRIX` | Data Matrix | +| `CODE_128` | Code 128 | +| `CODE_39` | Code 39 | +| `CODE_93` | Code 93 | +| `CODABAR` | Codabar | +| `EAN_13` | EAN-13 | +| `EAN_8` | EAN-8 | +| `ITF` | ITF | +| `UPC_A` | UPC-A | +| `UPC_E` | UPC-E | +| `UNKNOWN` | Unknown | + +`BarcodeScanningArguments`: + +- `outputOrientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` (iOS only) + +### Barcode Scanning (Static Images) + +Use `processImageBarcodeScanning` to analyze a file path or URI without the camera. + +```ts +import { processImageBarcodeScanning } from 'react-native-vision-camera-mlkit'; + +const result = await processImageBarcodeScanning(imageUri, { + formats: ['QR_CODE', 'PDF417'], + enableAllPotentialBarcodes: true, + orientation: 'portrait', + invertColors: false, + scaleFactor: 1, +}); + +for (const barcode of result.barcodes) { + console.log(barcode.formatName, barcode.valueTypeName, barcode.displayValue); +} +``` + +`BarcodeScanningImageOptions`: + +- `formats?: ('UNKNOWN' | 'ALL_FORMATS' | 'CODE_128' | 'CODE_39' | 'CODE_93' | 'CODABAR' | 'DATA_MATRIX' | 'EAN_13' | 'EAN_8' | 'ITF' | 'QR_CODE' | 'UPC_A' | 'UPC_E' | 'PDF417' | 'AZTEC')[]` +- `enableAllPotentialBarcodes?: boolean` (Android only) +- `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` +- `invertColors?: boolean` +- `scaleFactor?: number` (0.9-1.0) + +`BarcodeScanningResult` includes: + +- `barcodes[]` with `format`, `formatName`, `valueType`, `valueTypeName`, `rawValue`, `displayValue`, `rawBytes`, `bounds`, `corners`, `isPotential` +- `value?: { type: TYPE_*, data: ... }` for parsed payloads (WiFi, URL, SMS, Contact, Calendar Event, Driver License, etc.) + ### Feature Utilities The package also exposes helpers from the plugin factory: @@ -248,7 +364,7 @@ The library works correctly on **physical iOS devices** and on the **iOS Simulat | # | Feature | Status | Platform | | --- | --------------------------------- | ------------------------------------------ | ------------------------------------------------- | | 0 | **Text recognition v2** | [![complete][complete]][complete] | [![android][android]][android] [![ios][ios]][ios] | -| 1 | **Barcode scanning** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | +| 1 | **Barcode scanning** | [![complete][complete]][complete] | [![android][android]][android] [![ios][ios]][ios] | | 2 | **Face detection** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | | 3 | **Face mesh detection** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] | | 4 | **Pose detection** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | @@ -269,10 +385,8 @@ If you’re integrating this library into a production app, consider funding the [complete]: https://img.shields.io/badge/COMPLETE-5E5CE6 [in-progress]: https://img.shields.io/badge/IN%20PROGRESS-FFD60A - [android]: https://img.shields.io/badge/ANDROID-3DDC84 [ios]: https://img.shields.io/badge/IOS-0A84FF - [contributors-shield]: https://img.shields.io/github/contributors/pedrol2b/react-native-vision-camera-mlkit.svg?style=for-the-badge [contributors-url]: https://github.com/pedrol2b/react-native-vision-camera-mlkit/graphs/contributors [forks-shield]: https://img.shields.io/github/forks/pedrol2b/react-native-vision-camera-mlkit.svg?style=for-the-badge diff --git a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt b/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt new file mode 100644 index 0000000..d17142a --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt @@ -0,0 +1,45 @@ +package com.visioncameramlkit.application.usecases + +import com.mrousavy.camera.frameprocessors.Frame +import com.visioncameramlkit.domain.models.BarcodeScanningOptions +import com.visioncameramlkit.domain.models.BarcodeScanningResult +import com.visioncameramlkit.domain.models.ImagePreprocessingOptions +import com.visioncameramlkit.domain.services.IImagePreprocessor +import com.visioncameramlkit.domain.services.IRecognitionService +import java.io.File + +class RecognizeBarcodesUseCase( + private val imagePreprocessor: IImagePreprocessor, + private val recognitionService: IRecognitionService, +) { + fun execute( + frame: Frame, + options: BarcodeScanningOptions, + ): BarcodeScanningResult { + val preprocessingOptions = + ImagePreprocessingOptions( + invertColors = options.invertColors, + outputOrientation = options.outputOrientation, + scaleFactor = options.scaleFactor, + ) + + val processedImage = imagePreprocessor.preprocessFrame(frame, preprocessingOptions) + return recognitionService.recognize(processedImage) + } + + fun execute( + imageFile: File, + imageOptions: ImagePreprocessingOptions, + @Suppress("unused") barcodeOptions: BarcodeScanningOptions, + ): BarcodeScanningResult { + val preprocessingOptions = + ImagePreprocessingOptions( + invertColors = imageOptions.invertColors, + orientation = imageOptions.orientation, + scaleFactor = imageOptions.scaleFactor, + ) + + val processedImage = imagePreprocessor.preprocessImage(imageFile, preprocessingOptions) + return recognitionService.recognize(processedImage) + } +} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticBarcodeScanningHandler.kt b/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticBarcodeScanningHandler.kt new file mode 100644 index 0000000..6526044 --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticBarcodeScanningHandler.kt @@ -0,0 +1,169 @@ +package com.visioncameramlkit.bridge.handlers + +import android.net.Uri +import androidx.core.net.toUri +import com.facebook.react.bridge.Promise +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReadableMap +import com.visioncameramlkit.application.usecases.RecognizeBarcodesUseCase +import com.visioncameramlkit.bridge.parsers.BarcodeScanningOptionParser +import com.visioncameramlkit.domain.models.BarcodeScanningOptions +import com.visioncameramlkit.domain.models.ImagePreprocessingOptions +import com.visioncameramlkit.domain.models.Orientation +import com.visioncameramlkit.infrastructure.image.ImagePreprocessor +import com.visioncameramlkit.infrastructure.mlkit.factories.BarcodeScanningServiceFactory +import com.visioncameramlkit.infrastructure.serializers.BarcodeScanningSerializer +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import java.io.File +import java.io.FileOutputStream + +class StaticBarcodeScanningHandler( + private val reactContext: ReactApplicationContext, +) : IStaticImageHandler { + private val cachedUseCases = mutableMapOf() + + private fun getRecognizeBarcodesUseCase(options: BarcodeScanningOptions): RecognizeBarcodesUseCase { + val cacheKey = + buildString { + append(options.enableAllPotentialBarcodes) + append("|") + append(options.formats.joinToString(separator = ",") { it.name }) + } + + return cachedUseCases.getOrPut(cacheKey) { + RecognizeBarcodesUseCase( + ImagePreprocessor(), + BarcodeScanningServiceFactory.create(options), + ) + } + } + + override fun process( + path: String, + options: ReadableMap, + promise: Promise, + ) { + CoroutineScope(Dispatchers.IO).launch { + var resolvedImage: ResolvedImage? = null + try { + resolvedImage = resolveImage(path) + val imageFile = resolvedImage.file + if (!imageFile.exists()) { + promise.reject("IMAGE_NOT_FOUND_ERROR", "Image file not found at path: $path") + return@launch + } + + val imageOptions = parseImageOptions(options) + val barcodeOptions = parseBarcodeOptions(options) + val recognizeBarcodesUseCase = getRecognizeBarcodesUseCase(barcodeOptions) + + val result = recognizeBarcodesUseCase.execute(imageFile, imageOptions, barcodeOptions) + val serializedResult = BarcodeScanningSerializer.toWritableMap(result) + + promise.resolve(serializedResult) + } catch ( + @Suppress("TooGenericExceptionCaught") e: Exception, + ) { + when (e) { + is IllegalArgumentException -> { + promise.reject("INVALID_URI_ERROR", e.message) + } + + is UnsupportedOperationException -> { + promise.reject( + "UNSUPPORTED_IMAGE_FORMAT_ERROR", + e.message, + ) + } + + else -> { + promise.reject( + "IMAGE_PROCESSING_FAILED_ERROR", + e.message ?: "Barcode scanning failed", + ) + } + } + } finally { + val imageFile = resolvedImage?.file + if (resolvedImage?.shouldDelete == true && imageFile != null && imageFile.exists()) { + imageFile.delete() + } + } + } + } + + private data class ResolvedImage( + val file: File, + val shouldDelete: Boolean, + ) + + private fun resolveImage(path: String): ResolvedImage { + val uri = path.toUri() + return when (uri.scheme) { + "content" -> ResolvedImage(copyContentToCache(uri), true) + "file" -> ResolvedImage(File(uri.path ?: path), false) + null -> ResolvedImage(File(path), false) + else -> ResolvedImage(File(path), false) + } + } + + private fun copyContentToCache(uri: Uri): File { + val cacheFile = File(reactContext.cacheDir, "mlkit_${System.currentTimeMillis()}.tmp") + val inputStream = + reactContext.contentResolver.openInputStream(uri) + ?: throw IllegalArgumentException("Unable to open content URI: $uri") + inputStream.use { input -> + FileOutputStream(cacheFile).use { output -> + input.copyTo(output) + } + } + return cacheFile + } + + private fun parseImageOptions(options: ReadableMap): ImagePreprocessingOptions = + ImagePreprocessingOptions( + invertColors = options.getOrDefault("invertColors", false), + orientation = options.getString("orientation")?.let { parseOrientation(it) }, + scaleFactor = options.getOrDefault("scaleFactor", 1.0f), + ) + + private fun parseBarcodeOptions(options: ReadableMap): BarcodeScanningOptions = + BarcodeScanningOptions( + formats = BarcodeScanningOptionParser.parseFormats(options), + enableAllPotentialBarcodes = + BarcodeScanningOptionParser.parseEnableAllPotentialBarcodes( + options, + ), + ) + + private fun parseOrientation(orientation: String): Orientation? = + when (orientation) { + "portrait" -> Orientation.PORTRAIT + "portrait-upside-down" -> Orientation.PORTRAIT_UPSIDE_DOWN + "landscape-left" -> Orientation.LANDSCAPE_LEFT + "landscape-right" -> Orientation.LANDSCAPE_RIGHT + else -> null + } + + private fun ReadableMap.getOrDefault( + key: String, + defaultValue: Boolean, + ): Boolean { + if (!hasKey(key) || isNull(key)) { + return defaultValue + } + return getBoolean(key) + } + + private fun ReadableMap.getOrDefault( + key: String, + defaultValue: Float, + ): Float { + if (!hasKey(key) || isNull(key)) { + return defaultValue + } + return getDouble(key).toFloat() + } +} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/parsers/BarcodeScanningOptionParser.kt b/android/src/main/java/com/visioncameramlkit/bridge/parsers/BarcodeScanningOptionParser.kt new file mode 100644 index 0000000..61c5912 --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/bridge/parsers/BarcodeScanningOptionParser.kt @@ -0,0 +1,41 @@ +package com.visioncameramlkit.bridge.parsers + +import com.facebook.react.bridge.ReadableArray +import com.facebook.react.bridge.ReadableMap +import com.visioncameramlkit.domain.models.BarcodeFormatOption + +object BarcodeScanningOptionParser { + fun parseFormats(value: Any?): List = + (value as? List<*>) + ?.mapNotNull { item -> parseFormat(item as? String) } + ?: emptyList() + + @Suppress("ReturnCount") + fun parseFormats(readableMap: ReadableMap): List { + if (!readableMap.hasKey("formats") || readableMap.isNull("formats")) { + return emptyList() + } + val readableArray: ReadableArray = readableMap.getArray("formats") ?: return emptyList() + val parsed = mutableListOf() + for (index in 0 until readableArray.size()) { + val formatString = readableArray.getString(index) + parseFormat(formatString)?.let(parsed::add) + } + return parsed + } + + fun parseEnableAllPotentialBarcodes(value: Any?): Boolean = value as? Boolean ?: false + + fun parseEnableAllPotentialBarcodes(readableMap: ReadableMap): Boolean { + if (!readableMap.hasKey("enableAllPotentialBarcodes") || readableMap.isNull("enableAllPotentialBarcodes")) { + return false + } + return readableMap.getBoolean("enableAllPotentialBarcodes") + } + + private fun parseFormat(format: String?): BarcodeFormatOption? = + format + ?.trim() + ?.takeIf { it.isNotEmpty() } + ?.let { name -> runCatching { BarcodeFormatOption.valueOf(name) }.getOrNull() } +} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/plugins/BarcodeScanningPlugin.kt b/android/src/main/java/com/visioncameramlkit/bridge/plugins/BarcodeScanningPlugin.kt new file mode 100644 index 0000000..f96f965 --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/bridge/plugins/BarcodeScanningPlugin.kt @@ -0,0 +1,47 @@ +package com.visioncameramlkit.bridge.plugins + +import com.mrousavy.camera.frameprocessors.Frame +import com.mrousavy.camera.frameprocessors.VisionCameraProxy +import com.visioncameramlkit.application.usecases.RecognizeBarcodesUseCase +import com.visioncameramlkit.bridge.parsers.BarcodeScanningOptionParser +import com.visioncameramlkit.domain.models.BarcodeScanningOptions +import com.visioncameramlkit.infrastructure.image.ImagePreprocessor +import com.visioncameramlkit.infrastructure.mlkit.factories.BarcodeScanningServiceFactory +import com.visioncameramlkit.infrastructure.serializers.BarcodeScanningSerializer + +@Suppress("unused") +class BarcodeScanningPlugin( + proxy: VisionCameraProxy, + options: Map?, +) : BaseMLKitPlugin(proxy, options) { + override val tag = "BarcodeScanningPlugin" + + private val barcodeScanningOptions: BarcodeScanningOptions = + BarcodeScanningOptions( + formats = BarcodeScanningOptionParser.parseFormats(options?.get("formats")), + enableAllPotentialBarcodes = + BarcodeScanningOptionParser.parseEnableAllPotentialBarcodes( + options?.get("enableAllPotentialBarcodes"), + ), + invertColors = invertColors, + outputOrientation = outputOrientation, + scaleFactor = scaleFactor, + ) + private val recognizeBarcodesUseCase: RecognizeBarcodesUseCase + + init { + + val recognitionService = BarcodeScanningServiceFactory.create(barcodeScanningOptions) + val imagePreprocessor = ImagePreprocessor() + + recognizeBarcodesUseCase = RecognizeBarcodesUseCase(imagePreprocessor, recognitionService) + } + + override fun processFrame( + frame: Frame, + arguments: Map?, + ): HashMap { + val result = recognizeBarcodesUseCase.execute(frame, barcodeScanningOptions) + return BarcodeScanningSerializer.toReactNativeMap(result) + } +} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt b/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt index 13a7122..73d75de 100644 --- a/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt +++ b/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt @@ -19,6 +19,11 @@ object PluginRegistry { if (BuildConfig.MLKIT_TEXT_RECOGNITION_ANY) { registerTextRecognitionPlugin() } + + @Suppress("KotlinConstantConditions") + if (BuildConfig.MLKIT_BARCODE_SCANNING) { + registerBarcodeScanningPlugin() + } } private fun registerTextRecognitionPlugin() { @@ -47,4 +52,31 @@ object PluginRegistry { android.util.Log.e("VisionCameraMLKit", "Error registering TextRecognitionPlugin", e) } } + + private fun registerBarcodeScanningPlugin() { + try { + val pluginClass = + Class.forName("com.visioncameramlkit.bridge.plugins.BarcodeScanningPlugin") + + FrameProcessorPluginRegistry.addFrameProcessorPlugin( + MLKitFeatureKeys.BARCODE_SCANNING, + ) { proxy, options -> + val constructor = + pluginClass.getConstructor( + com.mrousavy.camera.frameprocessors.VisionCameraProxy::class.java, + Map::class.java, + ) + constructor.newInstance( + proxy, + options, + ) as com.mrousavy.camera.frameprocessors.FrameProcessorPlugin + } + } catch (e: ClassNotFoundException) { + android.util.Log.w("VisionCameraMLKit", "BarcodeScanningPlugin not available: ${e.message}") + } catch ( + @Suppress("TooGenericExceptionCaught") e: Exception, + ) { + android.util.Log.e("VisionCameraMLKit", "Error registering BarcodeScanningPlugin", e) + } + } } diff --git a/android/src/main/java/com/visioncameramlkit/domain/models/BarcodeScanningOptions.kt b/android/src/main/java/com/visioncameramlkit/domain/models/BarcodeScanningOptions.kt new file mode 100644 index 0000000..ac69be6 --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/domain/models/BarcodeScanningOptions.kt @@ -0,0 +1,28 @@ +package com.visioncameramlkit.domain.models + +data class BarcodeScanningOptions( + val formats: List = emptyList(), + val enableAllPotentialBarcodes: Boolean = false, + val invertColors: Boolean = false, + // Android handles rotation automatically, so outputOrientation is ignored. Added for iOS parity. + val outputOrientation: OutputOrientation? = null, + val scaleFactor: Float = 1.0f, +) + +enum class BarcodeFormatOption { + UNKNOWN, + ALL_FORMATS, + CODE_128, + CODE_39, + CODE_93, + CODABAR, + DATA_MATRIX, + EAN_13, + EAN_8, + ITF, + QR_CODE, + UPC_A, + UPC_E, + PDF417, + AZTEC, +} diff --git a/android/src/main/java/com/visioncameramlkit/domain/models/BarcodeScanningResult.kt b/android/src/main/java/com/visioncameramlkit/domain/models/BarcodeScanningResult.kt new file mode 100644 index 0000000..0c6d1ae --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/domain/models/BarcodeScanningResult.kt @@ -0,0 +1,24 @@ +package com.visioncameramlkit.domain.models + +data class BarcodeScanningResult( + val barcodes: List, +) + +data class BarcodeData( + val bounds: BoundingBox?, + val corners: List?, + val format: Int, + val formatName: String, + val valueType: Int, + val valueTypeName: String, + val rawValue: String?, + val displayValue: String?, + val rawBytes: List?, + val isPotential: Boolean, + val value: BarcodeParsedValue?, +) + +data class BarcodeParsedValue( + val type: String, + val data: Map, +) diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt new file mode 100644 index 0000000..6c93181 --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt @@ -0,0 +1,19 @@ +package com.visioncameramlkit.infrastructure.mlkit + +import com.google.android.gms.tasks.Tasks +import com.google.mlkit.vision.barcode.BarcodeScanner +import com.google.mlkit.vision.barcode.common.Barcode +import com.visioncameramlkit.domain.models.BarcodeScanningResult +import com.visioncameramlkit.domain.models.ProcessedImage +import com.visioncameramlkit.domain.services.IRecognitionService +import com.visioncameramlkit.infrastructure.mlkit.adapters.MLKitBarcodeAdapter + +class MLKitBarcodeScanningService( + private val barcodeScanner: BarcodeScanner, +) : IRecognitionService { + override fun recognize(image: ProcessedImage): BarcodeScanningResult { + val task = barcodeScanner.process(image.image) + val barcodes: List = Tasks.await(task) + return MLKitBarcodeAdapter.toDomain(barcodes) + } +} diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitBarcodeAdapter.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitBarcodeAdapter.kt new file mode 100644 index 0000000..06ae9a0 --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitBarcodeAdapter.kt @@ -0,0 +1,320 @@ +package com.visioncameramlkit.infrastructure.mlkit.adapters + +import com.google.mlkit.vision.barcode.common.Barcode +import com.visioncameramlkit.domain.models.BarcodeData +import com.visioncameramlkit.domain.models.BarcodeParsedValue +import com.visioncameramlkit.domain.models.BarcodeScanningResult +import com.visioncameramlkit.domain.models.BoundingBox +import com.visioncameramlkit.domain.models.Corner + +@Suppress("TooManyFunctions") +object MLKitBarcodeAdapter { + private const val BYTE_MASK = 0xFF + + private val formatNameMap: Map = + mapOf( + Barcode.FORMAT_CODE_128 to "CODE_128", + Barcode.FORMAT_CODE_39 to "CODE_39", + Barcode.FORMAT_CODE_93 to "CODE_93", + Barcode.FORMAT_CODABAR to "CODABAR", + Barcode.FORMAT_DATA_MATRIX to "DATA_MATRIX", + Barcode.FORMAT_EAN_13 to "EAN_13", + Barcode.FORMAT_EAN_8 to "EAN_8", + Barcode.FORMAT_ITF to "ITF", + Barcode.FORMAT_QR_CODE to "QR_CODE", + Barcode.FORMAT_UPC_A to "UPC_A", + Barcode.FORMAT_UPC_E to "UPC_E", + Barcode.FORMAT_PDF417 to "PDF417", + Barcode.FORMAT_AZTEC to "AZTEC", + Barcode.FORMAT_UNKNOWN to "UNKNOWN", + ) + + private val valueTypeNameMap: Map = + mapOf( + Barcode.TYPE_CONTACT_INFO to "TYPE_CONTACT_INFO", + Barcode.TYPE_EMAIL to "TYPE_EMAIL", + Barcode.TYPE_ISBN to "TYPE_ISBN", + Barcode.TYPE_PHONE to "TYPE_PHONE", + Barcode.TYPE_PRODUCT to "TYPE_PRODUCT", + Barcode.TYPE_SMS to "TYPE_SMS", + Barcode.TYPE_TEXT to "TYPE_TEXT", + Barcode.TYPE_URL to "TYPE_URL", + Barcode.TYPE_WIFI to "TYPE_WIFI", + Barcode.TYPE_GEO to "TYPE_GEO", + Barcode.TYPE_CALENDAR_EVENT to "TYPE_CALENDAR_EVENT", + Barcode.TYPE_DRIVER_LICENSE to "TYPE_DRIVER_LICENSE", + Barcode.TYPE_UNKNOWN to "TYPE_UNKNOWN", + ) + + private val parsedValueExtractors: Map BarcodeParsedValue?> = + mapOf( + Barcode.TYPE_WIFI to { barcode -> barcode.wifi?.let(::toWifi) }, + Barcode.TYPE_URL to { barcode -> barcode.url?.let(::toUrl) }, + Barcode.TYPE_SMS to { barcode -> barcode.sms?.let(::toSms) }, + Barcode.TYPE_EMAIL to { barcode -> barcode.email?.let(::toEmail) }, + Barcode.TYPE_PHONE to { barcode -> barcode.phone?.let(::toPhone) }, + Barcode.TYPE_CONTACT_INFO to { barcode -> barcode.contactInfo?.let(::toContactInfo) }, + Barcode.TYPE_CALENDAR_EVENT to { barcode -> barcode.calendarEvent?.let(::toCalendarEvent) }, + Barcode.TYPE_GEO to { barcode -> barcode.geoPoint?.let(::toGeoPoint) }, + Barcode.TYPE_DRIVER_LICENSE to { barcode -> barcode.driverLicense?.let(::toDriverLicense) }, + ) + + private val rawValueTypeMappings: Map> = + mapOf( + Barcode.TYPE_TEXT to ("TYPE_TEXT" to "text"), + Barcode.TYPE_PRODUCT to ("TYPE_PRODUCT" to "product"), + Barcode.TYPE_ISBN to ("TYPE_ISBN" to "isbn"), + ) + + fun toDomain(barcodes: List): BarcodeScanningResult = + BarcodeScanningResult( + barcodes = barcodes.map(::toBarcodeData), + ) + + private fun toBarcodeData(barcode: Barcode): BarcodeData = + BarcodeData( + bounds = barcode.boundingBox?.let(::toBoundingBox), + corners = barcode.cornerPoints?.map(::toCorner), + format = barcode.format, + formatName = formatName(barcode.format), + valueType = barcode.valueType, + valueTypeName = valueTypeName(barcode.valueType), + rawValue = barcode.rawValue, + displayValue = barcode.displayValue, + rawBytes = barcode.rawBytes?.map { byte -> byte.toInt() and BYTE_MASK }, + isPotential = barcode.rawValue == null && barcode.rawBytes == null, + value = toParsedValue(barcode), + ) + + private fun toParsedValue(barcode: Barcode): BarcodeParsedValue? { + parsedValueExtractors[barcode.valueType]?.invoke(barcode)?.let { parsed -> + return parsed + } + + val rawMapping = rawValueTypeMappings[barcode.valueType] ?: return null + val rawValue = barcode.rawValue ?: return null + return BarcodeParsedValue( + type = rawMapping.first, + data = mapOf(rawMapping.second to rawValue), + ) + } + + private fun toWifi(wifi: Barcode.WiFi): BarcodeParsedValue = + BarcodeParsedValue( + type = "TYPE_WIFI", + data = + mapOf( + "ssid" to wifi.ssid, + "password" to wifi.password, + "encryptionType" to wifi.encryptionType, + "encryptionTypeName" to wifiEncryptionName(wifi.encryptionType), + ), + ) + + private fun toUrl(url: Barcode.UrlBookmark): BarcodeParsedValue = + BarcodeParsedValue( + type = "TYPE_URL", + data = + mapOf( + "title" to url.title, + "url" to url.url, + ), + ) + + private fun toSms(sms: Barcode.Sms): BarcodeParsedValue = + BarcodeParsedValue( + type = "TYPE_SMS", + data = + mapOf( + "message" to sms.message, + "phoneNumber" to sms.phoneNumber, + ), + ) + + private fun toEmail(email: Barcode.Email): BarcodeParsedValue = + BarcodeParsedValue( + type = "TYPE_EMAIL", + data = + mapOf( + "address" to email.address, + "subject" to email.subject, + "body" to email.body, + "type" to email.type, + "typeName" to emailTypeName(email.type), + ), + ) + + private fun toPhone(phone: Barcode.Phone): BarcodeParsedValue = + BarcodeParsedValue( + type = "TYPE_PHONE", + data = + mapOf( + "number" to phone.number, + "type" to phone.type, + "typeName" to phoneTypeName(phone.type), + ), + ) + + private fun toContactInfo(contactInfo: Barcode.ContactInfo): BarcodeParsedValue = + BarcodeParsedValue( + type = "TYPE_CONTACT_INFO", + data = + mapOf( + "name" to contactInfo.name?.let(::toPersonName), + "organization" to contactInfo.organization, + "title" to contactInfo.title, + "phones" to contactInfo.phones.map(::toPhoneMap), + "emails" to contactInfo.emails.map(::toEmailMap), + "addresses" to contactInfo.addresses.map(::toAddressMap), + "urls" to contactInfo.urls, + ), + ) + + private fun toCalendarEvent(event: Barcode.CalendarEvent): BarcodeParsedValue = + BarcodeParsedValue( + type = "TYPE_CALENDAR_EVENT", + data = + mapOf( + "summary" to event.summary, + "description" to event.description, + "location" to event.location, + "organizer" to event.organizer, + "status" to event.status, + "start" to event.start?.let(::toCalendarDateTime), + "end" to event.end?.let(::toCalendarDateTime), + ), + ) + + private fun toGeoPoint(geoPoint: Barcode.GeoPoint): BarcodeParsedValue = + BarcodeParsedValue( + type = "TYPE_GEO", + data = + mapOf( + "lat" to geoPoint.lat, + "lng" to geoPoint.lng, + ), + ) + + private fun toDriverLicense(driverLicense: Barcode.DriverLicense): BarcodeParsedValue = + BarcodeParsedValue( + type = "TYPE_DRIVER_LICENSE", + data = + mapOf( + "documentType" to driverLicense.documentType, + "licenseNumber" to driverLicense.licenseNumber, + "firstName" to driverLicense.firstName, + "middleName" to driverLicense.middleName, + "lastName" to driverLicense.lastName, + "gender" to driverLicense.gender, + "addressStreet" to driverLicense.addressStreet, + "addressCity" to driverLicense.addressCity, + "addressState" to driverLicense.addressState, + "addressZip" to driverLicense.addressZip, + "birthDate" to driverLicense.birthDate, + "issueDate" to driverLicense.issueDate, + "expiryDate" to driverLicense.expiryDate, + "issuingCountry" to driverLicense.issuingCountry, + ), + ) + + private fun toPersonName(name: Barcode.PersonName): Map = + mapOf( + "formattedName" to name.formattedName, + "pronunciation" to name.pronunciation, + "prefix" to name.prefix, + "first" to name.first, + "middle" to name.middle, + "last" to name.last, + "suffix" to name.suffix, + ) + + private fun toPhoneMap(phone: Barcode.Phone): Map = + mapOf( + "number" to phone.number, + "type" to phone.type, + "typeName" to phoneTypeName(phone.type), + ) + + private fun toEmailMap(email: Barcode.Email): Map = + mapOf( + "address" to email.address, + "subject" to email.subject, + "body" to email.body, + "type" to email.type, + "typeName" to emailTypeName(email.type), + ) + + private fun toAddressMap(address: Barcode.Address): Map = + mapOf( + "addressLines" to address.addressLines.toList(), + "type" to address.type, + "typeName" to addressTypeName(address.type), + ) + + private fun toCalendarDateTime(dateTime: Barcode.CalendarDateTime): Map = + mapOf( + "year" to dateTime.year, + "month" to dateTime.month, + "day" to dateTime.day, + "hours" to dateTime.hours, + "minutes" to dateTime.minutes, + "seconds" to dateTime.seconds, + "isUtc" to dateTime.isUtc, + "rawValue" to dateTime.rawValue, + ) + + private fun toBoundingBox(rect: android.graphics.Rect): BoundingBox = + BoundingBox( + x = rect.exactCenterX().toDouble(), + y = rect.exactCenterY().toDouble(), + centerX = rect.centerX().toDouble(), + centerY = rect.centerY().toDouble(), + width = rect.width().toDouble(), + height = rect.height().toDouble(), + top = rect.top.toDouble(), + left = rect.left.toDouble(), + bottom = rect.bottom.toDouble(), + right = rect.right.toDouble(), + ) + + private fun toCorner(point: android.graphics.Point): Corner = + Corner( + x = point.x.toDouble(), + y = point.y.toDouble(), + ) + + private fun formatName(format: Int): String = formatNameMap[format] ?: "UNKNOWN" + + private fun valueTypeName(valueType: Int): String = valueTypeNameMap[valueType] ?: "TYPE_UNKNOWN" + + private fun wifiEncryptionName(type: Int): String = + when (type) { + Barcode.WiFi.TYPE_OPEN -> "OPEN" + Barcode.WiFi.TYPE_WEP -> "WEP" + Barcode.WiFi.TYPE_WPA -> "WPA" + else -> "UNKNOWN" + } + + private fun phoneTypeName(type: Int): String = + when (type) { + Barcode.Phone.TYPE_FAX -> "FAX" + Barcode.Phone.TYPE_HOME -> "HOME" + Barcode.Phone.TYPE_MOBILE -> "MOBILE" + Barcode.Phone.TYPE_WORK -> "WORK" + else -> "UNKNOWN" + } + + private fun emailTypeName(type: Int): String = + when (type) { + Barcode.Email.TYPE_HOME -> "HOME" + Barcode.Email.TYPE_WORK -> "WORK" + else -> "UNKNOWN" + } + + private fun addressTypeName(type: Int): String = + when (type) { + Barcode.Address.TYPE_HOME -> "HOME" + Barcode.Address.TYPE_WORK -> "WORK" + else -> "UNKNOWN" + } +} diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt new file mode 100644 index 0000000..e172a34 --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt @@ -0,0 +1,59 @@ +package com.visioncameramlkit.infrastructure.mlkit.factories + +import com.google.mlkit.vision.barcode.BarcodeScanner +import com.google.mlkit.vision.barcode.BarcodeScannerOptions +import com.google.mlkit.vision.barcode.BarcodeScanning +import com.google.mlkit.vision.barcode.common.Barcode +import com.visioncameramlkit.domain.models.BarcodeFormatOption +import com.visioncameramlkit.domain.models.BarcodeScanningOptions +import com.visioncameramlkit.infrastructure.mlkit.MLKitBarcodeScanningService + +object BarcodeScanningServiceFactory { + private val barcodeFormatMap: Map = + mapOf( + BarcodeFormatOption.UNKNOWN to Barcode.FORMAT_UNKNOWN, + BarcodeFormatOption.ALL_FORMATS to Barcode.FORMAT_ALL_FORMATS, + BarcodeFormatOption.CODE_128 to Barcode.FORMAT_CODE_128, + BarcodeFormatOption.CODE_39 to Barcode.FORMAT_CODE_39, + BarcodeFormatOption.CODE_93 to Barcode.FORMAT_CODE_93, + BarcodeFormatOption.CODABAR to Barcode.FORMAT_CODABAR, + BarcodeFormatOption.DATA_MATRIX to Barcode.FORMAT_DATA_MATRIX, + BarcodeFormatOption.EAN_13 to Barcode.FORMAT_EAN_13, + BarcodeFormatOption.EAN_8 to Barcode.FORMAT_EAN_8, + BarcodeFormatOption.ITF to Barcode.FORMAT_ITF, + BarcodeFormatOption.QR_CODE to Barcode.FORMAT_QR_CODE, + BarcodeFormatOption.UPC_A to Barcode.FORMAT_UPC_A, + BarcodeFormatOption.UPC_E to Barcode.FORMAT_UPC_E, + BarcodeFormatOption.PDF417 to Barcode.FORMAT_PDF417, + BarcodeFormatOption.AZTEC to Barcode.FORMAT_AZTEC, + ) + + fun createBarcodeScanner(options: BarcodeScanningOptions = BarcodeScanningOptions()): BarcodeScanner { + val builder = BarcodeScannerOptions.Builder() + + val selectedFormats = + options.formats + .map(::toMlKitFormat) + .filter { it != Barcode.FORMAT_UNKNOWN && it != Barcode.FORMAT_ALL_FORMATS } + + if (selectedFormats.isNotEmpty()) { + val first = selectedFormats.first() + val rest = selectedFormats.drop(1).toIntArray() + @Suppress("SpreadOperator") + builder.setBarcodeFormats(first, *rest) + } + + if (options.enableAllPotentialBarcodes) { + builder.enableAllPotentialBarcodes() + } + + return BarcodeScanning.getClient(builder.build()) + } + + fun create(options: BarcodeScanningOptions = BarcodeScanningOptions()): MLKitBarcodeScanningService { + val scanner = createBarcodeScanner(options) + return MLKitBarcodeScanningService(scanner) + } + + private fun toMlKitFormat(format: BarcodeFormatOption): Int = barcodeFormatMap[format] ?: Barcode.FORMAT_UNKNOWN +} diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt index f07b26a..8222645 100644 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt @@ -10,6 +10,7 @@ import com.facebook.react.module.annotations.ReactModule import com.visioncameramlkit.BuildConfig import com.visioncameramlkit.bridge.constants.MLKitFeatureKeys import com.visioncameramlkit.bridge.handlers.IStaticImageHandler +import com.visioncameramlkit.bridge.handlers.StaticBarcodeScanningHandler import com.visioncameramlkit.bridge.handlers.StaticTextRecognitionHandler @ReactModule(name = VisionCameraMLKitModule.NAME) @@ -29,6 +30,12 @@ class VisionCameraMLKitModule( StaticTextRecognitionHandler(reactApplicationContext) } + @Suppress("KotlinConstantConditions") + if (BuildConfig.MLKIT_BARCODE_SCANNING) { + handlers[MLKitFeatureKeys.BARCODE_SCANNING] = + StaticBarcodeScanningHandler(reactApplicationContext) + } + handlers } diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/serializers/BarcodeScanningSerializer.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/serializers/BarcodeScanningSerializer.kt new file mode 100644 index 0000000..8ce3192 --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/serializers/BarcodeScanningSerializer.kt @@ -0,0 +1,190 @@ +package com.visioncameramlkit.infrastructure.serializers + +import com.facebook.react.bridge.WritableNativeArray +import com.facebook.react.bridge.WritableNativeMap +import com.visioncameramlkit.domain.models.BarcodeData +import com.visioncameramlkit.domain.models.BarcodeParsedValue +import com.visioncameramlkit.domain.models.BarcodeScanningResult +import com.visioncameramlkit.domain.models.BoundingBox +import com.visioncameramlkit.domain.models.Corner + +@Suppress("TooManyFunctions") +object BarcodeScanningSerializer { + fun toWritableMap(result: BarcodeScanningResult): WritableNativeMap = + WritableNativeMap().apply { + putArray("barcodes", toBarcodesArray(result.barcodes)) + } + + fun toReactNativeMap(result: BarcodeScanningResult): HashMap { + @Suppress("UNCHECKED_CAST") + return toWritableMap(result).toHashMap() as HashMap + } + + private fun toBarcodesArray(barcodes: List): WritableNativeArray = + WritableNativeArray().apply { + barcodes.forEach { barcode -> pushMap(toBarcodeMap(barcode)) } + } + + private fun toBarcodeMap(barcode: BarcodeData): WritableNativeMap = + WritableNativeMap().apply { + barcode.bounds?.let { putMap("bounds", toBoundsMap(it)) } + barcode.corners?.let { putArray("corners", toCornersArray(it)) } + putInt("format", barcode.format) + putString("formatName", barcode.formatName) + putInt("valueType", barcode.valueType) + putString("valueTypeName", barcode.valueTypeName) + putString("rawValue", barcode.rawValue) + putString("displayValue", barcode.displayValue) + putBoolean("isPotential", barcode.isPotential) + barcode.rawBytes?.let { putArray("rawBytes", toByteArray(it)) } + barcode.value?.let { putMap("value", toParsedValueMap(it)) } + } + + private fun toBoundsMap(bounds: BoundingBox): WritableNativeMap = + WritableNativeMap().apply { + putDouble("x", bounds.x) + putDouble("y", bounds.y) + putDouble("centerX", bounds.centerX) + putDouble("centerY", bounds.centerY) + putDouble("width", bounds.width) + putDouble("height", bounds.height) + putDouble("top", bounds.top) + putDouble("left", bounds.left) + putDouble("bottom", bounds.bottom) + putDouble("right", bounds.right) + } + + private fun toCornersArray(corners: List): WritableNativeArray = + WritableNativeArray().apply { + corners.forEach { corner -> + pushMap( + WritableNativeMap().apply { + putDouble("x", corner.x) + putDouble("y", corner.y) + }, + ) + } + } + + private fun toByteArray(bytes: List): WritableNativeArray = + WritableNativeArray().apply { + bytes.forEach { byteValue -> pushInt(byteValue) } + } + + private fun toParsedValueMap(value: BarcodeParsedValue): WritableNativeMap = + WritableNativeMap().apply { + putString("type", value.type) + putMap("data", toDynamicMap(value.data)) + } + + private fun toDynamicMap(map: Map): WritableNativeMap = + WritableNativeMap().apply { + map.forEach { (key, value) -> + putDynamic(this, key, value) + } + } + + private fun toDynamicArray(list: List): WritableNativeArray = + WritableNativeArray().apply { + list.forEach { value -> pushDynamic(this, value) } + } + + private fun putDynamic( + target: WritableNativeMap, + key: String, + value: Any?, + ) { + when (value) { + null -> { + target.putNull(key) + } + + is String -> { + target.putString(key, value) + } + + is Boolean -> { + target.putBoolean(key, value) + } + + is Int -> { + target.putInt(key, value) + } + + is Long -> { + target.putDouble(key, value.toDouble()) + } + + is Float -> { + target.putDouble(key, value.toDouble()) + } + + is Double -> { + target.putDouble(key, value) + } + + is Map<*, *> -> { + @Suppress("UNCHECKED_CAST") + target.putMap(key, toDynamicMap(value as Map)) + } + + is List<*> -> { + @Suppress("UNCHECKED_CAST") + target.putArray(key, toDynamicArray(value)) + } + + else -> { + target.putString(key, value.toString()) + } + } + } + + private fun pushDynamic( + target: WritableNativeArray, + value: Any?, + ) { + when (value) { + null -> { + target.pushNull() + } + + is String -> { + target.pushString(value) + } + + is Boolean -> { + target.pushBoolean(value) + } + + is Int -> { + target.pushInt(value) + } + + is Long -> { + target.pushDouble(value.toDouble()) + } + + is Float -> { + target.pushDouble(value.toDouble()) + } + + is Double -> { + target.pushDouble(value) + } + + is Map<*, *> -> { + @Suppress("UNCHECKED_CAST") + target.pushMap(toDynamicMap(value as Map)) + } + + is List<*> -> { + @Suppress("UNCHECKED_CAST") + target.pushArray(toDynamicArray(value)) + } + + else -> { + target.pushString(value.toString()) + } + } + } +} diff --git a/docs/barcode-scanning.md b/docs/barcode-scanning.md new file mode 100644 index 0000000..27ddaae --- /dev/null +++ b/docs/barcode-scanning.md @@ -0,0 +1,130 @@ +# Barcode Scanning API + +This page documents the Barcode Scanning API in more detail than the README quick-start. + +## Imports + +```ts +import { + useBarcodeScanning, + processImageBarcodeScanning, + type BarcodeScanningOptions, + type BarcodeScanningArguments, + type BarcodeScanningImageOptions, + type BarcodeScanningResult, +} from 'react-native-vision-camera-mlkit'; +``` + +## Frame Processor + +Use `useBarcodeScanning()` for live camera frames. + +```ts +const { barcodeScanning } = useBarcodeScanning({ + formats: ['QR_CODE', 'CODE_128'], + enableAllPotentialBarcodes: true, + scaleFactor: 1, + invertColors: false, +}); + +const result = barcodeScanning(frame, { + outputOrientation: 'portrait', +}); +``` + +### Frame options + +- `formats?: BarcodeFormat[]` +- `enableAllPotentialBarcodes?: boolean` (Android only) +- `scaleFactor?: number` (`0.9`-`1.0`) +- `invertColors?: boolean` +- `frameProcessInterval?: number` (deprecated; prefer `runAtTargetFps`) + +### Frame arguments + +- `outputOrientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` (iOS only) + +## Static Image Processing + +Use `processImageBarcodeScanning(uri, options)` for gallery files / local images. + +```ts +const result = await processImageBarcodeScanning(imageUri, { + formats: ['QR_CODE', 'PDF417'], + enableAllPotentialBarcodes: true, + orientation: 'portrait', + invertColors: false, + scaleFactor: 1, +}); +``` + +### Image options + +- `formats?: BarcodeFormat[]` +- `enableAllPotentialBarcodes?: boolean` (Android only) +- `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` +- `invertColors?: boolean` +- `scaleFactor?: number` (`0.9`-`1.0`) + +## Supported formats + +- `ALL_FORMATS` +- `QR_CODE` +- `AZTEC` +- `PDF417` +- `DATA_MATRIX` +- `CODE_128` +- `CODE_39` +- `CODE_93` +- `CODABAR` +- `EAN_13` +- `EAN_8` +- `ITF` +- `UPC_A` +- `UPC_E` +- `UNKNOWN` + +## Result shape + +`BarcodeScanningResult` contains `barcodes[]`. + +Each barcode includes: + +- geometry: `bounds`, `corners` +- identity: `format`, `formatName`, `valueType`, `valueTypeName` +- raw payload: `rawValue`, `displayValue`, `rawBytes` +- `isPotential` (true for potential/undecoded barcode candidates) +- parsed payload: `value?: BarcodeParsedValue` + +### Parsed value typing + +`BarcodeParsedValue` is a discriminated union with `type` as `TYPE_*` values. + +Examples: + +- `TYPE_WIFI` +- `TYPE_URL` +- `TYPE_CONTACT_INFO` +- `TYPE_CALENDAR_EVENT` +- `TYPE_DRIVER_LICENSE` +- `TYPE_TEXT` +- `TYPE_PRODUCT` +- `TYPE_ISBN` + +Use `switch (barcode.value?.type)` for strong type inference in TypeScript. + +## Errors + +For static image processing, promise rejections can use: + +- `IMAGE_NOT_FOUND_ERROR` +- `INVALID_URI_ERROR` +- `IMAGE_PROCESSING_FAILED_ERROR` +- `UNSUPPORTED_IMAGE_FORMAT_ERROR` + +## Performance guidance + +- Filter `formats` whenever possible for better performance. +- Use `runAsync(frame, ...)` to avoid blocking frame processing. +- Use `runAtTargetFps(...)` for throttling. +- Keep `scaleFactor` as high as possible for decoding reliability. diff --git a/docs/text-recognition.md b/docs/text-recognition.md new file mode 100644 index 0000000..f8c582e --- /dev/null +++ b/docs/text-recognition.md @@ -0,0 +1,92 @@ +# Text Recognition API + +This page documents the Text Recognition API in more detail than the README quick-start. + +## Imports + +```ts +import { + useTextRecognition, + processImageTextRecognition, + type TextRecognitionOptions, + type TextRecognitionArguments, + type TextRecognitionImageOptions, +} from 'react-native-vision-camera-mlkit'; +``` + +## Frame Processor + +Use `useTextRecognition()` for live camera frames. + +```ts +const { textRecognition } = useTextRecognition({ + language: 'LATIN', + scaleFactor: 1, + invertColors: false, +}); + +const result = textRecognition(frame, { + outputOrientation: 'portrait', +}); +``` + +### Frame options + +- `language?: 'LATIN' | 'CHINESE' | 'DEVANAGARI' | 'JAPANESE' | 'KOREAN'` +- `scaleFactor?: number` (`0.9`-`1.0`) +- `invertColors?: boolean` +- `frameProcessInterval?: number` (deprecated; prefer `runAtTargetFps`) + +### Frame arguments + +- `outputOrientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` (iOS only) + +## Static Image Processing + +Use `processImageTextRecognition(uri, options)` for gallery files / local images. + +```ts +const result = await processImageTextRecognition(imageUri, { + language: 'LATIN', + orientation: 'portrait', + invertColors: false, + scaleFactor: 1, +}); +``` + +### Image options + +- `language?: 'LATIN' | 'CHINESE' | 'DEVANAGARI' | 'JAPANESE' | 'KOREAN'` +- `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` +- `invertColors?: boolean` +- `scaleFactor?: number` (`0.9`-`1.0`) + +## Result shape + +Text recognition returns: + +- top-level text content +- blocks +- lines +- elements +- geometry (`bounds`, `corners`) with transformed coordinates + +## iOS orientation note + +For frame processing on iOS, pass `outputOrientation` to ensure ML Kit receives the correct image orientation. +Android rotation is handled automatically. + +## Errors + +For static image processing, promise rejections can use: + +- `IMAGE_NOT_FOUND_ERROR` +- `INVALID_URI_ERROR` +- `IMAGE_PROCESSING_FAILED_ERROR` +- `UNSUPPORTED_IMAGE_FORMAT_ERROR` + +## Performance guidance + +- Use `runAsync(frame, ...)` to avoid blocking frame processing. +- Use `runAtTargetFps(...)` for throttling. +- Keep `scaleFactor` as high as possible for OCR accuracy. diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 85ff1c8..faba3f9 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -2943,7 +2943,7 @@ PODS: - VisionCamera/React (4.7.3): - React-Core - VisionCamera/FrameProcessors - - VisionCameraMLKit (1.0.0): + - VisionCameraMLKit (1.0.1): - boost - DoubleConversion - fast_float @@ -3363,7 +3363,7 @@ SPEC CHECKSUMS: RNWorklets: ab7740c2a152f77ff76a40d52be860d8f128fab1 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 VisionCamera: 47f10351a1b6a8d251b90986ae72ca5f52331954 - VisionCameraMLKit: 5d0ba97a6fd96b6a497d91cdb0a62190e0a2ba71 + VisionCameraMLKit: 771681486e3ec5b6590e28170f9ed74248b8912c Yoga: fa23995c18b65978347b096d0836f4f5093df545 PODFILE CHECKSUM: 1e33379caa52806ba900154e8eb803a55bbce31c diff --git a/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj b/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj index 8a05b0c..3712a26 100644 --- a/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj +++ b/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj @@ -9,22 +9,22 @@ /* Begin PBXBuildFile section */ 057CEFDD928055D235F3DB7A /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 390372C79ECA56845CF0980F /* libPods-VisionCameraMLKitExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A05D4578D644FAE34FCA956C /* libPods-VisionCameraMLKitExample.a */; }; 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + F12AE336641C07D5C2078619 /* libPods-VisionCameraMLKitExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F3C7C2173AAF51CC7ECBF1AB /* libPods-VisionCameraMLKitExample.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 0BC6BB36A04F6716F2FD86A6 /* Pods-VisionCameraMLKitExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.release.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.release.xcconfig"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* VisionCameraMLKitExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VisionCameraMLKitExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = VisionCameraMLKitExample/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = VisionCameraMLKitExample/Info.plist; sourceTree = ""; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = VisionCameraMLKitExample/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 703F5B7AAE7B2A7EB18ADAF9 /* Pods-VisionCameraMLKitExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.release.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.release.xcconfig"; sourceTree = ""; }; 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = VisionCameraMLKitExample/AppDelegate.swift; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = VisionCameraMLKitExample/LaunchScreen.storyboard; sourceTree = ""; }; - A05D4578D644FAE34FCA956C /* libPods-VisionCameraMLKitExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VisionCameraMLKitExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + B92D03227A73B651DBBE5627 /* Pods-VisionCameraMLKitExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.debug.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.debug.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; - EF06C59FC6233A25C9565962 /* Pods-VisionCameraMLKitExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.debug.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.debug.xcconfig"; sourceTree = ""; }; + F3C7C2173AAF51CC7ECBF1AB /* libPods-VisionCameraMLKitExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VisionCameraMLKitExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -32,7 +32,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 390372C79ECA56845CF0980F /* libPods-VisionCameraMLKitExample.a in Frameworks */, + F12AE336641C07D5C2078619 /* libPods-VisionCameraMLKitExample.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -55,7 +55,7 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - A05D4578D644FAE34FCA956C /* libPods-VisionCameraMLKitExample.a */, + F3C7C2173AAF51CC7ECBF1AB /* libPods-VisionCameraMLKitExample.a */, ); name = Frameworks; sourceTree = ""; @@ -92,8 +92,8 @@ BBD78D7AC51CEA395F1C20DB /* Pods */ = { isa = PBXGroup; children = ( - EF06C59FC6233A25C9565962 /* Pods-VisionCameraMLKitExample.debug.xcconfig */, - 0BC6BB36A04F6716F2FD86A6 /* Pods-VisionCameraMLKitExample.release.xcconfig */, + B92D03227A73B651DBBE5627 /* Pods-VisionCameraMLKitExample.debug.xcconfig */, + 703F5B7AAE7B2A7EB18ADAF9 /* Pods-VisionCameraMLKitExample.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -105,13 +105,13 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "VisionCameraMLKitExample" */; buildPhases = ( - 426E5821A05DA85CA40980A2 /* [CP] Check Pods Manifest.lock */, + A58C7600C49A08D8191F7748 /* [CP] Check Pods Manifest.lock */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - E734EAFC8B4C50F49B773837 /* [CP] Embed Pods Frameworks */, - 8F49C75216F54AC817EEDA3D /* [CP] Copy Pods Resources */, + 91792E50D381D930AC7AC001 /* [CP] Embed Pods Frameworks */, + CDBBCB7CB689AD23EC870207 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -184,7 +184,24 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; - 426E5821A05DA85CA40980A2 /* [CP] Check Pods Manifest.lock */ = { + 91792E50D381D930AC7AC001 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + A58C7600C49A08D8191F7748 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -206,7 +223,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 8F49C75216F54AC817EEDA3D /* [CP] Copy Pods Resources */ = { + CDBBCB7CB689AD23EC870207 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -223,23 +240,6 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources.sh\"\n"; showEnvVarsInLog = 0; }; - E734EAFC8B4C50F49B773837 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -256,7 +256,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = EF06C59FC6233A25C9565962 /* Pods-VisionCameraMLKitExample.debug.xcconfig */; + baseConfigurationReference = B92D03227A73B651DBBE5627 /* Pods-VisionCameraMLKitExample.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -289,7 +289,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0BC6BB36A04F6716F2FD86A6 /* Pods-VisionCameraMLKitExample.release.xcconfig */; + baseConfigurationReference = 703F5B7AAE7B2A7EB18ADAF9 /* Pods-VisionCameraMLKitExample.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/example/src/components/views/CameraView.tsx b/example/src/components/views/CameraView.tsx index 388dbb4..38f28fe 100644 --- a/example/src/components/views/CameraView.tsx +++ b/example/src/components/views/CameraView.tsx @@ -16,6 +16,7 @@ import { useFrameProcessor, } from 'react-native-vision-camera'; import { + useBarcodeScanning, useTextRecognition, type Orientation, } from 'react-native-vision-camera-mlkit'; @@ -84,6 +85,13 @@ const CameraView = forwardRef( ...sharedOptions, }); + const barcodeScanningPlugin = useBarcodeScanning({ + formats: pluginOptions[PLUGIN_ID.BARCODE_SCANNING].formats, + enableAllPotentialBarcodes: + pluginOptions[PLUGIN_ID.BARCODE_SCANNING].enableAllPotentialBarcodes, + ...sharedOptions, + }); + const [focusPoint, setFocusPoint] = useState<{ x: number; y: number }>(); const scale = useSharedValue(0); @@ -165,6 +173,11 @@ const CameraView = forwardRef( frame, withArguments ); + } else if (pluginId === PLUGIN_ID.BARCODE_SCANNING) { + resultObject = barcodeScanningPlugin.barcodeScanning( + frame, + withArguments + ); } if (resultObject) { @@ -178,6 +191,7 @@ const CameraView = forwardRef( pluginId, frameOutputOrientation, textRecognitionPlugin, + barcodeScanningPlugin, handleResultWorklet, ] ); diff --git a/example/src/screens/HomeScreen.tsx b/example/src/screens/HomeScreen.tsx index 2babf02..1c13927 100644 --- a/example/src/screens/HomeScreen.tsx +++ b/example/src/screens/HomeScreen.tsx @@ -91,7 +91,7 @@ const BASE_DATA: Array> = [ isBeta: false, android: true, ios: true, - status: 'in-progress', + status: 'complete', }, { id: PLUGIN_ID.IMAGE_LABELING, diff --git a/example/src/screens/ImageScreen.tsx b/example/src/screens/ImageScreen.tsx index 560f613..a1ba3fe 100644 --- a/example/src/screens/ImageScreen.tsx +++ b/example/src/screens/ImageScreen.tsx @@ -8,7 +8,10 @@ import { import { useState } from 'react'; import { Image, ScrollView, StyleSheet, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { processImageTextRecognition } from 'react-native-vision-camera-mlkit'; +import { + processImageBarcodeScanning, + processImageTextRecognition, +} from 'react-native-vision-camera-mlkit'; import { Button, Divider, @@ -29,6 +32,7 @@ import { useImageProcessingOptionsStore, useTerminalStore } from '../stores'; const IMAGE_PROCESSOR_MAP = { [PLUGIN_ID.TEXT_RECOGNITION]: processImageTextRecognition, + [PLUGIN_ID.BARCODE_SCANNING]: processImageBarcodeScanning, } as const; const ImageScreen = () => { @@ -208,6 +212,59 @@ const ImageScreen = () => { } /> )} + {pluginId === PLUGIN_ID.BARCODE_SCANNING && ( + <> + + setImageProcessingOption( + PLUGIN_ID.BARCODE_SCANNING, + 'enableAllPotentialBarcodes', + value + ) + } + /> + + setImageProcessingOption( + PLUGIN_ID.BARCODE_SCANNING, + 'formats', + value === 'ALL_FORMATS' ? [] : [value as any] + ) + } + /> + + )} { } /> )} + {pluginId === PLUGIN_ID.BARCODE_SCANNING && ( + <> + + setPluginOption( + PLUGIN_ID.BARCODE_SCANNING, + 'enableAllPotentialBarcodes', + value + ) + } + /> + + setPluginOption( + PLUGIN_ID.BARCODE_SCANNING, + 'formats', + value === 'ALL_FORMATS' ? [] : [value as any] + ) + } + /> + + )}
diff --git a/example/src/stores/imageProcessingOptionsStore.ts b/example/src/stores/imageProcessingOptionsStore.ts index 5ec5439..7150ac2 100644 --- a/example/src/stores/imageProcessingOptionsStore.ts +++ b/example/src/stores/imageProcessingOptionsStore.ts @@ -1,4 +1,5 @@ import type { + BarcodeScanningImageOptions, ImageProcessingBaseOptions as SharedImageProcessingOptions, TextRecognitionImageOptions, } from 'react-native-vision-camera-mlkit'; @@ -7,6 +8,10 @@ import { PLUGIN_ID } from '../constants/PLUGINS'; type SpecificImageProcessingOptions = { [PLUGIN_ID.TEXT_RECOGNITION]: Pick; + [PLUGIN_ID.BARCODE_SCANNING]: Pick< + BarcodeScanningImageOptions, + 'formats' | 'enableAllPotentialBarcodes' + >; }; type ImageProcessingOptionsState = { @@ -36,6 +41,10 @@ export const useImageProcessingOptionsStore = [PLUGIN_ID.TEXT_RECOGNITION]: { language: 'LATIN', }, + [PLUGIN_ID.BARCODE_SCANNING]: { + formats: [], + enableAllPotentialBarcodes: false, + }, }, setSharedOption: (key, value) => set((state) => ({ diff --git a/example/src/stores/pluginOptionsStore.ts b/example/src/stores/pluginOptionsStore.ts index 17e898c..ab5c4e9 100644 --- a/example/src/stores/pluginOptionsStore.ts +++ b/example/src/stores/pluginOptionsStore.ts @@ -1,4 +1,5 @@ import type { + BarcodeScanningOptions, MLKitBaseOptions as SharedPluginOptions, TextRecognitionOptions, } from 'react-native-vision-camera-mlkit'; @@ -7,6 +8,10 @@ import { PLUGIN_ID } from '../constants/PLUGINS'; type SpecificPluginOptions = { [PLUGIN_ID.TEXT_RECOGNITION]: Pick; + [PLUGIN_ID.BARCODE_SCANNING]: Pick< + BarcodeScanningOptions, + 'formats' | 'enableAllPotentialBarcodes' + >; }; type PluginOptionsState = { @@ -36,6 +41,10 @@ export const usePluginOptionsStore = create((set) => ({ [PLUGIN_ID.TEXT_RECOGNITION]: { language: 'LATIN', }, + [PLUGIN_ID.BARCODE_SCANNING]: { + formats: [], + enableAllPotentialBarcodes: false, + }, }, setSharedOption: (key, value) => set((state) => ({ diff --git a/ios/Application/UseCases/RecognizeBarcodesUseCase.swift b/ios/Application/UseCases/RecognizeBarcodesUseCase.swift new file mode 100644 index 0000000..98d1bf4 --- /dev/null +++ b/ios/Application/UseCases/RecognizeBarcodesUseCase.swift @@ -0,0 +1,77 @@ +import Foundation + +#if MLKIT_BARCODE_SCANNING + #if canImport(VisionCamera) + import VisionCamera + + class RecognizeBarcodesUseCase { + private let imagePreprocessor: IImagePreprocessor + private let recognitionService: MLKitBarcodeScanningService + + init( + imagePreprocessor: IImagePreprocessor, + recognitionService: MLKitBarcodeScanningService + ) { + self.imagePreprocessor = imagePreprocessor + self.recognitionService = recognitionService + } + + func execute(frame: Frame, options: BarcodeScanningOptions) throws + -> BarcodeScanningResult + { + return try autoreleasepool { + let preprocessingOptions = ImagePreprocessingOptions( + invertColors: options.invertColors, + outputOrientation: options.outputOrientation, + scaleFactor: options.scaleFactor + ) + + guard + let processedImage = imagePreprocessor.preprocessFrame( + frame: frame, + options: preprocessingOptions + ) + else { + throw NSError( + domain: "RecognizeBarcodesUseCase", + code: -1, + userInfo: [NSLocalizedDescriptionKey: "Failed to preprocess image"] + ) + } + + return try recognitionService.recognize(image: processedImage) + } + } + + func execute( + imageFile: URL, + imageOptions: ImagePreprocessingOptions, + barcodeOptions: BarcodeScanningOptions + ) throws -> BarcodeScanningResult { + return try autoreleasepool { + let preprocessingOptions = ImagePreprocessingOptions( + invertColors: imageOptions.invertColors, + scaleFactor: imageOptions.scaleFactor, + orientation: imageOptions.orientation + ) + + guard + let processedImage = imagePreprocessor.preprocessImage( + imageFile: imageFile, + options: preprocessingOptions + ) + else { + throw NSError( + domain: "RecognizeBarcodesUseCase", + code: -1, + userInfo: [NSLocalizedDescriptionKey: "Failed to preprocess image"] + ) + } + + _ = barcodeOptions + return try recognitionService.recognize(image: processedImage) + } + } + } + #endif +#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift b/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift new file mode 100644 index 0000000..86a19e1 --- /dev/null +++ b/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift @@ -0,0 +1,126 @@ +import Foundation + +#if MLKIT_BARCODE_SCANNING + class StaticBarcodeScanningHandler: IStaticImageHandler { + private var cachedUseCases: [String: RecognizeBarcodesUseCase] = [:] + + private func getRecognizeBarcodesUseCase(for options: BarcodeScanningOptions) + -> RecognizeBarcodesUseCase + { + let key = + "\(options.enableAllPotentialBarcodes)|\(options.formats.map(\.rawValue).joined(separator: ","))" + + if let useCase = cachedUseCases[key] { + return useCase + } + + let newUseCase = RecognizeBarcodesUseCase( + imagePreprocessor: ImagePreprocessor(), + recognitionService: BarcodeScanningServiceFactory.create(options: options) + ) + cachedUseCases[key] = newUseCase + return newUseCase + } + + func process( + path: String, + options: [String: Any], + resolver: @escaping PromiseResolver, + rejecter: @escaping PromiseRejecter + ) { + Task { + do { + guard let fileURL = resolveFileURL(from: path) else { + rejecter( + "IMAGE_NOT_FOUND_ERROR", + "Unsupported image URI: \(path)", + nil + ) + return + } + + if fileURL.isFileURL + && !FileManager.default.fileExists(atPath: fileURL.path) + { + rejecter( + "IMAGE_NOT_FOUND_ERROR", + "Image file not found at path: \(path)", + nil + ) + return + } + + let imageOptions = parseImageOptions(options) + let barcodeOptions = parseBarcodeOptions(options) + let recognizeBarcodesUseCase = getRecognizeBarcodesUseCase(for: barcodeOptions) + + let result = try recognizeBarcodesUseCase.execute( + imageFile: fileURL, + imageOptions: imageOptions, + barcodeOptions: barcodeOptions + ) + + resolver(BarcodeScanningSerializer.toReactNativeMap(result)) + } catch { + switch error { + case let nsError as NSError: + if nsError.domain == NSCocoaErrorDomain + && nsError.code == NSFileReadNoSuchFileError + { + rejecter("IMAGE_NOT_FOUND_ERROR", "Image file not found", error) + } else { + rejecter( + "IMAGE_PROCESSING_FAILED_ERROR", + error.localizedDescription, + error + ) + } + default: + rejecter( + "IMAGE_PROCESSING_FAILED_ERROR", + error.localizedDescription, + error + ) + } + } + } + } + + private func parseImageOptions(_ options: [String: Any]) + -> ImagePreprocessingOptions + { + let invertColors = options["invertColors"] as? Bool ?? false + let orientation = (options["orientation"] as? String).flatMap { Orientation(string: $0) } + let scaleFactor = CGFloat((options["scaleFactor"] as? NSNumber)?.doubleValue ?? 1.0) + + return ImagePreprocessingOptions( + invertColors: invertColors, + scaleFactor: scaleFactor, + orientation: orientation + ) + } + + private func parseBarcodeOptions(_ options: [String: Any]) + -> BarcodeScanningOptions + { + return BarcodeScanningOptions( + formats: BarcodeScanningOptionParser.parseFormats(options["formats"]), + enableAllPotentialBarcodes: + BarcodeScanningOptionParser + .parseEnableAllPotentialBarcodes(options["enableAllPotentialBarcodes"]) + ) + } + + private func resolveFileURL(from path: String) -> URL? { + if path.hasPrefix("file://") { + return URL(string: path) + } + + if let url = URL(string: path), url.scheme != nil { + return url + } + + return URL(fileURLWithPath: path) + } + } +#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Bridge/Parsers/BarcodeScanningOptionParser.swift b/ios/Bridge/Parsers/BarcodeScanningOptionParser.swift new file mode 100644 index 0000000..85a17aa --- /dev/null +++ b/ios/Bridge/Parsers/BarcodeScanningOptionParser.swift @@ -0,0 +1,21 @@ +import Foundation + +#if MLKIT_BARCODE_SCANNING + enum BarcodeScanningOptionParser { + static func parseFormats(_ value: Any?) -> [BarcodeFormatOption] { + guard let items = value as? [Any] else { + return [] + } + return + items + .compactMap { $0 as? String } + .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } + .compactMap { BarcodeFormatOption(rawValue: $0) } + } + + static func parseEnableAllPotentialBarcodes(_ value: Any?) -> Bool { + return value as? Bool ?? false + } + + } +#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Bridge/Plugins/BarcodeScanningPlugin.swift b/ios/Bridge/Plugins/BarcodeScanningPlugin.swift new file mode 100644 index 0000000..52b14de --- /dev/null +++ b/ios/Bridge/Plugins/BarcodeScanningPlugin.swift @@ -0,0 +1,50 @@ +import Foundation + +#if MLKIT_BARCODE_SCANNING + #if canImport(VisionCamera) + import VisionCamera + + @objc(BarcodeScanningPlugin) + public class BarcodeScanningPlugin: BaseMLKitPlugin { + + private lazy var recognitionOptions: BarcodeScanningOptions = { + BarcodeScanningOptions( + formats: BarcodeScanningOptionParser.parseFormats(self.options?["formats"]), + enableAllPotentialBarcodes: BarcodeScanningOptionParser.parseEnableAllPotentialBarcodes( + self.options?["enableAllPotentialBarcodes"] + ), + invertColors: self.invertColors, + scaleFactor: self.scaleFactor + ) + }() + + private lazy var recognizeBarcodesUseCase: RecognizeBarcodesUseCase = { + let recognitionService = BarcodeScanningServiceFactory.create( + options: self.recognitionOptions) + let imagePreprocessor = ImagePreprocessor() + + return RecognizeBarcodesUseCase( + imagePreprocessor: imagePreprocessor, + recognitionService: recognitionService + ) + }() + + public override func processFrame( + _ frame: Frame, + withArguments arguments: [AnyHashable: Any]? + ) throws -> [String: Any] { + let options = BarcodeScanningOptions( + formats: recognitionOptions.formats, + enableAllPotentialBarcodes: recognitionOptions.enableAllPotentialBarcodes, + invertColors: recognitionOptions.invertColors, + outputOrientation: outputOrientation, + scaleFactor: recognitionOptions.scaleFactor + ) + + _ = arguments + let result = try recognizeBarcodesUseCase.execute(frame: frame, options: options) + return BarcodeScanningSerializer.toReactNativeMap(result) + } + } + #endif +#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Domain/Models/BarcodeScanningOptions.swift b/ios/Domain/Models/BarcodeScanningOptions.swift new file mode 100644 index 0000000..cd49f4d --- /dev/null +++ b/ios/Domain/Models/BarcodeScanningOptions.swift @@ -0,0 +1,43 @@ +import Foundation + +#if MLKIT_BARCODE_SCANNING + struct BarcodeScanningOptions { + let formats: [BarcodeFormatOption] + let enableAllPotentialBarcodes: Bool + let invertColors: Bool + let outputOrientation: OutputOrientation? + let scaleFactor: CGFloat + + init( + formats: [BarcodeFormatOption] = [], + enableAllPotentialBarcodes: Bool = false, + invertColors: Bool = false, + outputOrientation: OutputOrientation? = nil, + scaleFactor: CGFloat = 1.0 + ) { + self.formats = formats + self.enableAllPotentialBarcodes = enableAllPotentialBarcodes + self.invertColors = invertColors + self.outputOrientation = outputOrientation + self.scaleFactor = scaleFactor + } + } + + enum BarcodeFormatOption: String, CaseIterable { + case formatUnknown = "UNKNOWN" + case formatAllFormats = "ALL_FORMATS" + case formatCode128 = "CODE_128" + case formatCode39 = "CODE_39" + case formatCode93 = "CODE_93" + case formatCodabar = "CODABAR" + case formatDataMatrix = "DATA_MATRIX" + case formatEan13 = "EAN_13" + case formatEan8 = "EAN_8" + case formatItf = "ITF" + case formatQrCode = "QR_CODE" + case formatUpcA = "UPC_A" + case formatUpcE = "UPC_E" + case formatPdf417 = "PDF417" + case formatAztec = "AZTEC" + } +#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Domain/Models/BarcodeScanningResult.swift b/ios/Domain/Models/BarcodeScanningResult.swift new file mode 100644 index 0000000..270bda9 --- /dev/null +++ b/ios/Domain/Models/BarcodeScanningResult.swift @@ -0,0 +1,26 @@ +import Foundation + +#if MLKIT_BARCODE_SCANNING + struct BarcodeScanningResult { + let barcodes: [BarcodeData] + } + + struct BarcodeData { + let bounds: BoundingBox? + let corners: [Corner]? + let format: Int + let formatName: String + let valueType: Int + let valueTypeName: String + let rawValue: String? + let displayValue: String? + let rawBytes: [Int]? + let isPotential: Bool + let value: BarcodeParsedValue? + } + + struct BarcodeParsedValue { + let type: String + let data: [String: Any?] + } +#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Infrastructure/Image/ImagePreprocessor.swift b/ios/Infrastructure/Image/ImagePreprocessor.swift index 55230cf..89d6dfb 100644 --- a/ios/Infrastructure/Image/ImagePreprocessor.swift +++ b/ios/Infrastructure/Image/ImagePreprocessor.swift @@ -250,25 +250,34 @@ import Foundation // EXIF orientation into uiImage.imageOrientation. let effectiveOrientation = options.orientation ?? Orientation.fromUIImageOrientation(uiImage.imageOrientation) + let effectiveScale = clampScale(options.scaleFactor) + + var processedImage = uiImage - // Rotate image based on orientation - let rotatedImage = rotateUIImage(uiImage, orientation: effectiveOrientation) + // Apply optional downscale for static images. + if effectiveScale < 1.0 { + processedImage = scaleUIImage(processedImage, scaleFactor: effectiveScale) + } // Apply color inversion if needed - let processedImage: UIImage if options.invertColors { - processedImage = invertUIImageColors(rotatedImage) - } else { - processedImage = rotatedImage + processedImage = invertUIImageColors(processedImage) } // Create VisionImage let visionImage = VisionImage(image: processedImage) visionImage.orientation = effectiveOrientation.asUIImageOrientation + let pixelWidth = + processedImage.cgImage?.width + ?? Int(processedImage.size.width * processedImage.scale) + let pixelHeight = + processedImage.cgImage?.height + ?? Int(processedImage.size.height * processedImage.scale) + let metadata = ImageMetadata( - width: Int(processedImage.size.width), - height: Int(processedImage.size.height), + width: pixelWidth, + height: pixelHeight, orientation: effectiveOrientation.asUIImageOrientation, isInverted: options.invertColors ) @@ -276,46 +285,19 @@ import Foundation return ProcessedImage(image: visionImage, metadata: metadata) } - private func rotateUIImage(_ image: UIImage, orientation: Orientation) - -> UIImage - { - let degrees: CGFloat - switch orientation { - case .portrait: - degrees = 0 - case .portraitUpsideDown: - degrees = 180 - case .landscapeLeft: - degrees = 90 - case .landscapeRight: - degrees = 270 - } - - let radians = degrees * .pi / 180 - let rotatedSize = CGRect(origin: .zero, size: image.size) - .applying(CGAffineTransform(rotationAngle: radians)) - .integral - - UIGraphicsBeginImageContext(rotatedSize.size) - defer { UIGraphicsEndImageContext() } - - guard let context = UIGraphicsGetCurrentContext() else { return image } - - context.translateBy( - x: rotatedSize.size.width / 2, - y: rotatedSize.size.height / 2 - ) - context.rotate(by: radians) - image.draw( - in: CGRect( - x: -image.size.width / 2, - y: -image.size.height / 2, - width: image.size.width, - height: image.size.height - ) + private func scaleUIImage(_ image: UIImage, scaleFactor: CGFloat) -> UIImage { + let targetSize = CGSize( + width: image.size.width * scaleFactor, + height: image.size.height * scaleFactor ) - return UIGraphicsGetImageFromCurrentImageContext() ?? image + let rendererFormat = UIGraphicsImageRendererFormat.default() + rendererFormat.scale = image.scale + + let renderer = UIGraphicsImageRenderer(size: targetSize, format: rendererFormat) + return renderer.image { _ in + image.draw(in: CGRect(origin: .zero, size: targetSize)) + } } private func invertUIImageColors(_ image: UIImage) -> UIImage { diff --git a/ios/Infrastructure/MLKit/Adapters/MLKitBarcodeAdapter.swift b/ios/Infrastructure/MLKit/Adapters/MLKitBarcodeAdapter.swift new file mode 100644 index 0000000..960a6dd --- /dev/null +++ b/ios/Infrastructure/MLKit/Adapters/MLKitBarcodeAdapter.swift @@ -0,0 +1,274 @@ +import Foundation + +#if MLKIT_BARCODE_SCANNING + import MLKitBarcodeScanning + + class MLKitBarcodeAdapter { + + static func toDomain(_ barcodes: [Barcode]) -> BarcodeScanningResult { + return BarcodeScanningResult(barcodes: barcodes.map { toBarcodeData($0) }) + } + + private static func toBarcodeData(_ barcode: Barcode) -> BarcodeData { + return BarcodeData( + bounds: toBoundingBox(barcode.frame), + corners: barcode.cornerPoints?.map { toCorner($0.cgPointValue) }, + format: Int(barcode.format.rawValue), + formatName: formatName(barcode.format), + valueType: Int(barcode.valueType.rawValue), + valueTypeName: valueTypeName(barcode.valueType), + rawValue: barcode.rawValue, + displayValue: barcode.displayValue, + rawBytes: barcode.rawData.map { Array($0).map { Int($0) } }, + isPotential: barcode.rawValue == nil && barcode.rawData == nil, + value: toParsedValue(barcode) + ) + } + + private static func toParsedValue(_ barcode: Barcode) -> BarcodeParsedValue? { + if let wifi = barcode.wifi { + return BarcodeParsedValue( + type: "TYPE_WIFI", + data: [ + "ssid": wifi.ssid, + "password": wifi.password, + "type": String(describing: wifi.type), + "typeName": String(describing: wifi.type), + ] + ) + } + + if let url = barcode.url { + return BarcodeParsedValue( + type: "TYPE_URL", + data: [ + "title": url.title, + "url": url.url, + ] + ) + } + + if let sms = barcode.sms { + return BarcodeParsedValue( + type: "TYPE_SMS", + data: [ + "message": sms.message, + "phoneNumber": sms.phoneNumber, + ] + ) + } + + if let email = barcode.email { + return BarcodeParsedValue( + type: "TYPE_EMAIL", + data: [ + "address": email.address, + "subject": email.subject, + "body": email.body, + "type": String(describing: email.type), + "typeName": String(describing: email.type), + ] + ) + } + + if let phone = barcode.phone { + return BarcodeParsedValue( + type: "TYPE_PHONE", + data: [ + "number": phone.number, + "type": String(describing: phone.type), + "typeName": String(describing: phone.type), + ] + ) + } + + if let geo = barcode.geoPoint { + return BarcodeParsedValue( + type: "TYPE_GEO", + data: [ + "lat": geo.latitude, + "lng": geo.longitude, + ] + ) + } + + if let calendarEvent = barcode.calendarEvent { + return BarcodeParsedValue( + type: "TYPE_CALENDAR_EVENT", + data: [ + "summary": calendarEvent.summary, + "description": calendarEvent.eventDescription, + "location": calendarEvent.location, + "organizer": calendarEvent.organizer, + "status": calendarEvent.status, + "start": toDateInfo(calendarEvent.start), + "end": toDateInfo(calendarEvent.end), + ] + ) + } + + if let contactInfo = barcode.contactInfo { + return BarcodeParsedValue( + type: "TYPE_CONTACT_INFO", + data: [ + "name": contactInfo.name.map { toPersonName($0) }, + "organization": contactInfo.organization, + "title": contactInfo.jobTitle, + "phones": contactInfo.phones?.map { phone in + [ + "number": phone.number, + "type": String(describing: phone.type), + "typeName": String(describing: phone.type), + ] + }, + "emails": contactInfo.emails?.map { email in + [ + "address": email.address, + "subject": email.subject, + "body": email.body, + "type": String(describing: email.type), + "typeName": String(describing: email.type), + ] + }, + "addresses": contactInfo.addresses?.map { address in + [ + "addressLines": address.addressLines, + "type": String(describing: address.type), + "typeName": String(describing: address.type), + ] + }, + "urls": contactInfo.urls, + ] + ) + } + + if let driverLicense = barcode.driverLicense { + return BarcodeParsedValue( + type: "TYPE_DRIVER_LICENSE", + data: [ + "documentType": driverLicense.documentType, + "licenseNumber": driverLicense.licenseNumber, + "firstName": driverLicense.firstName, + "middleName": driverLicense.middleName, + "lastName": driverLicense.lastName, + "gender": driverLicense.gender, + "addressStreet": driverLicense.addressStreet, + "addressCity": driverLicense.addressCity, + "addressState": driverLicense.addressState, + "addressZip": driverLicense.addressZip, + "birthDate": driverLicense.birthDate, + "issueDate": driverLicense.issuingDate, + "expiryDate": driverLicense.expiryDate, + "issuingCountry": driverLicense.issuingCountry, + ] + ) + } + + if let rawValue = barcode.rawValue { + return BarcodeParsedValue( + type: valueTypeName(barcode.valueType), + data: ["value": rawValue] + ) + } + + return nil + } + + private static func toPersonName(_ name: BarcodePersonName) -> [String: Any?] { + return [ + "formattedName": name.formattedName, + "pronunciation": name.pronunciation, + "prefix": name.prefix, + "first": name.first, + "middle": name.middle, + "last": name.last, + "suffix": name.suffix, + ] + } + + private static func toDateInfo(_ date: Date?) -> [String: Any?]? { + guard let date else { + return nil + } + + let calendar = Calendar(identifier: .gregorian) + let components = calendar.dateComponents( + [.year, .month, .day, .hour, .minute, .second], + from: date + ) + + return [ + "year": components.year, + "month": components.month, + "day": components.day, + "hours": components.hour, + "minutes": components.minute, + "seconds": components.second, + "isUtc": false, + "rawValue": ISO8601DateFormatter().string(from: date), + ] + } + + private static func toBoundingBox(_ rect: CGRect) -> BoundingBox { + let offsetX = (rect.midX - ceil(rect.width)) / 2.0 + let offsetY = (rect.midY - ceil(rect.height)) / 2.0 + + let x = rect.maxX + offsetX + let y = rect.minY + offsetY + + return BoundingBox( + x: rect.midX + (rect.midX - x), + y: rect.midY + (y - rect.midY), + centerX: rect.midX, + centerY: rect.midY, + width: rect.width, + height: rect.height, + top: rect.maxY, + left: rect.minX, + bottom: rect.minY, + right: rect.maxX + ) + } + + private static func toCorner(_ point: CGPoint) -> Corner { + return Corner(x: Double(point.x), y: Double(point.y)) + } + + private static func formatName(_ format: BarcodeFormat) -> String { + switch Int(format.rawValue) { + case 1: return "CODE_128" + case 2: return "CODE_39" + case 4: return "CODE_93" + case 8: return "CODABAR" + case 16: return "DATA_MATRIX" + case 32: return "EAN_13" + case 64: return "EAN_8" + case 128: return "ITF" + case 256: return "QR_CODE" + case 512: return "UPC_A" + case 1024: return "UPC_E" + case 2048: return "PDF417" + case 4096: return "AZTEC" + default: return "UNKNOWN" + } + } + + private static func valueTypeName(_ valueType: BarcodeValueType) -> String { + switch Int(valueType.rawValue) { + case 1: return "TYPE_CONTACT_INFO" + case 2: return "TYPE_EMAIL" + case 3: return "TYPE_ISBN" + case 4: return "TYPE_PHONE" + case 5: return "TYPE_PRODUCT" + case 6: return "TYPE_SMS" + case 7: return "TYPE_TEXT" + case 8: return "TYPE_URL" + case 9: return "TYPE_WIFI" + case 10: return "TYPE_GEO" + case 11: return "TYPE_CALENDAR_EVENT" + case 12: return "TYPE_DRIVER_LICENSE" + default: return "TYPE_UNKNOWN" + } + } + } +#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Infrastructure/MLKit/Factories/BarcodeScanningServiceFactory.swift b/ios/Infrastructure/MLKit/Factories/BarcodeScanningServiceFactory.swift new file mode 100644 index 0000000..e68261d --- /dev/null +++ b/ios/Infrastructure/MLKit/Factories/BarcodeScanningServiceFactory.swift @@ -0,0 +1,65 @@ +import Foundation + +#if MLKIT_BARCODE_SCANNING + import MLKitBarcodeScanning + + class BarcodeScanningServiceFactory { + + static func createBarcodeScanner(options: BarcodeScanningOptions) + -> BarcodeScanner + { + let scannerOptions: BarcodeScannerOptions + if options.formats.isEmpty || options.formats.contains(.formatAllFormats) { + scannerOptions = BarcodeScannerOptions() + } else { + scannerOptions = BarcodeScannerOptions(formats: toMLKitFormats(options.formats)) + } + return BarcodeScanner.barcodeScanner(options: scannerOptions) + } + + static func create(options: BarcodeScanningOptions = BarcodeScanningOptions()) + -> MLKitBarcodeScanningService + { + let scanner = createBarcodeScanner(options: options) + return MLKitBarcodeScanningService(barcodeScanner: scanner) + } + + private static func toMLKitFormats(_ formats: [BarcodeFormatOption]) -> BarcodeFormat { + var rawValue = 0 + for format in formats { + switch format { + case .formatUnknown, .formatAllFormats: + continue + case .formatCode128: + rawValue |= 1 + case .formatCode39: + rawValue |= 2 + case .formatCode93: + rawValue |= 4 + case .formatCodabar: + rawValue |= 8 + case .formatDataMatrix: + rawValue |= 16 + case .formatEan13: + rawValue |= 32 + case .formatEan8: + rawValue |= 64 + case .formatItf: + rawValue |= 128 + case .formatQrCode: + rawValue |= 256 + case .formatUpcA: + rawValue |= 512 + case .formatUpcE: + rawValue |= 1024 + case .formatPdf417: + rawValue |= 2048 + case .formatAztec: + rawValue |= 4096 + } + } + + return BarcodeFormat(rawValue: BarcodeFormat.RawValue(rawValue)) + } + } +#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Infrastructure/MLKit/MLKitBarcodeScanningService.swift b/ios/Infrastructure/MLKit/MLKitBarcodeScanningService.swift new file mode 100644 index 0000000..f217f6f --- /dev/null +++ b/ios/Infrastructure/MLKit/MLKitBarcodeScanningService.swift @@ -0,0 +1,20 @@ +import Foundation + +#if MLKIT_BARCODE_SCANNING + import MLKitBarcodeScanning + + class MLKitBarcodeScanningService: IRecognitionService { + typealias ResultType = BarcodeScanningResult + + private let barcodeScanner: BarcodeScanner + + init(barcodeScanner: BarcodeScanner) { + self.barcodeScanner = barcodeScanner + } + + func recognize(image: ProcessedImage) throws -> BarcodeScanningResult { + let barcodes = try barcodeScanner.results(in: image.image) + return MLKitBarcodeAdapter.toDomain(barcodes) + } + } +#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift b/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift index 1096fcf..a52bb74 100644 --- a/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift +++ b/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift @@ -10,6 +10,10 @@ class VisionCameraMLKitModule: NSObject { handlers[MLKitFeatureKeys.TEXT_RECOGNITION] = StaticTextRecognitionHandler() #endif + #if MLKIT_BARCODE_SCANNING + handlers[MLKitFeatureKeys.BARCODE_SCANNING] = StaticBarcodeScanningHandler() + #endif + return handlers }() diff --git a/ios/Infrastructure/Serializers/BarcodeScanningSerializer.swift b/ios/Infrastructure/Serializers/BarcodeScanningSerializer.swift new file mode 100644 index 0000000..51e7122 --- /dev/null +++ b/ios/Infrastructure/Serializers/BarcodeScanningSerializer.swift @@ -0,0 +1,89 @@ +import Foundation + +#if MLKIT_BARCODE_SCANNING + class BarcodeScanningSerializer { + + static func toReactNativeMap(_ result: BarcodeScanningResult) -> [String: Any] { + var map = [String: Any]() + map["barcodes"] = result.barcodes.map { toBarcodeMap($0) } + return map + } + + private static func toBarcodeMap(_ barcode: BarcodeData) -> [String: Any] { + var map = [String: Any]() + if let bounds = barcode.bounds { + map["bounds"] = toBoundsMap(bounds) + } + if let corners = barcode.corners { + map["corners"] = corners.map { toCornerMap($0) } + } + map["format"] = barcode.format + map["formatName"] = barcode.formatName + map["valueType"] = barcode.valueType + map["valueTypeName"] = barcode.valueTypeName + map["rawValue"] = barcode.rawValue as Any + map["displayValue"] = barcode.displayValue as Any + map["rawBytes"] = barcode.rawBytes as Any + map["isPotential"] = barcode.isPotential + if let value = barcode.value { + map["value"] = toValueMap(value) + } + return map + } + + private static func toValueMap(_ value: BarcodeParsedValue) -> [String: Any] { + var map = [String: Any]() + map["type"] = value.type + map["data"] = toNonOptionalDictionary(value.data) + return map + } + + private static func toBoundsMap(_ bounds: BoundingBox) -> [String: Double] { + return [ + "x": bounds.x, + "y": bounds.y, + "centerX": bounds.centerX, + "centerY": bounds.centerY, + "width": bounds.width, + "height": bounds.height, + "top": bounds.top, + "left": bounds.left, + "bottom": bounds.bottom, + "right": bounds.right, + ] + } + + private static func toCornerMap(_ corner: Corner) -> [String: Double] { + return [ + "x": corner.x, + "y": corner.y, + ] + } + + private static func toNonOptionalDictionary(_ dictionary: [String: Any?]) + -> [String: Any] + { + var output: [String: Any] = [:] + for (key, value) in dictionary { + output[key] = normalize(value) + } + return output + } + + private static func normalize(_ value: Any?) -> Any { + guard let value = value else { + return NSNull() + } + + if let dictionary = value as? [String: Any?] { + return toNonOptionalDictionary(dictionary) + } + + if let array = value as? [Any?] { + return array.map { normalize($0) } + } + + return value + } + } +#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/VisionCameraMLKit.mm b/ios/VisionCameraMLKit.mm index 0577bf2..b39551d 100644 --- a/ios/VisionCameraMLKit.mm +++ b/ios/VisionCameraMLKit.mm @@ -35,3 +35,7 @@ +(void)load { \ #ifdef MLKIT_TEXT_RECOGNITION_ANY REGISTER_PLUGIN(TextRecognitionPlugin, @"TextRecognition") #endif + +#ifdef MLKIT_BARCODE_SCANNING +REGISTER_PLUGIN(BarcodeScanningPlugin, @"BarcodeScanning") +#endif diff --git a/src/features/barcode-scanning/index.ts b/src/features/barcode-scanning/index.ts new file mode 100644 index 0000000..df3ac2b --- /dev/null +++ b/src/features/barcode-scanning/index.ts @@ -0,0 +1,52 @@ +import type { Frame } from 'react-native-vision-camera'; +import { NativeBridge } from '../../core/NativeBridge'; +import { MLKIT_FEATURE_KEYS } from '../../core/constants'; +import { useMLKitPlugin } from '../../hooks/useMLKitPlugin'; +import type { + BarcodeScanningArguments, + BarcodeScanningImageOptions, + BarcodeScanningOptions, + BarcodeScanningResult, +} from './types'; + +/** + * Scan barcodes from a static image file. + */ +export const processImageBarcodeScanning = async ( + uri: string, + options: BarcodeScanningImageOptions = {} +): Promise => { + return await NativeBridge.processImage( + MLKIT_FEATURE_KEYS.BARCODE_SCANNING, + uri, + options + ); +}; + +/** + * Hook for barcode scanning from camera frames. + */ +export const useBarcodeScanning = (options: BarcodeScanningOptions = {}) => { + const plugin = useMLKitPlugin(MLKIT_FEATURE_KEYS.BARCODE_SCANNING, options); + + return { + barcodeScanning: ( + frame: Frame, + args?: BarcodeScanningArguments + ): BarcodeScanningResult => { + 'worklet'; + return plugin.recognize(frame, args); + }, + }; +}; + +export type { + BarcodeData, + BarcodeFormat, + BarcodeParsedValue, + BarcodeScanningArguments, + BarcodeScanningImageOptions, + BarcodeScanningOptions, + BarcodeScanningResult, + BarcodeValueTypeName, +} from './types'; diff --git a/src/features/barcode-scanning/types.ts b/src/features/barcode-scanning/types.ts new file mode 100644 index 0000000..52c81fe --- /dev/null +++ b/src/features/barcode-scanning/types.ts @@ -0,0 +1,229 @@ +import type { + BoundingBox, + Corner, + ImageProcessingBaseOptions, + MLKitBaseArguments, + MLKitBaseOptions, +} from '../../core/types'; + +export type BarcodeFormat = + | 'UNKNOWN' + | 'ALL_FORMATS' + | 'CODE_128' + | 'CODE_39' + | 'CODE_93' + | 'CODABAR' + | 'DATA_MATRIX' + | 'EAN_13' + | 'EAN_8' + | 'ITF' + | 'QR_CODE' + | 'UPC_A' + | 'UPC_E' + | 'PDF417' + | 'AZTEC'; + +export type BarcodeScanningOptions = MLKitBaseOptions & { + /** Restrict detected barcode formats for better performance. */ + formats?: BarcodeFormat[]; + /** + * Android only: include potential barcodes that could not be decoded yet. + * For potential barcodes, `rawValue` and `rawBytes` may be null. + */ + enableAllPotentialBarcodes?: boolean; +}; + +export type BarcodeScanningImageOptions = ImageProcessingBaseOptions & { + /** Restrict detected barcode formats for better performance. */ + formats?: BarcodeFormat[]; + /** + * Android only: include potential barcodes that could not be decoded yet. + * For potential barcodes, `rawValue` and `rawBytes` may be null. + */ + enableAllPotentialBarcodes?: boolean; + /** Optional image downscaling for performance optimization. Clamped to [0.9, 1.0]. */ + scaleFactor?: number; +}; + +export type BarcodeScanningArguments = MLKitBaseArguments & {}; + +export type BarcodeScanningResult = { + barcodes: BarcodeData[]; +}; + +export type BarcodeData = { + bounds?: BoundingBox; + corners?: Corner[]; + format: number; + formatName: BarcodeFormat; + valueType: number; + valueTypeName: BarcodeValueTypeName; + rawValue: string | null; + displayValue: string | null; + rawBytes?: number[] | null; + isPotential: boolean; + value?: BarcodeParsedValue; +}; + +export type BarcodeValueTypeName = + | 'TYPE_UNKNOWN' + | 'TYPE_CONTACT_INFO' + | 'TYPE_EMAIL' + | 'TYPE_ISBN' + | 'TYPE_PHONE' + | 'TYPE_PRODUCT' + | 'TYPE_SMS' + | 'TYPE_TEXT' + | 'TYPE_URL' + | 'TYPE_WIFI' + | 'TYPE_GEO' + | 'TYPE_CALENDAR_EVENT' + | 'TYPE_DRIVER_LICENSE'; + +export type BarcodeCalendarDateTime = { + year?: number | null; + month?: number | null; + day?: number | null; + hours?: number | null; + minutes?: number | null; + seconds?: number | null; + isUtc?: boolean | null; + rawValue?: string | null; +}; + +export type BarcodePersonName = { + formattedName?: string | null; + pronunciation?: string | null; + prefix?: string | null; + first?: string | null; + middle?: string | null; + last?: string | null; + suffix?: string | null; +}; + +export type BarcodePhoneData = { + number?: string | null; + type?: number | string | null; + typeName?: string | null; +}; + +export type BarcodeEmailData = { + address?: string | null; + subject?: string | null; + body?: string | null; + type?: number | string | null; + typeName?: string | null; +}; + +export type BarcodeAddressData = { + addressLines?: string[] | null; + type?: number | string | null; + typeName?: string | null; +}; + +export type BarcodeParsedValue = + | { + type: 'TYPE_WIFI'; + data: { + ssid?: string | null; + password?: string | null; + encryptionType?: number | null; + encryptionTypeName?: string | null; + type?: number | string | null; + typeName?: string | null; + }; + } + | { + type: 'TYPE_URL'; + data: { + title?: string | null; + url?: string | null; + }; + } + | { + type: 'TYPE_SMS'; + data: { + message?: string | null; + phoneNumber?: string | null; + }; + } + | { + type: 'TYPE_EMAIL'; + data: BarcodeEmailData; + } + | { + type: 'TYPE_PHONE'; + data: BarcodePhoneData; + } + | { + type: 'TYPE_GEO'; + data: { + lat?: number | null; + lng?: number | null; + }; + } + | { + type: 'TYPE_CALENDAR_EVENT'; + data: { + summary?: string | null; + description?: string | null; + location?: string | null; + organizer?: string | null; + status?: string | null; + start?: BarcodeCalendarDateTime | null; + end?: BarcodeCalendarDateTime | null; + }; + } + | { + type: 'TYPE_CONTACT_INFO'; + data: { + name?: BarcodePersonName | null; + organization?: string | null; + title?: string | null; + phones?: BarcodePhoneData[] | null; + emails?: BarcodeEmailData[] | null; + addresses?: BarcodeAddressData[] | null; + urls?: string[] | null; + }; + } + | { + type: 'TYPE_DRIVER_LICENSE'; + data: { + documentType?: string | null; + licenseNumber?: string | null; + firstName?: string | null; + middleName?: string | null; + lastName?: string | null; + gender?: string | number | null; + addressStreet?: string | null; + addressCity?: string | null; + addressState?: string | null; + addressZip?: string | null; + birthDate?: string | null; + issueDate?: string | null; + expiryDate?: string | null; + issuingCountry?: string | null; + }; + } + | { + type: 'TYPE_TEXT'; + data: { + text?: string | null; + }; + } + | { + type: 'TYPE_PRODUCT'; + data: { + product?: string | null; + }; + } + | { + type: 'TYPE_ISBN'; + data: { + isbn?: string | null; + }; + } + | { + type: 'TYPE_UNKNOWN'; + data: Record; + }; diff --git a/src/index.ts b/src/index.ts index 182f296..480d648 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,4 +25,5 @@ export { type Orientation, } from './core/types'; +export * from './features/barcode-scanning'; export * from './features/text-recognition'; From 845db8ca0f092dc42af3befb83ebe9341b1788f4 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Mon, 30 Mar 2026 17:44:43 -0300 Subject: [PATCH 14/45] chore(xcode): update pbxproj --- .../project.pbxproj | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj b/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj index 3712a26..8a0bd97 100644 --- a/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj +++ b/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj @@ -11,20 +11,20 @@ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; - F12AE336641C07D5C2078619 /* libPods-VisionCameraMLKitExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F3C7C2173AAF51CC7ECBF1AB /* libPods-VisionCameraMLKitExample.a */; }; + AF701C817C27352337BA2476 /* libPods-VisionCameraMLKitExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C5BC0F5E3B6D3A9AF3491FC5 /* libPods-VisionCameraMLKitExample.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 0B24B4F6BC4651B8BA449206 /* Pods-VisionCameraMLKitExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.release.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.release.xcconfig"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* VisionCameraMLKitExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VisionCameraMLKitExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = VisionCameraMLKitExample/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = VisionCameraMLKitExample/Info.plist; sourceTree = ""; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = VisionCameraMLKitExample/PrivacyInfo.xcprivacy; sourceTree = ""; }; - 703F5B7AAE7B2A7EB18ADAF9 /* Pods-VisionCameraMLKitExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.release.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.release.xcconfig"; sourceTree = ""; }; + 1AB01998217D40EF3F0E1BC8 /* Pods-VisionCameraMLKitExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.debug.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.debug.xcconfig"; sourceTree = ""; }; 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = VisionCameraMLKitExample/AppDelegate.swift; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = VisionCameraMLKitExample/LaunchScreen.storyboard; sourceTree = ""; }; - B92D03227A73B651DBBE5627 /* Pods-VisionCameraMLKitExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.debug.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.debug.xcconfig"; sourceTree = ""; }; + C5BC0F5E3B6D3A9AF3491FC5 /* libPods-VisionCameraMLKitExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VisionCameraMLKitExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; - F3C7C2173AAF51CC7ECBF1AB /* libPods-VisionCameraMLKitExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VisionCameraMLKitExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -32,7 +32,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F12AE336641C07D5C2078619 /* libPods-VisionCameraMLKitExample.a in Frameworks */, + AF701C817C27352337BA2476 /* libPods-VisionCameraMLKitExample.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -55,7 +55,7 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - F3C7C2173AAF51CC7ECBF1AB /* libPods-VisionCameraMLKitExample.a */, + C5BC0F5E3B6D3A9AF3491FC5 /* libPods-VisionCameraMLKitExample.a */, ); name = Frameworks; sourceTree = ""; @@ -92,8 +92,8 @@ BBD78D7AC51CEA395F1C20DB /* Pods */ = { isa = PBXGroup; children = ( - B92D03227A73B651DBBE5627 /* Pods-VisionCameraMLKitExample.debug.xcconfig */, - 703F5B7AAE7B2A7EB18ADAF9 /* Pods-VisionCameraMLKitExample.release.xcconfig */, + 1AB01998217D40EF3F0E1BC8 /* Pods-VisionCameraMLKitExample.debug.xcconfig */, + 0B24B4F6BC4651B8BA449206 /* Pods-VisionCameraMLKitExample.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -105,13 +105,13 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "VisionCameraMLKitExample" */; buildPhases = ( - A58C7600C49A08D8191F7748 /* [CP] Check Pods Manifest.lock */, + 57C1816353D674F9DF420F97 /* [CP] Check Pods Manifest.lock */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 91792E50D381D930AC7AC001 /* [CP] Embed Pods Frameworks */, - CDBBCB7CB689AD23EC870207 /* [CP] Copy Pods Resources */, + 8DB53D091FCF94AC3271259D /* [CP] Embed Pods Frameworks */, + 1D67C6E1EB6EEC99B3F2D89A /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -184,24 +184,24 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; - 91792E50D381D930AC7AC001 /* [CP] Embed Pods Frameworks */ = { + 1D67C6E1EB6EEC99B3F2D89A /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources.sh\"\n"; showEnvVarsInLog = 0; }; - A58C7600C49A08D8191F7748 /* [CP] Check Pods Manifest.lock */ = { + 57C1816353D674F9DF420F97 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -223,21 +223,21 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - CDBBCB7CB689AD23EC870207 /* [CP] Copy Pods Resources */ = { + 8DB53D091FCF94AC3271259D /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -256,7 +256,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B92D03227A73B651DBBE5627 /* Pods-VisionCameraMLKitExample.debug.xcconfig */; + baseConfigurationReference = 1AB01998217D40EF3F0E1BC8 /* Pods-VisionCameraMLKitExample.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -289,7 +289,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 703F5B7AAE7B2A7EB18ADAF9 /* Pods-VisionCameraMLKitExample.release.xcconfig */; + baseConfigurationReference = 0B24B4F6BC4651B8BA449206 /* Pods-VisionCameraMLKitExample.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; From d874792c51d8f90cc34c7e4e55395ba6626c9b47 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Mon, 6 Apr 2026 18:45:18 -0300 Subject: [PATCH 15/45] docs(xcode): add Xcode 26.4 + fmt Build Failure Workaround --- docs/xcode-26.4-fmt-consteval-workaround.md | 79 +++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 docs/xcode-26.4-fmt-consteval-workaround.md diff --git a/docs/xcode-26.4-fmt-consteval-workaround.md b/docs/xcode-26.4-fmt-consteval-workaround.md new file mode 100644 index 0000000..88a3c6c --- /dev/null +++ b/docs/xcode-26.4-fmt-consteval-workaround.md @@ -0,0 +1,79 @@ +# Xcode 26.4 + fmt Build Failure Workaround (React Native / iOS) + +## Problem + +After updating to Xcode 26.4 (including betas/RC), iOS builds can fail in Pods with errors related to the `fmt` C++ library. + +Typical symptom: + +- build fails in `fmt/include/fmt/base.h` +- errors reference `consteval` or macros around `FMT_USE_CONSTEVAL` + +## Root cause + +`fmt` checks compiler support and enables: + +```cpp +FMT_USE_CONSTEVAL 1 +``` + +On Xcode 26.4 toolchains, `__cpp_consteval` can be detected, but compilation may still fail for this path. + +## Workaround + +Patch `fmt/include/fmt/base.h` after `pod install` and force: + +- `FMT_USE_CONSTEVAL 1` -> `FMT_USE_CONSTEVAL 0` + +This is safe for app behavior; it only disables that compile-time optimization path and falls back to non-`consteval` handling. + +## Podfile patch + +Add this in your `Podfile` (inside your target where `post_install` exists): + +```ruby +def apply_fmt_xcode_26_workaround(installer) + fmt_base = File.join(installer.sandbox.root, 'fmt', 'include', 'fmt', 'base.h') + return unless File.exist?(fmt_base) + + content = File.read(fmt_base) + return if content.include?('Xcode 26 workaround: disable consteval') + + patched = content.gsub( + /^(#elif defined\(__cpp_consteval\)\n# define FMT_USE_CONSTEVAL) 1$/, + "// Xcode 26 workaround: disable consteval\n\\1 0" + ) + + return if patched == content + + File.chmod(0o644, fmt_base) + File.write(fmt_base, patched) + Pod::UI.puts('Applied Xcode 26 fmt workaround (FMT_USE_CONSTEVAL=0)'.yellow) +end + +post_install do |installer| + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + ) + + apply_fmt_xcode_26_workaround(installer) +end +``` + +## Apply it + +From your iOS folder: + +```bash +pod install +``` + +Then rebuild in Xcode. + +## Notes + +- This is a temporary workaround until upstream toolchain/library compatibility is fully resolved. +- Keep this patch only while affected by Xcode 26.4 toolchains. +- Because it runs in `post_install`, it is re-applied automatically on future `pod install` runs. From 6b3cdc6fd96d701f76c719f788da5a346da77bc7 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Mon, 6 Apr 2026 18:46:07 -0300 Subject: [PATCH 16/45] chore(xcode): update pods and add workaround for newest xcode version --- example/ios/Podfile | 21 +++ example/ios/Podfile.lock | 144 +++++++++--------- .../project.pbxproj | 32 ++-- 3 files changed, 105 insertions(+), 92 deletions(-) diff --git a/example/ios/Podfile b/example/ios/Podfile index 62887aa..31e7ddf 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -53,6 +53,25 @@ target 'VisionCameraMLKitExample' do :app_path => "#{Pod::Config.instance.installation_root}/.." ) + def apply_fmt_xcode_26_workaround(installer) + fmt_base = File.join(installer.sandbox.root, 'fmt', 'include', 'fmt', 'base.h') + return unless File.exist?(fmt_base) + + content = File.read(fmt_base) + return if content.include?('Xcode 26 workaround: disable consteval') + + patched = content.gsub( + /^(#elif defined\(__cpp_consteval\)\n# define FMT_USE_CONSTEVAL) 1$/, + "// Xcode 26 workaround: disable consteval\n\\1 0" + ) + + return if patched == content + + File.chmod(0o644, fmt_base) + File.write(fmt_base, patched) + Pod::UI.puts('Applied Xcode 26 fmt workaround (FMT_USE_CONSTEVAL=0)'.yellow) + end + post_install do |installer| # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( @@ -61,5 +80,7 @@ target 'VisionCameraMLKitExample' do :mac_catalyst_enabled => false, # :ccache_enabled => true ) + + apply_fmt_xcode_26_workaround(installer) end end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index faba3f9..4d9e30f 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -9,38 +9,31 @@ PODS: - GoogleUtilities/Environment (~> 7.7) - nanopb (< 2.30911.0, >= 2.30908.0) - PromisesObjC (< 3.0, >= 1.2) - - GoogleMLKit/BarcodeScanning (5.0.0): + - GoogleMLKit/BarcodeScanning (6.0.0): - GoogleMLKit/MLKitCore - - MLKitBarcodeScanning (~> 4.0.0) - - GoogleMLKit/MLKitCore (5.0.0): - - MLKitCommon (~> 10.0.0) - - GoogleMLKit/TextRecognition (5.0.0): + - MLKitBarcodeScanning (~> 5.0.0) + - GoogleMLKit/MLKitCore (6.0.0): + - MLKitCommon (~> 11.0.0) + - GoogleMLKit/TextRecognition (6.0.0): - GoogleMLKit/MLKitCore - - MLKitTextRecognition (~> 3.0.0) - - GoogleMLKit/TextRecognitionChinese (5.0.0): + - MLKitTextRecognition (~> 4.0.0) + - GoogleMLKit/TextRecognitionChinese (6.0.0): - GoogleMLKit/MLKitCore - - MLKitTextRecognitionChinese (~> 2.0.0) - - GoogleMLKit/TextRecognitionDevanagari (5.0.0): + - MLKitTextRecognitionChinese (~> 3.0.0) + - GoogleMLKit/TextRecognitionDevanagari (6.0.0): - GoogleMLKit/MLKitCore - - MLKitTextRecognitionDevanagari (~> 2.0.0) - - GoogleMLKit/TextRecognitionJapanese (5.0.0): + - MLKitTextRecognitionDevanagari (~> 3.0.0) + - GoogleMLKit/TextRecognitionJapanese (6.0.0): - GoogleMLKit/MLKitCore - - MLKitTextRecognitionJapanese (~> 2.0.0) - - GoogleMLKit/TextRecognitionKorean (5.0.0): + - MLKitTextRecognitionJapanese (~> 3.0.0) + - GoogleMLKit/TextRecognitionKorean (6.0.0): - GoogleMLKit/MLKitCore - - MLKitTextRecognitionKorean (~> 2.0.0) - - GoogleToolboxForMac/DebugUtils (2.3.2): - - GoogleToolboxForMac/Defines (= 2.3.2) - - GoogleToolboxForMac/Defines (2.3.2) - - GoogleToolboxForMac/Logger (2.3.2): - - GoogleToolboxForMac/Defines (= 2.3.2) - - "GoogleToolboxForMac/NSData+zlib (2.3.2)": - - GoogleToolboxForMac/Defines (= 2.3.2) - - "GoogleToolboxForMac/NSDictionary+URLArguments (2.3.2)": - - GoogleToolboxForMac/DebugUtils (= 2.3.2) - - GoogleToolboxForMac/Defines (= 2.3.2) - - "GoogleToolboxForMac/NSString+URLArguments (= 2.3.2)" - - "GoogleToolboxForMac/NSString+URLArguments (2.3.2)" + - MLKitTextRecognitionKorean (~> 3.0.0) + - GoogleToolboxForMac/Defines (4.2.1) + - GoogleToolboxForMac/Logger (4.2.1): + - GoogleToolboxForMac/Defines (= 4.2.1) + - "GoogleToolboxForMac/NSData+zlib (4.2.1)": + - GoogleToolboxForMac/Defines (= 4.2.1) - GoogleUtilities/Environment (7.13.3): - GoogleUtilities/Privacy - PromisesObjC (< 3.0, >= 1.2) @@ -58,46 +51,45 @@ PODS: - hermes-engine/Pre-built (= 0.81.1) - hermes-engine/Pre-built (0.81.1) - MLImage (1.0.0-beta5) - - MLKitBarcodeScanning (4.0.0): - - MLKitCommon (~> 10.0) - - MLKitVision (~> 6.0) - - MLKitCommon (10.0.0): - - GoogleDataTransport (~> 9.0) - - GoogleToolboxForMac/Logger (~> 2.1) - - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" - - "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)" - - GoogleUtilities/UserDefaults (~> 7.0) + - MLKitBarcodeScanning (5.0.0): + - MLKitCommon (~> 11.0) + - MLKitVision (~> 7.0) + - MLKitCommon (11.0.0): + - GoogleDataTransport (< 10.0, >= 9.4.1) + - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) + - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" + - GoogleUtilities/UserDefaults (< 8.0, >= 7.13.0) - GoogleUtilitiesComponents (~> 1.0) - - GTMSessionFetcher/Core (< 4.0, >= 1.1) - - MLKitTextRecognition (3.0.0): - - MLKitCommon (~> 10.0) - - MLKitTextRecognitionCommon (= 2.0.0) - - MLKitVision (~> 6.0) - - MLKitTextRecognitionChinese (2.0.0): - - MLKitCommon (~> 10.0) - - MLKitTextRecognitionCommon (= 2.0.0) - - MLKitVision (~> 6.0) - - MLKitTextRecognitionCommon (2.0.0): - - MLKitCommon (~> 10.0) - - MLKitVision (~> 6.0) - - MLKitTextRecognitionDevanagari (2.0.0): - - MLKitCommon (~> 10.0) - - MLKitTextRecognitionCommon (= 2.0.0) - - MLKitVision (~> 6.0) - - MLKitTextRecognitionJapanese (2.0.0): - - MLKitCommon (~> 10.0) - - MLKitTextRecognitionCommon (= 2.0.0) - - MLKitVision (~> 6.0) - - MLKitTextRecognitionKorean (2.0.0): - - MLKitCommon (~> 10.0) - - MLKitTextRecognitionCommon (= 2.0.0) - - MLKitVision (~> 6.0) - - MLKitVision (6.0.0): - - GoogleToolboxForMac/Logger (~> 2.1) - - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" - - GTMSessionFetcher/Core (< 4.0, >= 1.1) + - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) + - MLKitTextRecognition (4.0.0): + - MLKitCommon (~> 11.0) + - MLKitTextRecognitionCommon (= 3.0.0) + - MLKitVision (~> 7.0) + - MLKitTextRecognitionChinese (3.0.0): + - MLKitCommon (~> 11.0) + - MLKitTextRecognitionCommon (= 3.0.0) + - MLKitVision (~> 7.0) + - MLKitTextRecognitionCommon (3.0.0): + - MLKitCommon (~> 11.0) + - MLKitVision (~> 7.0) + - MLKitTextRecognitionDevanagari (3.0.0): + - MLKitCommon (~> 11.0) + - MLKitTextRecognitionCommon (= 3.0.0) + - MLKitVision (~> 7.0) + - MLKitTextRecognitionJapanese (3.0.0): + - MLKitCommon (~> 11.0) + - MLKitTextRecognitionCommon (= 3.0.0) + - MLKitVision (~> 7.0) + - MLKitTextRecognitionKorean (3.0.0): + - MLKitCommon (~> 11.0) + - MLKitTextRecognitionCommon (= 3.0.0) + - MLKitVision (~> 7.0) + - MLKitVision (7.0.0): + - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) + - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" + - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - MLImage (= 1.0.0-beta5) - - MLKitCommon (~> 10.0) + - MLKitCommon (~> 11.0) - nanopb (2.30910.0): - nanopb/decode (= 2.30910.0) - nanopb/encode (= 2.30910.0) @@ -3269,22 +3261,22 @@ SPEC CHECKSUMS: fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a - GoogleMLKit: 90ba06e028795a50261f29500d238d6061538711 - GoogleToolboxForMac: 8bef7c7c5cf7291c687cf5354f39f9db6399ad34 + GoogleMLKit: 97ac7af399057e99182ee8edfa8249e3226a4065 + GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8 GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 hermes-engine: 4f8246b1f6d79f625e0d99472d1f3a71da4d28ca MLImage: 1824212150da33ef225fbd3dc49f184cf611046c - MLKitBarcodeScanning: 9cb0ec5ec65bbb5db31de4eba0a3289626beab4e - MLKitCommon: afcd11b6c0735066a0dde8b4bf2331f6197cbca2 - MLKitTextRecognition: 91706574e4db9b48cd2e66cbbcc6b58956156b0a - MLKitTextRecognitionChinese: 19d3a70799f17cf2115b6381964b87e874d96dcd - MLKitTextRecognitionCommon: 3bd8375160d21dc9f9cc512d1fab2530ecc43dde - MLKitTextRecognitionDevanagari: 9c689b259f6654fdaa1004a769dcec55b257a02a - MLKitTextRecognitionJapanese: 9afa94ad6d622a69c2043def562fffa9be8d269d - MLKitTextRecognitionKorean: 4b37d1a034af61bf9699d791ae8bcf04cb1e97b7 - MLKitVision: 90922bca854014a856f8b649d1f1f04f63fd9c79 + MLKitBarcodeScanning: 10ca0845a6d15f2f6e911f682a1998b68b973e8b + MLKitCommon: afec63980417d29ffbb4790529a1b0a2291699e1 + MLKitTextRecognition: c83c18ad25496f2077f6ec93c5940487ff2eb343 + MLKitTextRecognitionChinese: 09d6e22bbc2f1687573473ebb6e470af11c65e7a + MLKitTextRecognitionCommon: c0b3a63d529296a19bce1f8bc8a513644ed4d1f6 + MLKitTextRecognitionDevanagari: abd49431a28e385eaa98edcc2da9664e1eb05b51 + MLKitTextRecognitionJapanese: f59521572316562030b20cc98b8e635fc282a172 + MLKitTextRecognitionKorean: 4877b6a8d95cd8846586e83f85b2f3e9ac93a737 + MLKitVision: e858c5f125ecc288e4a31127928301eaba9ae0c1 nanopb: 438bc412db1928dac798aa6fd75726007be04262 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f @@ -3366,6 +3358,6 @@ SPEC CHECKSUMS: VisionCameraMLKit: 771681486e3ec5b6590e28170f9ed74248b8912c Yoga: fa23995c18b65978347b096d0836f4f5093df545 -PODFILE CHECKSUM: 1e33379caa52806ba900154e8eb803a55bbce31c +PODFILE CHECKSUM: 0180fc6b621205cc4ae68612463a743f47d460b3 COCOAPODS: 1.16.2 diff --git a/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj b/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj index 8a0bd97..ea317d0 100644 --- a/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj +++ b/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj @@ -9,21 +9,21 @@ /* Begin PBXBuildFile section */ 057CEFDD928055D235F3DB7A /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 4CBF21469BE79F9BE7F21815 /* libPods-VisionCameraMLKitExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A63FD178BEE6246E4EC6416 /* libPods-VisionCameraMLKitExample.a */; }; 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; - AF701C817C27352337BA2476 /* libPods-VisionCameraMLKitExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C5BC0F5E3B6D3A9AF3491FC5 /* libPods-VisionCameraMLKitExample.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 0B24B4F6BC4651B8BA449206 /* Pods-VisionCameraMLKitExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.release.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.release.xcconfig"; sourceTree = ""; }; + 0A63FD178BEE6246E4EC6416 /* libPods-VisionCameraMLKitExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VisionCameraMLKitExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07F961A680F5B00A75B9A /* VisionCameraMLKitExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VisionCameraMLKitExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = VisionCameraMLKitExample/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = VisionCameraMLKitExample/Info.plist; sourceTree = ""; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = VisionCameraMLKitExample/PrivacyInfo.xcprivacy; sourceTree = ""; }; - 1AB01998217D40EF3F0E1BC8 /* Pods-VisionCameraMLKitExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.debug.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.debug.xcconfig"; sourceTree = ""; }; + 2CC660772738D5556E105E3F /* Pods-VisionCameraMLKitExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.release.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.release.xcconfig"; sourceTree = ""; }; + 65B025F5B9C706CD9747F045 /* Pods-VisionCameraMLKitExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.debug.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.debug.xcconfig"; sourceTree = ""; }; 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = VisionCameraMLKitExample/AppDelegate.swift; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = VisionCameraMLKitExample/LaunchScreen.storyboard; sourceTree = ""; }; - C5BC0F5E3B6D3A9AF3491FC5 /* libPods-VisionCameraMLKitExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VisionCameraMLKitExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -32,7 +32,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - AF701C817C27352337BA2476 /* libPods-VisionCameraMLKitExample.a in Frameworks */, + 4CBF21469BE79F9BE7F21815 /* libPods-VisionCameraMLKitExample.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -55,7 +55,7 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - C5BC0F5E3B6D3A9AF3491FC5 /* libPods-VisionCameraMLKitExample.a */, + 0A63FD178BEE6246E4EC6416 /* libPods-VisionCameraMLKitExample.a */, ); name = Frameworks; sourceTree = ""; @@ -92,8 +92,8 @@ BBD78D7AC51CEA395F1C20DB /* Pods */ = { isa = PBXGroup; children = ( - 1AB01998217D40EF3F0E1BC8 /* Pods-VisionCameraMLKitExample.debug.xcconfig */, - 0B24B4F6BC4651B8BA449206 /* Pods-VisionCameraMLKitExample.release.xcconfig */, + 65B025F5B9C706CD9747F045 /* Pods-VisionCameraMLKitExample.debug.xcconfig */, + 2CC660772738D5556E105E3F /* Pods-VisionCameraMLKitExample.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -105,13 +105,13 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "VisionCameraMLKitExample" */; buildPhases = ( - 57C1816353D674F9DF420F97 /* [CP] Check Pods Manifest.lock */, + 8581DA70A42FA364899A11ED /* [CP] Check Pods Manifest.lock */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 8DB53D091FCF94AC3271259D /* [CP] Embed Pods Frameworks */, - 1D67C6E1EB6EEC99B3F2D89A /* [CP] Copy Pods Resources */, + D0DE7A8E7FF532555CD54397 /* [CP] Embed Pods Frameworks */, + 04BD6B3760B3994DF3D2A7AE /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -184,7 +184,7 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; - 1D67C6E1EB6EEC99B3F2D89A /* [CP] Copy Pods Resources */ = { + 04BD6B3760B3994DF3D2A7AE /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -201,7 +201,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 57C1816353D674F9DF420F97 /* [CP] Check Pods Manifest.lock */ = { + 8581DA70A42FA364899A11ED /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -223,7 +223,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 8DB53D091FCF94AC3271259D /* [CP] Embed Pods Frameworks */ = { + D0DE7A8E7FF532555CD54397 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -256,7 +256,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 1AB01998217D40EF3F0E1BC8 /* Pods-VisionCameraMLKitExample.debug.xcconfig */; + baseConfigurationReference = 65B025F5B9C706CD9747F045 /* Pods-VisionCameraMLKitExample.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -289,7 +289,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0B24B4F6BC4651B8BA449206 /* Pods-VisionCameraMLKitExample.release.xcconfig */; + baseConfigurationReference = 2CC660772738D5556E105E3F /* Pods-VisionCameraMLKitExample.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; From 562bcba7fb88e2adf7d057a99aaad637089448eb Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 5 Jul 2026 20:46:26 -0300 Subject: [PATCH 17/45] fix(ImageProcessor): image orientation on iOS --- ios/Infrastructure/Image/ImagePreprocessor.swift | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ios/Infrastructure/Image/ImagePreprocessor.swift b/ios/Infrastructure/Image/ImagePreprocessor.swift index 89d6dfb..df98c00 100644 --- a/ios/Infrastructure/Image/ImagePreprocessor.swift +++ b/ios/Infrastructure/Image/ImagePreprocessor.swift @@ -245,11 +245,9 @@ import Foundation return nil } - // Use the user-provided orientation override, or fall back to the image's - // embedded metadata orientation (EXIF). UIImage(data:) already decodes - // EXIF orientation into uiImage.imageOrientation. - let effectiveOrientation = - options.orientation ?? Orientation.fromUIImageOrientation(uiImage.imageOrientation) + // Use user-provided orientation override when available. + // Otherwise use the resulting UIImage orientation after processing. + let requestedOrientation = options.orientation?.asUIImageOrientation let effectiveScale = clampScale(options.scaleFactor) var processedImage = uiImage @@ -266,7 +264,8 @@ import Foundation // Create VisionImage let visionImage = VisionImage(image: processedImage) - visionImage.orientation = effectiveOrientation.asUIImageOrientation + let visionOrientation = requestedOrientation ?? processedImage.imageOrientation + visionImage.orientation = visionOrientation let pixelWidth = processedImage.cgImage?.width @@ -278,7 +277,7 @@ import Foundation let metadata = ImageMetadata( width: pixelWidth, height: pixelHeight, - orientation: effectiveOrientation.asUIImageOrientation, + orientation: visionOrientation, isInverted: options.invertColors ) From dd0a0567631500a742c8c6f22056cce01a652930 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 5 Jul 2026 21:08:44 -0300 Subject: [PATCH 18/45] chore(gitignore): ignores .context folder for local ai context --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 07e33b1..cb931b7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ .vscode/ jsconfig.json +# Local AI development context +.context/ + # Xcode # build/ From ef9130d651c4dc718c4f490856eaaf892c596af9 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 5 Jul 2026 22:23:36 -0300 Subject: [PATCH 19/45] feat(vision-camera): scaffold v5 Nitro migration Add Nitro specs, native HybridObject scaffolding, Android CMake integration, and VisionCamera v5 example frame-output wiring. Replace the old VisionCamera v4 frame processor plugin registration with the new Nitro-backed factory surface. Live ML Kit frame processing remains scaffolded for the next implementation phase. --- VisionCameraMLKit.podspec | 3 + android/CMakeLists.txt | 62 +++ android/build.gradle | 54 ++- android/src/main/cpp/cpp-adapter.cpp | 41 ++ .../visioncameramlkit/HybridBarcodeScanner.kt | 15 + .../visioncameramlkit/HybridTextRecognizer.kt | 15 + .../HybridVisionCameraMLKit.kt | 40 ++ .../VisionCameraMLKitPackage.kt | 10 +- .../usecases/RecognizeBarcodesUseCase.kt | 16 - .../usecases/RecognizeTextUseCase.kt | 16 - .../bridge/plugins/BarcodeScanningPlugin.kt | 47 -- .../bridge/plugins/BaseMLKitPlugin.kt | 64 --- .../bridge/plugins/TextRecognitionPlugin.kt | 60 --- .../bridge/registry/PluginRegistry.kt | 82 ---- .../domain/services/IImagePreprocessor.kt | 6 - .../infrastructure/image/ImagePreprocessor.kt | 77 --- example/babel.config.js | 5 +- example/package.json | 8 +- example/src/components/ui/CameraButton.tsx | 2 +- example/src/components/ui/CameraControls.tsx | 2 +- example/src/components/views/CameraView.tsx | 107 +++-- example/src/screens/CameraScreen.tsx | 27 +- ios/Bridge/Hybrid/HybridBarcodeScanner.swift | 15 + ios/Bridge/Hybrid/HybridTextRecognizer.swift | 15 + .../Hybrid/HybridVisionCameraMLKit.swift | 75 +++ .../Plugins/BarcodeScanningPlugin.swift | 50 -- ios/Bridge/Plugins/BaseMLKitPlugin.swift | 74 --- .../Plugins/TextRecognitionPlugin.swift | 68 --- ios/Bridge/Registry/PluginRegistry.swift | 15 - ios/VisionCameraMLKit-Bridging-Header.h | 4 - ios/VisionCameraMLKit.mm | 41 -- nitro.json | 25 + package.json | 19 +- src/core/PluginFactory.ts | 26 +- src/core/VisionCameraMLKit.ts | 5 + src/features/barcode-scanning/types.ts | 260 ++++++---- src/hooks/useMLKitPlugin.ts | 2 +- src/index.ts | 15 + src/specs/BarcodeScanner.nitro.ts | 20 + src/specs/BarcodeScannerTypes.ts | 70 +++ src/specs/MLKitVisionTypes.ts | 63 +++ src/specs/TextRecognizer.nitro.ts | 22 + src/specs/VisionCameraMLKit.nitro.ts | 29 ++ yarn.lock | 451 ++++++++++++++++-- 44 files changed, 1262 insertions(+), 861 deletions(-) create mode 100644 android/CMakeLists.txt create mode 100644 android/src/main/cpp/cpp-adapter.cpp create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridVisionCameraMLKit.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/bridge/plugins/BarcodeScanningPlugin.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/bridge/plugins/BaseMLKitPlugin.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/bridge/plugins/TextRecognitionPlugin.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt create mode 100644 ios/Bridge/Hybrid/HybridBarcodeScanner.swift create mode 100644 ios/Bridge/Hybrid/HybridTextRecognizer.swift create mode 100644 ios/Bridge/Hybrid/HybridVisionCameraMLKit.swift delete mode 100644 ios/Bridge/Plugins/BarcodeScanningPlugin.swift delete mode 100644 ios/Bridge/Plugins/BaseMLKitPlugin.swift delete mode 100644 ios/Bridge/Plugins/TextRecognitionPlugin.swift delete mode 100644 ios/Bridge/Registry/PluginRegistry.swift delete mode 100644 ios/VisionCameraMLKit-Bridging-Header.h delete mode 100644 ios/VisionCameraMLKit.mm create mode 100644 nitro.json create mode 100644 src/core/VisionCameraMLKit.ts create mode 100644 src/specs/BarcodeScanner.nitro.ts create mode 100644 src/specs/BarcodeScannerTypes.ts create mode 100644 src/specs/MLKitVisionTypes.ts create mode 100644 src/specs/TextRecognizer.nitro.ts create mode 100644 src/specs/VisionCameraMLKit.nitro.ts diff --git a/VisionCameraMLKit.podspec b/VisionCameraMLKit.podspec index a5236fb..88f71d1 100644 --- a/VisionCameraMLKit.podspec +++ b/VisionCameraMLKit.podspec @@ -1,6 +1,7 @@ require "json" package = JSON.parse(File.read(File.join(__dir__, "package.json"))) +load 'nitrogen/generated/ios/VisionCameraMLKit+autolinking.rb' # Load MLKit configuration from global variable set in Podfile # Default configuration @@ -153,6 +154,8 @@ Pod::Spec.new do |s| # ===== GoogleMLKit Vision ===== + add_nitrogen_files(s) + if respond_to?(:install_modules_dependencies, true) install_modules_dependencies(s) else diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt new file mode 100644 index 0000000..f71a5c6 --- /dev/null +++ b/android/CMakeLists.txt @@ -0,0 +1,62 @@ +project(VisionCameraMLKit) +cmake_minimum_required(VERSION 3.9.0) + +set(PACKAGE_NAME VisionCameraMLKit) +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_CXX_STANDARD 20) + +add_library(${PACKAGE_NAME} SHARED + src/main/cpp/cpp-adapter.cpp +) + +add_definitions(-DBUILDING_VISIONCAMERAMLKIT_WITH_GENERATED_CMAKE_PROJECT) +add_definitions(-DRN_SERIALIZABLE_STATE) + +include_directories( + "../nitrogen/generated/shared/c++" + "../nitrogen/generated/android/c++" + "../nitrogen/generated/android/" + "src/main/cpp" + "../cpp" +) + +target_sources( + ${PACKAGE_NAME} PRIVATE + ../nitrogen/generated/shared/c++/HybridBarcodeScannerSpec.cpp + ../nitrogen/generated/shared/c++/HybridTextRecognizerSpec.cpp + ../nitrogen/generated/shared/c++/HybridVisionCameraMLKitSpec.cpp + ../nitrogen/generated/android/c++/JHybridBarcodeScannerSpec.cpp + ../nitrogen/generated/android/c++/JHybridTextRecognizerSpec.cpp + ../nitrogen/generated/android/c++/JHybridVisionCameraMLKitSpec.cpp + ../nitrogen/generated/android/c++/JVariant_NullType_Double.cpp +) + +find_library(LOG_LIB log) +find_package(fbjni REQUIRED) +find_package(ReactAndroid REQUIRED) +find_package(react-native-nitro-image REQUIRED) +find_package(react-native-nitro-modules REQUIRED) +find_package(react-native-vision-camera REQUIRED) + +target_link_libraries( + ${PACKAGE_NAME} + ${LOG_LIB} + android + fbjni::fbjni + ReactAndroid::jsi + react-native-nitro-image::NitroImage + react-native-nitro-modules::NitroModules + react-native-vision-camera::VisionCamera +) + +if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76) + target_link_libraries( + ${PACKAGE_NAME} + ReactAndroid::reactnative + ) +else() + target_link_libraries( + ${PACKAGE_NAME} + ReactAndroid::react_nativemodule_core + ) +endif() diff --git a/android/build.gradle b/android/build.gradle index 8fc71e8..74e837f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -21,6 +21,11 @@ apply plugin: "kotlin-android" apply plugin: "com.facebook.react" +def reactNativeArchitectures() { + def value = rootProject.getProperties().get("reactNativeArchitectures") + return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] +} + def getExtOrIntegerDefault(name) { return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["VisionCameraMLKit_" + name]).toInteger() } @@ -79,6 +84,7 @@ def anyTextRecognition = mlkitConfig.textRecognition || android { namespace "com.visioncameramlkit" + ndkVersion getExtOrDefault("ndkVersion") compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") defaultConfig { @@ -103,10 +109,41 @@ android { buildConfigField "boolean", "MLKIT_IMAGE_LABELING", "${mlkitConfig.imageLabeling}" buildConfigField "boolean", "MLKIT_OBJECT_DETECTION", "${mlkitConfig.objectDetection}" buildConfigField "boolean", "MLKIT_DIGITAL_INK_RECOGNITION", "${mlkitConfig.digitalInkRecognition}" + + externalNativeBuild { + cmake { + cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all" + arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON" + abiFilters (*reactNativeArchitectures()) + + buildTypes { + debug { + cppFlags "-O1 -g" + } + release { + cppFlags "-O2" + } + } + } + } + } + + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } } buildFeatures { buildConfig true + prefab true + prefabPublishing true + } + + prefab { + VisionCameraMLKit { + headers "${project.buildDir}/headers/visioncameramlkit/" + } } buildTypes { @@ -128,7 +165,8 @@ android { main { java.srcDirs += [ "generated/java", - "generated/jni" + "generated/jni", + "../nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit" ] } } @@ -144,6 +182,8 @@ def kotlin_version = getExtOrDefault("kotlinVersion") dependencies { implementation "com.facebook.react:react-android" api project(":react-native-vision-camera") + implementation project(":react-native-nitro-image") + implementation project(":react-native-nitro-modules") implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "androidx.camera:camera-core:1.5.2" implementation "androidx.exifinterface:exifinterface:1.3.7" @@ -236,3 +276,15 @@ dependencies { // ===== GoogleMLKit Vision ===== } + +tasks.register('prepareHeaders', Copy) { + from fileTree('./src/main/cpp').filter { it.isFile() } + from fileTree('../cpp/').filter { it.isFile() } + from fileTree('../nitrogen/generated/android/c++').filter { it.isFile() } + from fileTree('../nitrogen/generated/shared/c++').filter { it.isFile() } + include "*.hpp" + into "${project.buildDir}/headers/visioncameramlkit/VisionCameraMLKit/" + includeEmptyDirs = false +} + +preBuild.dependsOn(prepareHeaders) diff --git a/android/src/main/cpp/cpp-adapter.cpp b/android/src/main/cpp/cpp-adapter.cpp new file mode 100644 index 0000000..6c6a5d9 --- /dev/null +++ b/android/src/main/cpp/cpp-adapter.cpp @@ -0,0 +1,41 @@ +#include "VisionCameraMLKitOnLoad.hpp" +#include "JHybridBarcodeScannerSpec.hpp" +#include "JHybridTextRecognizerSpec.hpp" +#include "JHybridVisionCameraMLKitSpec.hpp" +#include +#include +#include + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { + return facebook::jni::initialize(vm, []() { + margelo::nitro::visioncameramlkit::registerAllNatives(); + }); +} + +namespace margelo::nitro::visioncameramlkit { + +struct JHybridVisionCameraMLKitSpecImpl: + public facebook::jni::JavaClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/HybridVisionCameraMLKit;"; + + static std::shared_ptr create() { + static const auto constructorFn = javaClassStatic()->getConstructor(); + facebook::jni::local_ref javaPart = javaClassStatic()->newObject(constructorFn); + return javaPart->getJHybridVisionCameraMLKitSpec(); + } +}; + +void registerAllNatives() { + JHybridBarcodeScannerSpec::CxxPart::registerNatives(); + JHybridTextRecognizerSpec::CxxPart::registerNatives(); + JHybridVisionCameraMLKitSpec::CxxPart::registerNatives(); + + margelo::nitro::HybridObjectRegistry::registerHybridObjectConstructor( + "VisionCameraMLKit", + []() -> std::shared_ptr { + return JHybridVisionCameraMLKitSpecImpl::create(); + } + ); +} + +} // namespace margelo::nitro::visioncameramlkit diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt new file mode 100644 index 0000000..1740362 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt @@ -0,0 +1,15 @@ +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.camera.HybridFrameSpec + +@DoNotStrip +@Keep +class HybridBarcodeScanner( + private val options: BarcodeScannerOptions, +) : HybridBarcodeScannerSpec() { + override fun recognize(frame: HybridFrameSpec, args: MLKitBaseArguments?): BarcodeScannerResult { + throw Error("VisionCamera v5 barcode frame processing is not implemented yet.") + } +} diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt new file mode 100644 index 0000000..97d06b3 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt @@ -0,0 +1,15 @@ +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.camera.HybridFrameSpec + +@DoNotStrip +@Keep +class HybridTextRecognizer( + private val options: TextRecognizerOptions, +) : HybridTextRecognizerSpec() { + override fun recognize(frame: HybridFrameSpec, args: MLKitBaseArguments?): TextRecognitionResult { + throw Error("VisionCamera v5 text recognition frame processing is not implemented yet.") + } +} diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridVisionCameraMLKit.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridVisionCameraMLKit.kt new file mode 100644 index 0000000..01a151d --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridVisionCameraMLKit.kt @@ -0,0 +1,40 @@ +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import com.visioncameramlkit.BuildConfig + +@DoNotStrip +@Keep +class HybridVisionCameraMLKit : HybridVisionCameraMLKitSpec() { + override fun isFeatureAvailable(feature: MLKitFeature): Boolean = + when (feature) { + MLKitFeature.TEXTRECOGNITION -> BuildConfig.MLKIT_TEXT_RECOGNITION_ANY + MLKitFeature.BARCODESCANNING -> BuildConfig.MLKIT_BARCODE_SCANNING + MLKitFeature.FACEDETECTION -> BuildConfig.MLKIT_FACE_DETECTION + MLKitFeature.FACEMESHDETECTION -> BuildConfig.MLKIT_FACE_MESH_DETECTION + MLKitFeature.POSEDETECTION -> BuildConfig.MLKIT_POSE_DETECTION + MLKitFeature.SELFIESEGMENTATION -> BuildConfig.MLKIT_SELFIE_SEGMENTATION + MLKitFeature.SUBJECTSEGMENTATION -> BuildConfig.MLKIT_SUBJECT_SEGMENTATION + MLKitFeature.DOCUMENTSCANNER -> BuildConfig.MLKIT_DOCUMENT_SCANNER + MLKitFeature.IMAGELABELING -> BuildConfig.MLKIT_IMAGE_LABELING + MLKitFeature.OBJECTDETECTION -> BuildConfig.MLKIT_OBJECT_DETECTION + MLKitFeature.DIGITALINKRECOGNITION -> BuildConfig.MLKIT_DIGITAL_INK_RECOGNITION + } + + override fun createTextRecognizer(options: TextRecognizerOptions): HybridTextRecognizerSpec { + if (!isFeatureAvailable(MLKitFeature.TEXTRECOGNITION)) { + throw Error("TextRecognition is not enabled in the native ML Kit configuration.") + } + + return HybridTextRecognizer(options) + } + + override fun createBarcodeScanner(options: BarcodeScannerOptions): HybridBarcodeScannerSpec { + if (!isFeatureAvailable(MLKitFeature.BARCODESCANNING)) { + throw Error("BarcodeScanning is not enabled in the native ML Kit configuration.") + } + + return HybridBarcodeScanner(options) + } +} diff --git a/android/src/main/java/com/visioncameramlkit/VisionCameraMLKitPackage.kt b/android/src/main/java/com/visioncameramlkit/VisionCameraMLKitPackage.kt index b3451cc..40ffb2b 100644 --- a/android/src/main/java/com/visioncameramlkit/VisionCameraMLKitPackage.kt +++ b/android/src/main/java/com/visioncameramlkit/VisionCameraMLKitPackage.kt @@ -6,7 +6,7 @@ import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.module.model.ReactModuleInfo import com.facebook.react.module.model.ReactModuleInfoProvider import com.facebook.react.uimanager.ViewManager -import com.visioncameramlkit.bridge.registry.PluginRegistry +import com.margelo.nitro.visioncameramlkit.VisionCameraMLKitOnLoad import com.visioncameramlkit.infrastructure.modules.VisionCameraMLKitModule class VisionCameraMLKitPackage : BaseReactPackage() { @@ -16,7 +16,6 @@ class VisionCameraMLKitPackage : BaseReactPackage() { ): NativeModule? = when (name) { VisionCameraMLKitModule.NAME -> { - PluginRegistry.ensureRegistered() VisionCameraMLKitModule(reactContext) } @@ -27,7 +26,6 @@ class VisionCameraMLKitPackage : BaseReactPackage() { override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider { - PluginRegistry.ensureRegistered() mapOf( VisionCameraMLKitModule.NAME to ReactModuleInfo( @@ -42,4 +40,10 @@ class VisionCameraMLKitPackage : BaseReactPackage() { } override fun createViewManagers(reactContext: ReactApplicationContext): List> = emptyList() + + companion object { + init { + VisionCameraMLKitOnLoad.initializeNative() + } + } } diff --git a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt b/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt index d17142a..047e70f 100644 --- a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt +++ b/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt @@ -1,6 +1,5 @@ package com.visioncameramlkit.application.usecases -import com.mrousavy.camera.frameprocessors.Frame import com.visioncameramlkit.domain.models.BarcodeScanningOptions import com.visioncameramlkit.domain.models.BarcodeScanningResult import com.visioncameramlkit.domain.models.ImagePreprocessingOptions @@ -12,21 +11,6 @@ class RecognizeBarcodesUseCase( private val imagePreprocessor: IImagePreprocessor, private val recognitionService: IRecognitionService, ) { - fun execute( - frame: Frame, - options: BarcodeScanningOptions, - ): BarcodeScanningResult { - val preprocessingOptions = - ImagePreprocessingOptions( - invertColors = options.invertColors, - outputOrientation = options.outputOrientation, - scaleFactor = options.scaleFactor, - ) - - val processedImage = imagePreprocessor.preprocessFrame(frame, preprocessingOptions) - return recognitionService.recognize(processedImage) - } - fun execute( imageFile: File, imageOptions: ImagePreprocessingOptions, diff --git a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeTextUseCase.kt b/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeTextUseCase.kt index 33ceee7..47f3925 100644 --- a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeTextUseCase.kt +++ b/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeTextUseCase.kt @@ -1,6 +1,5 @@ package com.visioncameramlkit.application.usecases -import com.mrousavy.camera.frameprocessors.Frame import com.visioncameramlkit.domain.models.ImagePreprocessingOptions import com.visioncameramlkit.domain.models.TextRecognitionOptions import com.visioncameramlkit.domain.models.TextRecognitionResult @@ -12,21 +11,6 @@ class RecognizeTextUseCase( private val imagePreprocessor: IImagePreprocessor, private val recognitionService: IRecognitionService, ) { - fun execute( - frame: Frame, - options: TextRecognitionOptions, - ): TextRecognitionResult { - val preprocessingOptions = - ImagePreprocessingOptions( - invertColors = options.invertColors, - outputOrientation = options.outputOrientation, - scaleFactor = options.scaleFactor, - ) - - val processedImage = imagePreprocessor.preprocessFrame(frame, preprocessingOptions) - return recognitionService.recognize(processedImage) - } - fun execute( imageFile: File, imageOptions: ImagePreprocessingOptions, diff --git a/android/src/main/java/com/visioncameramlkit/bridge/plugins/BarcodeScanningPlugin.kt b/android/src/main/java/com/visioncameramlkit/bridge/plugins/BarcodeScanningPlugin.kt deleted file mode 100644 index f96f965..0000000 --- a/android/src/main/java/com/visioncameramlkit/bridge/plugins/BarcodeScanningPlugin.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.visioncameramlkit.bridge.plugins - -import com.mrousavy.camera.frameprocessors.Frame -import com.mrousavy.camera.frameprocessors.VisionCameraProxy -import com.visioncameramlkit.application.usecases.RecognizeBarcodesUseCase -import com.visioncameramlkit.bridge.parsers.BarcodeScanningOptionParser -import com.visioncameramlkit.domain.models.BarcodeScanningOptions -import com.visioncameramlkit.infrastructure.image.ImagePreprocessor -import com.visioncameramlkit.infrastructure.mlkit.factories.BarcodeScanningServiceFactory -import com.visioncameramlkit.infrastructure.serializers.BarcodeScanningSerializer - -@Suppress("unused") -class BarcodeScanningPlugin( - proxy: VisionCameraProxy, - options: Map?, -) : BaseMLKitPlugin(proxy, options) { - override val tag = "BarcodeScanningPlugin" - - private val barcodeScanningOptions: BarcodeScanningOptions = - BarcodeScanningOptions( - formats = BarcodeScanningOptionParser.parseFormats(options?.get("formats")), - enableAllPotentialBarcodes = - BarcodeScanningOptionParser.parseEnableAllPotentialBarcodes( - options?.get("enableAllPotentialBarcodes"), - ), - invertColors = invertColors, - outputOrientation = outputOrientation, - scaleFactor = scaleFactor, - ) - private val recognizeBarcodesUseCase: RecognizeBarcodesUseCase - - init { - - val recognitionService = BarcodeScanningServiceFactory.create(barcodeScanningOptions) - val imagePreprocessor = ImagePreprocessor() - - recognizeBarcodesUseCase = RecognizeBarcodesUseCase(imagePreprocessor, recognitionService) - } - - override fun processFrame( - frame: Frame, - arguments: Map?, - ): HashMap { - val result = recognizeBarcodesUseCase.execute(frame, barcodeScanningOptions) - return BarcodeScanningSerializer.toReactNativeMap(result) - } -} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/plugins/BaseMLKitPlugin.kt b/android/src/main/java/com/visioncameramlkit/bridge/plugins/BaseMLKitPlugin.kt deleted file mode 100644 index 0cb28b7..0000000 --- a/android/src/main/java/com/visioncameramlkit/bridge/plugins/BaseMLKitPlugin.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.visioncameramlkit.bridge.plugins - -import android.util.Log -import com.google.mlkit.common.MlKitException -import com.mrousavy.camera.core.FrameInvalidError -import com.mrousavy.camera.frameprocessors.Frame -import com.mrousavy.camera.frameprocessors.FrameProcessorPlugin -import com.mrousavy.camera.frameprocessors.VisionCameraProxy - -@Suppress("MagicNumber") -abstract class BaseMLKitPlugin( - @Suppress("unused") - protected val proxy: VisionCameraProxy, - protected val options: Map?, -) : FrameProcessorPlugin() { - protected abstract val tag: String - - private val frameProcessInterval: Int = - (options?.get("frameProcessInterval") as? Number)?.toInt() ?: 0 - private var frameCounter: Int = 0 - protected val invertColors: Boolean = options?.get("invertColors") as? Boolean ?: false - protected val scaleFactor: Float = - (options?.get("scaleFactor") as? Number)?.toFloat()?.coerceIn(0.9f, 1.0f) ?: 1.0f - - // Android handles rotation automatically, so outputOrientation is ignored. Added for iOS parity. - protected val outputOrientation: com.visioncameramlkit.domain.models.OutputOrientation? = - com.visioncameramlkit.domain.models.OutputOrientation - .fromString(options?.get("outputOrientation") as? String) - - protected abstract fun processFrame( - frame: Frame, - arguments: Map?, - ): HashMap - - override fun callback( - frame: Frame, - arguments: Map?, - ): HashMap { - // Process 1 frame, skip N frames, repeat - // Note: Consider using runAtTargetFps() from Vision Camera instead - if (frameProcessInterval > 0) { - frameCounter++ - if (frameCounter <= frameProcessInterval) { - return hashMapOf() - } - frameCounter = 0 - } - - return try { - processFrame(frame, arguments) - } catch (e: FrameInvalidError) { - Log.e(tag, "FrameInvalidError occurred while processing the frame", e) - hashMapOf() - } catch (e: MlKitException) { - Log.e(tag, "MlKitException occurred while processing the image with ML Kit", e) - hashMapOf() - } catch ( - @Suppress("TooGenericExceptionCaught") e: Exception, - ) { - Log.e(tag, "Unexpected error occurred during recognition", e) - hashMapOf() - } - } -} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/plugins/TextRecognitionPlugin.kt b/android/src/main/java/com/visioncameramlkit/bridge/plugins/TextRecognitionPlugin.kt deleted file mode 100644 index 808b48c..0000000 --- a/android/src/main/java/com/visioncameramlkit/bridge/plugins/TextRecognitionPlugin.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.visioncameramlkit.bridge.plugins - -import com.mrousavy.camera.frameprocessors.Frame -import com.mrousavy.camera.frameprocessors.VisionCameraProxy -import com.visioncameramlkit.application.usecases.RecognizeTextUseCase -import com.visioncameramlkit.domain.models.TextRecognitionLanguage -import com.visioncameramlkit.domain.models.TextRecognitionOptions -import com.visioncameramlkit.infrastructure.image.ImagePreprocessor -import com.visioncameramlkit.infrastructure.mlkit.MLKitTextRecognitionService -import com.visioncameramlkit.infrastructure.mlkit.factories.TextRecognitionServiceFactory -import com.visioncameramlkit.infrastructure.serializers.TextRecognitionSerializer - -@Suppress("unused") -class TextRecognitionPlugin( - proxy: VisionCameraProxy, - options: Map?, -) : BaseMLKitPlugin(proxy, options) { - override val tag = "TextRecognitionPlugin" - - private val textRecognitionOptions: TextRecognitionOptions - private val recognizeTextUseCase: RecognizeTextUseCase - - init { - val languageString = options?.get("language") as? String ?: "LATIN" - - val language = - try { - TextRecognitionLanguage.valueOf(languageString) - } catch ( - @Suppress("SwallowedException") _: IllegalArgumentException, - ) { - TextRecognitionLanguage.LATIN - } - - textRecognitionOptions = - TextRecognitionOptions( - language = language, - invertColors = invertColors, - outputOrientation = outputOrientation, - scaleFactor = scaleFactor, - ) - - val textRecognizer = TextRecognitionServiceFactory.createTextRecognizer(language) - val recognitionService = MLKitTextRecognitionService(textRecognizer) - val imagePreprocessor = ImagePreprocessor() - - recognizeTextUseCase = RecognizeTextUseCase(imagePreprocessor, recognitionService) - } - - override fun processFrame( - frame: Frame, - arguments: Map?, - ): HashMap { - // Android handles rotation automatically, so outputOrientation is ignored. Added for iOS parity. - // val outputOrientation = arguments?.get("outputOrientation") as? String - - val result = recognizeTextUseCase.execute(frame, textRecognitionOptions) - return TextRecognitionSerializer.toReactNativeMap(result) - } -} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt b/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt deleted file mode 100644 index 73d75de..0000000 --- a/android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.visioncameramlkit.bridge.registry - -import com.mrousavy.camera.frameprocessors.FrameProcessorPluginRegistry -import com.visioncameramlkit.BuildConfig -import com.visioncameramlkit.bridge.constants.MLKitFeatureKeys - -object PluginRegistry { - private val isRegistered by lazy { - registerPlugins() - true - } - - fun ensureRegistered() { - isRegistered - } - - private fun registerPlugins() { - @Suppress("KotlinConstantConditions") - if (BuildConfig.MLKIT_TEXT_RECOGNITION_ANY) { - registerTextRecognitionPlugin() - } - - @Suppress("KotlinConstantConditions") - if (BuildConfig.MLKIT_BARCODE_SCANNING) { - registerBarcodeScanningPlugin() - } - } - - private fun registerTextRecognitionPlugin() { - try { - val pluginClass = - Class.forName("com.visioncameramlkit.bridge.plugins.TextRecognitionPlugin") - - FrameProcessorPluginRegistry.addFrameProcessorPlugin( - MLKitFeatureKeys.TEXT_RECOGNITION, - ) { proxy, options -> - val constructor = - pluginClass.getConstructor( - com.mrousavy.camera.frameprocessors.VisionCameraProxy::class.java, - Map::class.java, - ) - constructor.newInstance( - proxy, - options, - ) as com.mrousavy.camera.frameprocessors.FrameProcessorPlugin - } - } catch (e: ClassNotFoundException) { - android.util.Log.w("VisionCameraMLKit", "TextRecognitionPlugin not available: ${e.message}") - } catch ( - @Suppress("TooGenericExceptionCaught") e: Exception, - ) { - android.util.Log.e("VisionCameraMLKit", "Error registering TextRecognitionPlugin", e) - } - } - - private fun registerBarcodeScanningPlugin() { - try { - val pluginClass = - Class.forName("com.visioncameramlkit.bridge.plugins.BarcodeScanningPlugin") - - FrameProcessorPluginRegistry.addFrameProcessorPlugin( - MLKitFeatureKeys.BARCODE_SCANNING, - ) { proxy, options -> - val constructor = - pluginClass.getConstructor( - com.mrousavy.camera.frameprocessors.VisionCameraProxy::class.java, - Map::class.java, - ) - constructor.newInstance( - proxy, - options, - ) as com.mrousavy.camera.frameprocessors.FrameProcessorPlugin - } - } catch (e: ClassNotFoundException) { - android.util.Log.w("VisionCameraMLKit", "BarcodeScanningPlugin not available: ${e.message}") - } catch ( - @Suppress("TooGenericExceptionCaught") e: Exception, - ) { - android.util.Log.e("VisionCameraMLKit", "Error registering BarcodeScanningPlugin", e) - } - } -} diff --git a/android/src/main/java/com/visioncameramlkit/domain/services/IImagePreprocessor.kt b/android/src/main/java/com/visioncameramlkit/domain/services/IImagePreprocessor.kt index 541358c..eb36774 100644 --- a/android/src/main/java/com/visioncameramlkit/domain/services/IImagePreprocessor.kt +++ b/android/src/main/java/com/visioncameramlkit/domain/services/IImagePreprocessor.kt @@ -1,16 +1,10 @@ package com.visioncameramlkit.domain.services -import com.mrousavy.camera.frameprocessors.Frame import com.visioncameramlkit.domain.models.ImagePreprocessingOptions import com.visioncameramlkit.domain.models.ProcessedImage import java.io.File interface IImagePreprocessor { - fun preprocessFrame( - frame: Frame, - options: ImagePreprocessingOptions, - ): ProcessedImage - fun preprocessImage( imageFile: File, options: ImagePreprocessingOptions, diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt index 4e169f6..6e1fefd 100644 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt @@ -8,12 +8,9 @@ import android.graphics.ColorMatrix import android.graphics.ColorMatrixColorFilter import android.graphics.Matrix import android.graphics.Paint -import android.media.Image import androidx.core.graphics.scale import androidx.exifinterface.media.ExifInterface import com.google.mlkit.vision.common.InputImage -import com.google.mlkit.vision.common.internal.ImageConvertUtils -import com.mrousavy.camera.frameprocessors.Frame import com.visioncameramlkit.domain.models.ImageMetadata import com.visioncameramlkit.domain.models.ImagePreprocessingOptions import com.visioncameramlkit.domain.models.Orientation @@ -29,80 +26,6 @@ class ImagePreprocessor : IImagePreprocessor { return scale.coerceIn(0.9f, 1.0f) } - override fun preprocessFrame( - frame: Frame, - options: ImagePreprocessingOptions, - ): ProcessedImage { - val effectiveScale = clampScale(options.scaleFactor) - - val inputImage = - if (options.invertColors) { - createInvertedInputImage(frame, effectiveScale) - } else { - createInputImage(frame, effectiveScale) - } - - val metadata = - ImageMetadata( - width = (frame.imageProxy.width * effectiveScale).toInt(), - height = (frame.imageProxy.height * effectiveScale).toInt(), - rotation = frame.imageProxy.imageInfo.rotationDegrees, - isInverted = options.invertColors, - ) - - return ProcessedImage(inputImage, metadata) - } - - private fun createInputImage( - frame: Frame, - scaleFactor: Float, - ): InputImage { - val mediaImage: Image = frame.image - - val image = InputImage.fromMediaImage(mediaImage, frame.imageProxy.imageInfo.rotationDegrees) - val frameBitmap = ImageConvertUtils.getInstance().getUpRightBitmap(image) - - val finalBitmap = - if (scaleFactor < 1.0f) { - frameBitmap.scale( - (frameBitmap.width * scaleFactor).toInt(), - (frameBitmap.height * scaleFactor).toInt(), - false, - ) - } else { - frameBitmap - } - - return InputImage.fromBitmap(finalBitmap, 0) - } - - private fun createInvertedInputImage( - frame: Frame, - scaleFactor: Float, - ): InputImage { - val mediaImage: Image = frame.image - - val image = InputImage.fromMediaImage(mediaImage, frame.imageProxy.imageInfo.rotationDegrees) - val frameBitmap = ImageConvertUtils.getInstance().getUpRightBitmap(image) - - // Scale first if needed - val scaledBitmap = - if (scaleFactor < 1.0f) { - frameBitmap.scale( - (frameBitmap.width * scaleFactor).toInt(), - (frameBitmap.height * scaleFactor).toInt(), - false, - ) - } else { - frameBitmap - } - - // Then invert - val invertedBitmap = invertBitmap(scaledBitmap) - - return InputImage.fromBitmap(invertedBitmap, 0) - } - private fun invertBitmap(bitmap: Bitmap): Bitmap = createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888).apply { val canvas = Canvas(this) diff --git a/example/babel.config.js b/example/babel.config.js index 9ab06a3..63495de 100644 --- a/example/babel.config.js +++ b/example/babel.config.js @@ -10,10 +10,7 @@ const workletsPluginOptions = {}; module.exports = getConfig( { presets: ['module:@react-native/babel-preset'], - plugins: [ - ['react-native-worklets-core/plugin'], - ['react-native-worklets/plugin', workletsPluginOptions], - ], + plugins: [['react-native-worklets/plugin', workletsPluginOptions]], }, { root, pkg } ); diff --git a/example/package.json b/example/package.json index e6672bc..86ac7f0 100644 --- a/example/package.json +++ b/example/package.json @@ -21,14 +21,16 @@ "react-native": "0.81.1", "react-native-gesture-handler": "^2.30.0", "react-native-image-picker": "^8.2.1", + "react-native-nitro-image": "0.15.0", + "react-native-nitro-modules": "0.36.1", "react-native-permissions": "^5.4.4", "react-native-reanimated": "^4.2.1", "react-native-safe-area-context": "^5.6.2", "react-native-screens": "^4.20.0", "react-native-svg": "^15.15.1", - "react-native-vision-camera": "^4.7.3", - "react-native-worklets": "^0.7.1", - "react-native-worklets-core": "^1.6.2", + "react-native-vision-camera": "^5.1.0", + "react-native-vision-camera-worklets": "^5.0.9", + "react-native-worklets": "0.7.1", "zustand": "^5.0.9" }, "devDependencies": { diff --git a/example/src/components/ui/CameraButton.tsx b/example/src/components/ui/CameraButton.tsx index 81f4c54..422e95d 100644 --- a/example/src/components/ui/CameraButton.tsx +++ b/example/src/components/ui/CameraButton.tsx @@ -7,7 +7,7 @@ import Reanimated, { withSpring, type SharedValue, } from 'react-native-reanimated'; -import type { Orientation } from 'react-native-vision-camera'; +import type { Orientation } from 'react-native-vision-camera-mlkit'; const ROTATION_MAP = { 'portrait': '0deg', diff --git a/example/src/components/ui/CameraControls.tsx b/example/src/components/ui/CameraControls.tsx index e2923b1..3b6fccc 100644 --- a/example/src/components/ui/CameraControls.tsx +++ b/example/src/components/ui/CameraControls.tsx @@ -9,7 +9,7 @@ import { } from 'lucide-react-native'; import { StyleSheet, View } from 'react-native'; import type { SharedValue } from 'react-native-reanimated'; -import type { Orientation } from 'react-native-vision-camera'; +import type { Orientation } from 'react-native-vision-camera-mlkit'; /** Defaults to dark theme for the CameraView */ import { darkTheme as theme } from '../../styles/themes'; diff --git a/example/src/components/views/CameraView.tsx b/example/src/components/views/CameraView.tsx index 38f28fe..01bc5e0 100644 --- a/example/src/components/views/CameraView.tsx +++ b/example/src/components/views/CameraView.tsx @@ -1,19 +1,25 @@ -import { forwardRef, useCallback, useState, type ComponentProps } from 'react'; +import { + forwardRef, + useCallback, + useMemo, + useState, + type ComponentProps, +} from 'react'; import { StyleSheet } from 'react-native'; import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import Reanimated, { useAnimatedStyle, useSharedValue, + type SharedValue, withSequence, withTiming, } from 'react-native-reanimated'; import { Camera, - runAsync, - runAtTargetFps, - Templates, - useCameraFormat, - useFrameProcessor, + CommonResolutions, + useAsyncRunner, + useFrameOutput, + type CameraRef, } from 'react-native-vision-camera'; import { useBarcodeScanning, @@ -21,10 +27,6 @@ import { type Orientation, } from 'react-native-vision-camera-mlkit'; import { scheduleOnRN } from 'react-native-worklets'; -import { - useRunOnJS, - type ISharedValue as SharedWorkletsValue, -} from 'react-native-worklets-core'; import { PLUGIN_ID } from '../../constants/PLUGINS'; import { useTheme } from '../../providers/ThemeProvider'; import { @@ -33,8 +35,6 @@ import { useTerminalStore, } from '../../stores'; -const ReanimatedCamera = Reanimated.createAnimatedComponent(Camera); - const normalizeResultObject = (data: unknown) => { if (data === null || data === undefined) return data; @@ -51,11 +51,10 @@ type CameraViewProps = ComponentProps & { pluginId: PLUGIN_ID; flipCamera: () => void; isFrameProcessorEnabled?: boolean; - frameOutputOrientation: SharedWorkletsValue; - onOutputOrientationChangedCallback: (o: Orientation) => void; + frameOutputOrientation: SharedValue; }; -const CameraView = forwardRef( +const CameraView = forwardRef( ( { device, @@ -64,7 +63,7 @@ const CameraView = forwardRef( flipCamera, isFrameProcessorEnabled = true, frameOutputOrientation, - onOutputOrientationChangedCallback, + torchMode, ...props }, ref @@ -79,7 +78,6 @@ const CameraView = forwardRef( } = useSettingsStore(); const { sharedOptions, pluginOptions } = usePluginOptionsStore(); - // Initialize plugins with options from store const textRecognitionPlugin = useTextRecognition({ language: pluginOptions[PLUGIN_ID.TEXT_RECOGNITION].language, ...sharedOptions, @@ -96,14 +94,19 @@ const CameraView = forwardRef( const scale = useSharedValue(0); const opacity = useSharedValue(0); + const frameCount = useSharedValue(0); + const asyncRunner = useAsyncRunner(); - const format = useCameraFormat(device, Templates.FrameProcessing); + const frameInterval = useMemo( + () => Math.max(1, Math.round(30 / frameProcessorFps)), + [frameProcessorFps] + ); const focus = useCallback( (point: { x: number; y: number }) => { if (typeof ref === 'function' || !ref?.current) return; - ref.current.focus(point); + ref.current.focusTo(point).catch(console.error); setFocusPoint(point); scale.value = 0; @@ -144,26 +147,35 @@ const CameraView = forwardRef( transform: [{ scale: scale.value }], })); - const handleResultWorklet = useRunOnJS((data: unknown) => { + const addResult = useCallback((data: unknown) => { console.log(data); useTerminalStore .getState() .addEntry(normalizeResultObject(data), 'camera'); }, []); - const frameProcessor = useFrameProcessor( - (frame) => { + const frameOutput = useFrameOutput({ + targetResolution: CommonResolutions.VGA_16_9, + pixelFormat, + onFrame(frame) { 'worklet'; - runAtTargetFps(frameProcessorFps, () => { - 'worklet'; + if (!isFrameProcessorEnabled) { + frame.dispose(); + return; + } - runAsync(frame, () => { - 'worklet'; + frameCount.value = (frameCount.value + 1) % frameInterval; + if (frameCount.value !== 0) { + frame.dispose(); + return; + } - let resultObject: any = null; + const accepted = asyncRunner.runAsync(() => { + 'worklet'; - // Shared arguments for all plugins + try { + let resultObject: any = null; const withArguments = { outputOrientation: frameOutputOrientation.value, }; @@ -181,20 +193,18 @@ const CameraView = forwardRef( } if (resultObject) { - handleResultWorklet(resultObject); + scheduleOnRN(addResult, resultObject); } - }); + } finally { + frame.dispose(); + } }); + + if (!accepted) { + frame.dispose(); + } }, - [ - frameProcessorFps, - pluginId, - frameOutputOrientation, - textRecognitionPlugin, - barcodeScanningPlugin, - handleResultWorklet, - ] - ); + }); return ( @@ -203,23 +213,18 @@ const CameraView = forwardRef( accessibilityLabel="Camera viewfinder" accessibilityHint="Tap to focus, double tap to flip camera" > - {focusPoint && ( { const { params, name } = useRoute>(); const { open: openTerminal } = useTerminal(); - const cameraRef = useRef(null); + const cameraRef = useRef(null); const [cameraPosition, setCameraPosition] = useState('back'); const [torchState, setTorchState] = useState('off'); - const [isFpsGraphEnabled, setIsFpsGraphEnabled] = useState(false); + const [, setIsFpsGraphEnabled] = useState(false); - const frameOutputOrientation = - useWorkletsSharedValue(DEFAULT_ORIENTATION); const outputOrientation = useSharedValue(DEFAULT_ORIENTATION); const { isFrameProcessorEnabled } = useSettingsStore(); @@ -62,14 +59,6 @@ const CameraScreen = () => { const isActive = isFocused && appState === 'active'; - const onOutputOrientationChangedCallback = useCallback( - (o: Orientation) => { - outputOrientation.value = o; - frameOutputOrientation.value = o; - }, - [outputOrientation, frameOutputOrientation] - ); - const flipCamera = () => setCameraPosition((prev) => (prev === 'back' ? 'front' : 'back')); @@ -100,10 +89,8 @@ const CameraScreen = () => { pluginId={params.id} flipCamera={flipCamera} isFrameProcessorEnabled={isFrameProcessorEnabled} - frameOutputOrientation={frameOutputOrientation} - onOutputOrientationChangedCallback={onOutputOrientationChangedCallback} - torch={torchState} - enableFpsGraph={isFpsGraphEnabled} + frameOutputOrientation={outputOrientation} + torchMode={torchState} /> BarcodeScannerResult { + throw RuntimeError.error(withMessage: "VisionCamera v5 barcode frame processing is not implemented yet.") + } +} diff --git a/ios/Bridge/Hybrid/HybridTextRecognizer.swift b/ios/Bridge/Hybrid/HybridTextRecognizer.swift new file mode 100644 index 0000000..31cba26 --- /dev/null +++ b/ios/Bridge/Hybrid/HybridTextRecognizer.swift @@ -0,0 +1,15 @@ +import Foundation +import NitroModules +import VisionCamera + +final class HybridTextRecognizer: HybridTextRecognizerSpec { + private let options: TextRecognizerOptions + + init(options: TextRecognizerOptions) { + self.options = options + } + + func recognize(frame: any HybridFrameSpec, args: MLKitBaseArguments?) throws -> TextRecognitionResult { + throw RuntimeError.error(withMessage: "VisionCamera v5 text recognition frame processing is not implemented yet.") + } +} diff --git a/ios/Bridge/Hybrid/HybridVisionCameraMLKit.swift b/ios/Bridge/Hybrid/HybridVisionCameraMLKit.swift new file mode 100644 index 0000000..ef2e725 --- /dev/null +++ b/ios/Bridge/Hybrid/HybridVisionCameraMLKit.swift @@ -0,0 +1,75 @@ +import Foundation +import NitroModules + +final class HybridVisionCameraMLKit: HybridVisionCameraMLKitSpec { + func isFeatureAvailable(feature: MLKitFeature) throws -> Bool { + switch feature { + case .textrecognition: + #if MLKIT_TEXT_RECOGNITION_ANY + return true + #else + return false + #endif + case .barcodescanning: + #if MLKIT_BARCODE_SCANNING + return true + #else + return false + #endif + case .facedetection: + #if MLKIT_FACE_DETECTION + return true + #else + return false + #endif + case .posedetection: + #if MLKIT_POSE_DETECTION + return true + #else + return false + #endif + case .selfiesegmentation: + #if MLKIT_SELFIE_SEGMENTATION + return true + #else + return false + #endif + case .imagelabeling: + #if MLKIT_IMAGE_LABELING + return true + #else + return false + #endif + case .objectdetection: + #if MLKIT_OBJECT_DETECTION + return true + #else + return false + #endif + case .digitalinkrecognition: + #if MLKIT_DIGITAL_INK_RECOGNITION + return true + #else + return false + #endif + case .facemeshdetection, .subjectsegmentation, .documentscanner: + return false + } + } + + func createTextRecognizer(options: TextRecognizerOptions) throws -> any HybridTextRecognizerSpec { + guard try isFeatureAvailable(feature: .textrecognition) else { + throw RuntimeError.error(withMessage: "TextRecognition is not enabled in the native ML Kit configuration.") + } + + return HybridTextRecognizer(options: options) + } + + func createBarcodeScanner(options: BarcodeScannerOptions) throws -> any HybridBarcodeScannerSpec { + guard try isFeatureAvailable(feature: .barcodescanning) else { + throw RuntimeError.error(withMessage: "BarcodeScanning is not enabled in the native ML Kit configuration.") + } + + return HybridBarcodeScanner(options: options) + } +} diff --git a/ios/Bridge/Plugins/BarcodeScanningPlugin.swift b/ios/Bridge/Plugins/BarcodeScanningPlugin.swift deleted file mode 100644 index 52b14de..0000000 --- a/ios/Bridge/Plugins/BarcodeScanningPlugin.swift +++ /dev/null @@ -1,50 +0,0 @@ -import Foundation - -#if MLKIT_BARCODE_SCANNING - #if canImport(VisionCamera) - import VisionCamera - - @objc(BarcodeScanningPlugin) - public class BarcodeScanningPlugin: BaseMLKitPlugin { - - private lazy var recognitionOptions: BarcodeScanningOptions = { - BarcodeScanningOptions( - formats: BarcodeScanningOptionParser.parseFormats(self.options?["formats"]), - enableAllPotentialBarcodes: BarcodeScanningOptionParser.parseEnableAllPotentialBarcodes( - self.options?["enableAllPotentialBarcodes"] - ), - invertColors: self.invertColors, - scaleFactor: self.scaleFactor - ) - }() - - private lazy var recognizeBarcodesUseCase: RecognizeBarcodesUseCase = { - let recognitionService = BarcodeScanningServiceFactory.create( - options: self.recognitionOptions) - let imagePreprocessor = ImagePreprocessor() - - return RecognizeBarcodesUseCase( - imagePreprocessor: imagePreprocessor, - recognitionService: recognitionService - ) - }() - - public override func processFrame( - _ frame: Frame, - withArguments arguments: [AnyHashable: Any]? - ) throws -> [String: Any] { - let options = BarcodeScanningOptions( - formats: recognitionOptions.formats, - enableAllPotentialBarcodes: recognitionOptions.enableAllPotentialBarcodes, - invertColors: recognitionOptions.invertColors, - outputOrientation: outputOrientation, - scaleFactor: recognitionOptions.scaleFactor - ) - - _ = arguments - let result = try recognizeBarcodesUseCase.execute(frame: frame, options: options) - return BarcodeScanningSerializer.toReactNativeMap(result) - } - } - #endif -#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Bridge/Plugins/BaseMLKitPlugin.swift b/ios/Bridge/Plugins/BaseMLKitPlugin.swift deleted file mode 100644 index 6025924..0000000 --- a/ios/Bridge/Plugins/BaseMLKitPlugin.swift +++ /dev/null @@ -1,74 +0,0 @@ -import Foundation - -#if canImport(VisionCamera) - import VisionCamera - - @objc - public class BaseMLKitPlugin: FrameProcessorPlugin { - - let proxy: VisionCameraProxyHolder - let options: [AnyHashable: Any]? - - private let frameProcessInterval: Int - private var frameCounter: Int = 0 - let invertColors: Bool - let scaleFactor: CGFloat - - var outputOrientation: OutputOrientation? - - public override init( - proxy: VisionCameraProxyHolder, - options: [AnyHashable: Any]? - ) { - self.proxy = proxy - self.options = options - self.frameProcessInterval = options?["frameProcessInterval"] as? Int ?? 0 - self.invertColors = options?["invertColors"] as? Bool ?? false - - let sf = options?["scaleFactor"] as? NSNumber - let sfValue = sf?.doubleValue ?? 1.0 - - self.scaleFactor = min(max(CGFloat(sfValue), 0.9), 1.0) - - super.init(proxy: proxy, options: options) - } - - public override func callback( - _ frame: Frame, - withArguments arguments: [AnyHashable: Any]? - ) -> Any { - // Extract outputOrientation from arguments for rotation support - outputOrientation = OutputOrientation( - string: arguments?["outputOrientation"] as? String - ) - - // Process 1 frame, skip N frames, repeat - // Note: Consider using runAtTargetFps() from Vision Camera instead - if frameProcessInterval > 0 { - frameCounter += 1 - if frameCounter <= frameProcessInterval { - return [:] - } - frameCounter = 0 - } - - do { - return try processFrame(frame, withArguments: arguments) - } catch { - print("[\(pluginName)] Error occurred: \(error.localizedDescription)") - return [:] - } - } - - public func processFrame( - _ frame: Frame, - withArguments arguments: [AnyHashable: Any]? - ) throws -> [String: Any] { - fatalError("processFrame must be overridden by subclass") - } - - var pluginName: String { - return String(describing: type(of: self)) - } - } -#endif diff --git a/ios/Bridge/Plugins/TextRecognitionPlugin.swift b/ios/Bridge/Plugins/TextRecognitionPlugin.swift deleted file mode 100644 index 2136941..0000000 --- a/ios/Bridge/Plugins/TextRecognitionPlugin.swift +++ /dev/null @@ -1,68 +0,0 @@ -import Foundation - -#if MLKIT_TEXT_RECOGNITION_ANY - #if canImport(VisionCamera) - import VisionCamera - - @objc(TextRecognitionPlugin) - public class TextRecognitionPlugin: BaseMLKitPlugin { - - private let language: TextRecognitionLanguage - - private lazy var recognitionOptions: TextRecognitionOptions = { - TextRecognitionOptions( - language: self.language, - invertColors: self.invertColors, - scaleFactor: self.scaleFactor - ) - }() - - private lazy var recognizeTextUseCase: RecognizeTextUseCase = { - let textRecognizer = TextRecognitionServiceFactory.createTextRecognizer( - language: self.language - ) - let recognitionService = MLKitTextRecognitionService( - textRecognizer: textRecognizer - ) - let imagePreprocessor = ImagePreprocessor() - - return RecognizeTextUseCase( - imagePreprocessor: imagePreprocessor, - recognitionService: recognitionService - ) - }() - - @objc - public override init( - proxy: VisionCameraProxyHolder, - options: [AnyHashable: Any]? = [:] - ) { - let languageString = options?["language"] as? String ?? "LATIN" - self.language = - TextRecognitionLanguage(rawValue: languageString) ?? .latin - - super.init(proxy: proxy, options: options) - } - - public override func processFrame( - _ frame: Frame, - withArguments arguments: [AnyHashable: Any]? - ) throws -> [String: Any] { - let options = TextRecognitionOptions( - language: recognitionOptions.language, - invertColors: recognitionOptions.invertColors, - outputOrientation: outputOrientation, - scaleFactor: recognitionOptions.scaleFactor - ) - - let result = try recognizeTextUseCase.execute( - frame: frame, - options: options - ) - - return TextRecognitionSerializer.toReactNativeMap(result) - } - } - - #endif -#endif // MLKIT_TEXT_RECOGNITION_ANY diff --git a/ios/Bridge/Registry/PluginRegistry.swift b/ios/Bridge/Registry/PluginRegistry.swift deleted file mode 100644 index dde1201..0000000 --- a/ios/Bridge/Registry/PluginRegistry.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation - -#if canImport(VisionCamera) - import VisionCamera - - @objc - public class PluginRegistry: NSObject { - - @objc - public static func registerTextRecognitionPlugin() { - // Plugin registration is handled by the Objective-C bridge in VisionCameraMLKit.mm - // This class exists for consistency with Android architecture - } - } -#endif diff --git a/ios/VisionCameraMLKit-Bridging-Header.h b/ios/VisionCameraMLKit-Bridging-Header.h deleted file mode 100644 index 3fdf142..0000000 --- a/ios/VisionCameraMLKit-Bridging-Header.h +++ /dev/null @@ -1,4 +0,0 @@ -#if VISION_CAMERA_ENABLE_FRAME_PROCESSORS -#import -#import -#endif diff --git a/ios/VisionCameraMLKit.mm b/ios/VisionCameraMLKit.mm deleted file mode 100644 index b39551d..0000000 --- a/ios/VisionCameraMLKit.mm +++ /dev/null @@ -1,41 +0,0 @@ -#import -#import -#import -#import -#import - -#if __has_include("VisionCameraMLKit/VisionCameraMLKit-Swift.h") -#import "VisionCameraMLKit/VisionCameraMLKit-Swift.h" -#else -#import "VisionCameraMLKit-Swift.h" -#endif - -@interface RCT_EXTERN_MODULE (VisionCameraMLKitModule, NSObject) -RCT_EXTERN_METHOD(processImage : (NSString *)feature path : ( - NSString *)path options : (id)options resolver : (RCTPromiseResolveBlock) - resolve rejecter : (RCTPromiseRejectBlock)reject) -@end - -#define REGISTER_PLUGIN(PLUGIN_CLASS, PLUGIN_NAME) \ - @interface PLUGIN_CLASS (FrameProcessorPluginLoader) \ - @end \ - \ - @implementation PLUGIN_CLASS (FrameProcessorPluginLoader) \ - +(void)load { \ - [FrameProcessorPluginRegistry \ - addFrameProcessorPlugin:PLUGIN_NAME \ - withInitializer:^FrameProcessorPlugin *( \ - VisionCameraProxyHolder * proxy, NSDictionary * options) { \ - return [[PLUGIN_CLASS alloc] initWithProxy:proxy \ - withOptions:options]; \ - }]; \ - } \ - @end - -#ifdef MLKIT_TEXT_RECOGNITION_ANY -REGISTER_PLUGIN(TextRecognitionPlugin, @"TextRecognition") -#endif - -#ifdef MLKIT_BARCODE_SCANNING -REGISTER_PLUGIN(BarcodeScanningPlugin, @"BarcodeScanning") -#endif diff --git a/nitro.json b/nitro.json new file mode 100644 index 0000000..ea6151d --- /dev/null +++ b/nitro.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://nitro.margelo.com/nitro.schema.json", + "cxxNamespace": ["visioncameramlkit"], + "ios": { + "iosModuleName": "VisionCameraMLKit" + }, + "android": { + "androidNamespace": ["visioncameramlkit"], + "androidCxxLibName": "VisionCameraMLKit" + }, + "autolinking": { + "VisionCameraMLKit": { + "ios": { + "language": "swift", + "implementationClassName": "HybridVisionCameraMLKit" + }, + "android": { + "language": "kotlin", + "implementationClassName": "HybridVisionCameraMLKit" + } + } + }, + "ignorePaths": ["**/example/**"], + "gitAttributesGeneratedFlag": true +} diff --git a/package.json b/package.json index f7ffed9..b13e4aa 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,8 @@ "android", "ios", "cpp", + "nitrogen", + "nitro.json", "*.podspec", "react-native.config.js", "!ios/build", @@ -37,6 +39,7 @@ "prepare": "bob build", "typecheck": "tsc --noEmit", "lint": "eslint \"**/*.{js,ts,tsx}\"", + "specs": "nitrogen", "release": "release-it --only-version", "test": "jest" }, @@ -76,6 +79,7 @@ "registry": "https://registry.npmjs.org/" }, "devDependencies": { + "@babel/core": "^7.25.2", "@commitlint/config-conventional": "^19.8.1", "@eslint/compat": "^1.3.2", "@eslint/eslintrc": "^3.3.1", @@ -93,12 +97,16 @@ "eslint-plugin-prettier": "^5.5.4", "jest": "^29.7.0", "lefthook": "^2.0.3", + "nitrogen": "0.36.1", "prettier": "^2.8.8", "react": "19.1.0", "react-native": "0.81.1", "react-native-builder-bob": "^0.40.13", - "react-native-vision-camera": "^4.7.3", - "react-native-worklets-core": "^1.6.2", + "react-native-nitro-image": "0.15.0", + "react-native-nitro-modules": "0.36.1", + "react-native-vision-camera": "^5.1.0", + "react-native-vision-camera-worklets": "^5.0.9", + "react-native-worklets": "0.7.1", "release-it": "^19.0.4", "turbo": "^2.5.6", "typescript": "^5.9.2" @@ -106,8 +114,11 @@ "peerDependencies": { "react": "*", "react-native": "*", - "react-native-vision-camera": "*", - "react-native-worklets-core": "*" + "react-native-nitro-image": "*", + "react-native-nitro-modules": "*", + "react-native-vision-camera": ">=5", + "react-native-vision-camera-worklets": "*", + "react-native-worklets": "*" }, "workspaces": [ "example" diff --git a/src/core/PluginFactory.ts b/src/core/PluginFactory.ts index 6c17b5c..f1c39d9 100644 --- a/src/core/PluginFactory.ts +++ b/src/core/PluginFactory.ts @@ -1,7 +1,9 @@ import { Platform } from 'react-native'; -import { VisionCameraProxy } from 'react-native-vision-camera'; -import { FEATURE_ERROR_MAP, LINKING_ERROR } from '../shared/constants'; +import { FEATURE_ERROR_MAP } from '../shared/constants'; +import type { BarcodeScanningOptions } from '../features/barcode-scanning/types'; +import type { TextRecognitionOptions } from '../features/text-recognition/types'; import { MLKIT_FEATURE_KEYS } from './constants'; +import { VisionCameraMLKit } from './VisionCameraMLKit'; import type { MLKitBaseOptions, MLKitFeature } from './types'; /** @@ -72,13 +74,18 @@ buildscript { static initPlugin(feature: MLKitFeature, options: MLKitBaseOptions) { PluginFactory.assertFeatureAvailable(feature); - const plugin = VisionCameraProxy.initFrameProcessorPlugin(feature, options); - - if (!plugin) { - throw new Error(LINKING_ERROR); + switch (feature) { + case MLKIT_FEATURE_KEYS.TEXT_RECOGNITION: + return VisionCameraMLKit.createTextRecognizer( + options as TextRecognitionOptions + ); + case MLKIT_FEATURE_KEYS.BARCODE_SCANNING: + return VisionCameraMLKit.createBarcodeScanner( + options as BarcodeScanningOptions + ); + default: + throw new Error(PluginFactory.getFeatureErrorMessage(feature)); } - - return plugin; } /** @@ -88,8 +95,7 @@ buildscript { */ static isFeatureAvailable(feature: MLKitFeature): boolean { try { - const plugin = VisionCameraProxy.initFrameProcessorPlugin(feature, {}); - return plugin !== undefined && plugin !== null; + return VisionCameraMLKit.isFeatureAvailable(feature); } catch { return false; } diff --git a/src/core/VisionCameraMLKit.ts b/src/core/VisionCameraMLKit.ts new file mode 100644 index 0000000..0fcb2cc --- /dev/null +++ b/src/core/VisionCameraMLKit.ts @@ -0,0 +1,5 @@ +import { NitroModules } from 'react-native-nitro-modules'; +import type { VisionCameraMLKit as VisionCameraMLKitSpec } from '../specs/VisionCameraMLKit.nitro'; + +export const VisionCameraMLKit = + NitroModules.createHybridObject('VisionCameraMLKit'); diff --git a/src/features/barcode-scanning/types.ts b/src/features/barcode-scanning/types.ts index 52c81fe..b718ed1 100644 --- a/src/features/barcode-scanning/types.ts +++ b/src/features/barcode-scanning/types.ts @@ -121,109 +121,159 @@ export type BarcodeAddressData = { typeName?: string | null; }; +export type BarcodeWifiData = { + ssid?: string | null; + password?: string | null; + encryptionType?: number | null; + encryptionTypeName?: string | null; + type?: number | string | null; + typeName?: string | null; +}; + +export type BarcodeUrlData = { + title?: string | null; + url?: string | null; +}; + +export type BarcodeSmsData = { + message?: string | null; + phoneNumber?: string | null; +}; + +export type BarcodeGeoData = { + lat?: number | null; + lng?: number | null; +}; + +export type BarcodeCalendarEventData = { + summary?: string | null; + description?: string | null; + location?: string | null; + organizer?: string | null; + status?: string | null; + start?: BarcodeCalendarDateTime | null; + end?: BarcodeCalendarDateTime | null; +}; + +export type BarcodeContactInfoData = { + name?: BarcodePersonName | null; + organization?: string | null; + title?: string | null; + phones?: BarcodePhoneData[] | null; + emails?: BarcodeEmailData[] | null; + addresses?: BarcodeAddressData[] | null; + urls?: string[] | null; +}; + +export type BarcodeDriverLicenseData = { + documentType?: string | null; + licenseNumber?: string | null; + firstName?: string | null; + middleName?: string | null; + lastName?: string | null; + gender?: string | number | null; + addressStreet?: string | null; + addressCity?: string | null; + addressState?: string | null; + addressZip?: string | null; + birthDate?: string | null; + issueDate?: string | null; + expiryDate?: string | null; + issuingCountry?: string | null; +}; + +export type BarcodeTextData = { + text?: string | null; +}; + +export type BarcodeProductData = { + product?: string | null; +}; + +export type BarcodeIsbnData = { + isbn?: string | null; +}; + +export type BarcodeUnknownData = { + rawValue?: string | null; +}; + +export type BarcodeWifiParsedValue = { + type: 'TYPE_WIFI'; + data: BarcodeWifiData; +}; + +export type BarcodeUrlParsedValue = { + type: 'TYPE_URL'; + data: BarcodeUrlData; +}; + +export type BarcodeSmsParsedValue = { + type: 'TYPE_SMS'; + data: BarcodeSmsData; +}; + +export type BarcodeEmailParsedValue = { + type: 'TYPE_EMAIL'; + data: BarcodeEmailData; +}; + +export type BarcodePhoneParsedValue = { + type: 'TYPE_PHONE'; + data: BarcodePhoneData; +}; + +export type BarcodeGeoParsedValue = { + type: 'TYPE_GEO'; + data: BarcodeGeoData; +}; + +export type BarcodeCalendarParsedValue = { + type: 'TYPE_CALENDAR_EVENT'; + data: BarcodeCalendarEventData; +}; + +export type BarcodeContactParsedValue = { + type: 'TYPE_CONTACT_INFO'; + data: BarcodeContactInfoData; +}; + +export type BarcodeDriverLicenseParsedValue = { + type: 'TYPE_DRIVER_LICENSE'; + data: BarcodeDriverLicenseData; +}; + +export type BarcodeTextParsedValue = { + type: 'TYPE_TEXT'; + data: BarcodeTextData; +}; + +export type BarcodeProductParsedValue = { + type: 'TYPE_PRODUCT'; + data: BarcodeProductData; +}; + +export type BarcodeIsbnParsedValue = { + type: 'TYPE_ISBN'; + data: BarcodeIsbnData; +}; + +export type BarcodeUnknownParsedValue = { + type: 'TYPE_UNKNOWN'; + data: BarcodeUnknownData; +}; + export type BarcodeParsedValue = - | { - type: 'TYPE_WIFI'; - data: { - ssid?: string | null; - password?: string | null; - encryptionType?: number | null; - encryptionTypeName?: string | null; - type?: number | string | null; - typeName?: string | null; - }; - } - | { - type: 'TYPE_URL'; - data: { - title?: string | null; - url?: string | null; - }; - } - | { - type: 'TYPE_SMS'; - data: { - message?: string | null; - phoneNumber?: string | null; - }; - } - | { - type: 'TYPE_EMAIL'; - data: BarcodeEmailData; - } - | { - type: 'TYPE_PHONE'; - data: BarcodePhoneData; - } - | { - type: 'TYPE_GEO'; - data: { - lat?: number | null; - lng?: number | null; - }; - } - | { - type: 'TYPE_CALENDAR_EVENT'; - data: { - summary?: string | null; - description?: string | null; - location?: string | null; - organizer?: string | null; - status?: string | null; - start?: BarcodeCalendarDateTime | null; - end?: BarcodeCalendarDateTime | null; - }; - } - | { - type: 'TYPE_CONTACT_INFO'; - data: { - name?: BarcodePersonName | null; - organization?: string | null; - title?: string | null; - phones?: BarcodePhoneData[] | null; - emails?: BarcodeEmailData[] | null; - addresses?: BarcodeAddressData[] | null; - urls?: string[] | null; - }; - } - | { - type: 'TYPE_DRIVER_LICENSE'; - data: { - documentType?: string | null; - licenseNumber?: string | null; - firstName?: string | null; - middleName?: string | null; - lastName?: string | null; - gender?: string | number | null; - addressStreet?: string | null; - addressCity?: string | null; - addressState?: string | null; - addressZip?: string | null; - birthDate?: string | null; - issueDate?: string | null; - expiryDate?: string | null; - issuingCountry?: string | null; - }; - } - | { - type: 'TYPE_TEXT'; - data: { - text?: string | null; - }; - } - | { - type: 'TYPE_PRODUCT'; - data: { - product?: string | null; - }; - } - | { - type: 'TYPE_ISBN'; - data: { - isbn?: string | null; - }; - } - | { - type: 'TYPE_UNKNOWN'; - data: Record; - }; + | BarcodeWifiParsedValue + | BarcodeUrlParsedValue + | BarcodeSmsParsedValue + | BarcodeEmailParsedValue + | BarcodePhoneParsedValue + | BarcodeGeoParsedValue + | BarcodeCalendarParsedValue + | BarcodeContactParsedValue + | BarcodeDriverLicenseParsedValue + | BarcodeTextParsedValue + | BarcodeProductParsedValue + | BarcodeIsbnParsedValue + | BarcodeUnknownParsedValue; diff --git a/src/hooks/useMLKitPlugin.ts b/src/hooks/useMLKitPlugin.ts index 77439ab..345b6ab 100644 --- a/src/hooks/useMLKitPlugin.ts +++ b/src/hooks/useMLKitPlugin.ts @@ -27,7 +27,7 @@ export const useMLKitPlugin = ( return { recognize: (frame: Frame, args?: MLKitBaseArguments): any => { 'worklet'; - return plugin.call(frame, args ?? {}); + return plugin.recognize(frame, args ?? {}); }, }; }, [feature, options]); diff --git a/src/index.ts b/src/index.ts index 480d648..bd55070 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,7 @@ import { PluginFactory } from './core/PluginFactory'; +export { VisionCameraMLKit } from './core/VisionCameraMLKit'; + export const getFeatureErrorMessage = PluginFactory.getFeatureErrorMessage; export const isFeatureAvailable = PluginFactory.isFeatureAvailable; export const assertFeatureAvailable = PluginFactory.assertFeatureAvailable; @@ -25,5 +27,18 @@ export { type Orientation, } from './core/types'; +export type { BarcodeScanner } from './specs/BarcodeScanner.nitro'; +export type { + BarcodeScannerBarcode, + BarcodeScannerResult, +} from './specs/BarcodeScannerTypes'; +export type { + BarcodeScannerOptions, + MLKitRecognizerOptions, + TextRecognizerOptions, +} from './specs/MLKitVisionTypes'; +export type { TextRecognizer } from './specs/TextRecognizer.nitro'; +export type { VisionCameraMLKit as VisionCameraMLKitSpec } from './specs/VisionCameraMLKit.nitro'; + export * from './features/barcode-scanning'; export * from './features/text-recognition'; diff --git a/src/specs/BarcodeScanner.nitro.ts b/src/specs/BarcodeScanner.nitro.ts new file mode 100644 index 0000000..dfc9acf --- /dev/null +++ b/src/specs/BarcodeScanner.nitro.ts @@ -0,0 +1,20 @@ +import type { HybridObject } from 'react-native-nitro-modules'; +import type { Frame } from 'react-native-vision-camera/lib/specs/instances/Frame.nitro'; +import type { BarcodeScanningArguments } from '../features/barcode-scanning/types'; +import type { BarcodeScannerResult } from './BarcodeScannerTypes'; + +/** + * Scans barcodes from live VisionCamera frames. + * + * @see {@linkcode VisionCameraMLKit.createBarcodeScanner} + */ +export interface BarcodeScanner + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { + /** + * Runs ML Kit barcode scanning on a single camera frame. + */ + recognize( + frame: Frame, + args?: BarcodeScanningArguments + ): BarcodeScannerResult; +} diff --git a/src/specs/BarcodeScannerTypes.ts b/src/specs/BarcodeScannerTypes.ts new file mode 100644 index 0000000..216f7c5 --- /dev/null +++ b/src/specs/BarcodeScannerTypes.ts @@ -0,0 +1,70 @@ +import type { BoundingBox, Corner } from '../core/types'; +import type { + BarcodeFormat, + BarcodeValueTypeName, +} from '../features/barcode-scanning/types'; + +/** + * Codegen-safe barcode data returned by the live Nitro scanner. + */ +export interface BarcodeScannerBarcode { + /** + * Barcode bounds in frame coordinates when available. + */ + bounds?: BoundingBox; + + /** + * Barcode corner points in frame coordinates when available. + */ + corners?: Corner[]; + + /** + * Native ML Kit barcode format integer. + */ + format: number; + + /** + * Named ML Kit barcode format. + */ + formatName: BarcodeFormat; + + /** + * Native ML Kit value type integer. + */ + valueType: number; + + /** + * Named ML Kit value type. + */ + valueTypeName: BarcodeValueTypeName; + + /** + * Raw barcode payload when ML Kit decoded it. + */ + rawValue?: string; + + /** + * Display-ready barcode payload when ML Kit provides one. + */ + displayValue?: string; + + /** + * Raw byte payload when available. + */ + rawBytes?: number[]; + + /** + * Whether this is an undecoded potential barcode. + */ + isPotential: boolean; +} + +/** + * Codegen-safe live barcode scanning result. + */ +export interface BarcodeScannerResult { + /** + * Barcodes detected in the frame. + */ + barcodes: BarcodeScannerBarcode[]; +} diff --git a/src/specs/MLKitVisionTypes.ts b/src/specs/MLKitVisionTypes.ts new file mode 100644 index 0000000..17e06db --- /dev/null +++ b/src/specs/MLKitVisionTypes.ts @@ -0,0 +1,63 @@ +import type { BarcodeFormat } from '../features/barcode-scanning/types'; +import type { TextRecognitionLanguage } from '../features/text-recognition/types'; + +/** + * ML Kit features that can be compiled into this package. + */ +export type MLKitFeature = + | 'TextRecognition' + | 'FaceDetection' + | 'FaceMeshDetection' + | 'PoseDetection' + | 'SelfieSegmentation' + | 'SubjectSegmentation' + | 'DocumentScanner' + | 'BarcodeScanning' + | 'ImageLabeling' + | 'ObjectDetection' + | 'DigitalInkRecognition'; + +/** + * Shared options for live frame recognizers. + */ +export interface MLKitRecognizerOptions { + /** + * Optional image downscaling for performance optimization. + */ + scaleFactor?: number; + + /** + * Invert colors before processing. + */ + invertColors?: boolean; + + /** + * Legacy frame skipping option kept for compatibility while v5 examples use frame output throttling. + */ + frameProcessInterval?: number; +} + +/** + * Options for creating a configured text recognizer. + */ +export interface TextRecognizerOptions extends MLKitRecognizerOptions { + /** + * Language model used by ML Kit text recognition. + */ + language?: TextRecognitionLanguage; +} + +/** + * Options for creating a configured barcode scanner. + */ +export interface BarcodeScannerOptions extends MLKitRecognizerOptions { + /** + * Restricts detected barcode formats. + */ + formats?: BarcodeFormat[]; + + /** + * Android-only option for returning potential barcodes. + */ + enableAllPotentialBarcodes?: boolean; +} diff --git a/src/specs/TextRecognizer.nitro.ts b/src/specs/TextRecognizer.nitro.ts new file mode 100644 index 0000000..4109dce --- /dev/null +++ b/src/specs/TextRecognizer.nitro.ts @@ -0,0 +1,22 @@ +import type { HybridObject } from 'react-native-nitro-modules'; +import type { Frame } from 'react-native-vision-camera/lib/specs/instances/Frame.nitro'; +import type { + TextRecognitionArguments, + TextRecognitionResult, +} from '../features/text-recognition/types'; + +/** + * Recognizes text from live VisionCamera frames. + * + * @see {@linkcode VisionCameraMLKit.createTextRecognizer} + */ +export interface TextRecognizer + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { + /** + * Runs ML Kit text recognition on a single camera frame. + */ + recognize( + frame: Frame, + args?: TextRecognitionArguments + ): TextRecognitionResult; +} diff --git a/src/specs/VisionCameraMLKit.nitro.ts b/src/specs/VisionCameraMLKit.nitro.ts new file mode 100644 index 0000000..8f262c0 --- /dev/null +++ b/src/specs/VisionCameraMLKit.nitro.ts @@ -0,0 +1,29 @@ +import type { HybridObject } from 'react-native-nitro-modules'; +import type { BarcodeScanner } from './BarcodeScanner.nitro'; +import type { + BarcodeScannerOptions, + MLKitFeature, + TextRecognizerOptions, +} from './MLKitVisionTypes'; +import type { TextRecognizer } from './TextRecognizer.nitro'; + +/** + * Root factory for ML Kit recognizers backed by VisionCamera v5 Nitro frames. + */ +export interface VisionCameraMLKit + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { + /** + * Returns whether a feature was compiled into the native package. + */ + isFeatureAvailable(feature: MLKitFeature): boolean; + + /** + * Creates a configured text recognizer. + */ + createTextRecognizer(options: TextRecognizerOptions): TextRecognizer; + + /** + * Creates a configured barcode scanner. + */ + createBarcodeScanner(options: BarcodeScannerOptions): BarcodeScanner; +} diff --git a/yarn.lock b/yarn.lock index 1bba5fc..93b974a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32,6 +32,17 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/code-frame@npm:7.29.7" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.29.7" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.1.1" + checksum: 10c0/169fc2080169a40c1760155eaaaf739bcb882df0bec76a83adbda5493645bc17270a3434b8848c494b1933e96fe1d147370001e3cda09a39f43ae30f08ef2069 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.27.2, @babel/compat-data@npm:^7.27.7, @babel/compat-data@npm:^7.28.5": version: 7.28.5 resolution: "@babel/compat-data@npm:7.28.5" @@ -89,6 +100,19 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/generator@npm:7.29.7" + dependencies: + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + "@jridgewell/gen-mapping": "npm:^0.3.12" + "@jridgewell/trace-mapping": "npm:^0.3.28" + jsesc: "npm:^3.0.2" + checksum: 10c0/9bf72b01b5bd0ea5b1288a0e37dbd360bff2f2b1ce73342c0d40fb3db2ec3dc004ada5ffa925c5e12939a416eed59e600d562b8ecd938ce0d27dfd0eb6c6c2b7 + languageName: node + linkType: hard + "@babel/helper-annotate-as-pure@npm:^7.27.1, @babel/helper-annotate-as-pure@npm:^7.27.3": version: 7.27.3 resolution: "@babel/helper-annotate-as-pure@npm:7.27.3" @@ -98,6 +122,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-annotate-as-pure@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-annotate-as-pure@npm:7.29.7" + dependencies: + "@babel/types": "npm:^7.29.7" + checksum: 10c0/c56536b52d17632d89d49db2063ed6102f0e3bbadf6a0ccb74e6599d6a77173b644c7fe8c3ef17c7a162709d55b75ee5145ef6db917d16ba7f375fbffcf2e942 + languageName: node + linkType: hard + "@babel/helper-compilation-targets@npm:^7.27.1, @babel/helper-compilation-targets@npm:^7.27.2": version: 7.27.2 resolution: "@babel/helper-compilation-targets@npm:7.27.2" @@ -128,6 +161,23 @@ __metadata: languageName: node linkType: hard +"@babel/helper-create-class-features-plugin@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-create-class-features-plugin@npm:7.29.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.29.7" + "@babel/helper-member-expression-to-functions": "npm:^7.29.7" + "@babel/helper-optimise-call-expression": "npm:^7.29.7" + "@babel/helper-replace-supers": "npm:^7.29.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/75f34905b5e708b473f1e9b33e07b2fcc8f4c60676df8bc74541bb91c77f387c32a948dd04d5071e469ba454d72d0a872e3ace40fbb1d1e7aaa8569efcf09ed4 + languageName: node + linkType: hard + "@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.27.1": version: 7.28.5 resolution: "@babel/helper-create-regexp-features-plugin@npm:7.28.5" @@ -163,6 +213,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-globals@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-globals@npm:7.29.7" + checksum: 10c0/f38417c40b1129a1b2b519ca961b9040c8827d1444fd74068702286b91b77089431dc76b6b9d5c1496e5da2a4f3ad329c6946e688ba3fa0d1d0b3d2b4f34f36a + languageName: node + linkType: hard + "@babel/helper-member-expression-to-functions@npm:^7.27.1, @babel/helper-member-expression-to-functions@npm:^7.28.5": version: 7.28.5 resolution: "@babel/helper-member-expression-to-functions@npm:7.28.5" @@ -173,6 +230,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-member-expression-to-functions@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-member-expression-to-functions@npm:7.29.7" + dependencies: + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/eef7940ce0797208854a5af1049a98fee9abbffb5c619640c69ff5a555f8e3552295bb18756490b02bc6af7df8c1babcb83f12203aac2deb9dfecfc78846e12d + languageName: node + linkType: hard + "@babel/helper-module-imports@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-module-imports@npm:7.27.1" @@ -205,6 +272,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-optimise-call-expression@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-optimise-call-expression@npm:7.29.7" + dependencies: + "@babel/types": "npm:^7.29.7" + checksum: 10c0/fd0244b9bfbb487db02d59aa2703c6991d654ea5f3f39d912682842bdca2e87b5ae8643b0ce8069bf5fbee39d1aa9db7abefeb5e6ba1aa650dca12777cf5b7e2 + languageName: node + linkType: hard + "@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.27.1, @babel/helper-plugin-utils@npm:^7.8.0": version: 7.27.1 resolution: "@babel/helper-plugin-utils@npm:7.27.1" @@ -212,6 +288,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-plugin-utils@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-plugin-utils@npm:7.29.7" + checksum: 10c0/380477a06133274a2759f9355929cb60a95e8b8fee624a1ae1fa349e1d1645b89daca456f72833f6d1062bffa12ee4271c5bf0cc5a61c0166cdc24c7591e2408 + languageName: node + linkType: hard + "@babel/helper-remap-async-to-generator@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-remap-async-to-generator@npm:7.27.1" @@ -238,6 +321,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-replace-supers@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-replace-supers@npm:7.29.7" + dependencies: + "@babel/helper-member-expression-to-functions": "npm:^7.29.7" + "@babel/helper-optimise-call-expression": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/1c7ae37797f226e965ab85f6affa53d25a10c169c604a4daeb36f9df09e673471e6522f631c13761cf9fbafeca2ea14c241dea8d723a51039d561beb01d86ac4 + languageName: node + linkType: hard + "@babel/helper-skip-transparent-expression-wrappers@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.27.1" @@ -248,6 +344,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.29.7" + dependencies: + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/8c59493621487fc491f27adfc200af82a6aca3b9a5511e4e6050f8716593b4b243472cb56c8d2016e828b7ae12d605a819205aa8600ca08ee291dcd58d65c832 + languageName: node + linkType: hard + "@babel/helper-string-parser@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-string-parser@npm:7.27.1" @@ -255,6 +361,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-string-parser@npm:7.29.7" + checksum: 10c0/194bc0f1716e396d5ffde56ad6119745fb9557662c98611590e5e454906783a4ccb21ce93056b8eb69a4909044834e45d96e50ac695bbe9e3221648fe033c06c + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.27.1, @babel/helper-validator-identifier@npm:^7.28.5": version: 7.28.5 resolution: "@babel/helper-validator-identifier@npm:7.28.5" @@ -262,6 +375,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-identifier@npm:7.29.7" + checksum: 10c0/4795354e7ae0dcafa72de1cd04ec51252dc1498517170beaf019e03effc5b7bf13c6b21a3949a77e07b8125be7f106ed1131350d8ebd4566ae874094a726d62b + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-validator-option@npm:7.27.1" @@ -301,6 +421,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/parser@npm:7.29.7" + dependencies: + "@babel/types": "npm:^7.29.7" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/65133038f80b54a714d6027cb77cee3f9a6b5c4c6842ce674301e13947cbcbfa8055e63acaf1b84c085d34226a14425b2c2b97b829e0e226d2e8f1299942a51d + languageName: node + linkType: hard + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.28.5": version: 7.28.5 resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.28.5" @@ -611,6 +742,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-typescript@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/plugin-syntax-typescript@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/c49883b0327e8683b770dc823205af5c697da216e590dcf5bf53f3f031e7e381de450b164f8f99853f0837a3de5cb793298e2be6697a0f6e452bb9dd34b5165e + languageName: node + linkType: hard + "@babel/plugin-syntax-unicode-sets-regex@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-syntax-unicode-sets-regex@npm:7.18.6" @@ -1299,7 +1441,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typescript@npm:^7.25.2, @babel/plugin-transform-typescript@npm:^7.27.1, @babel/plugin-transform-typescript@npm:^7.28.5": +"@babel/plugin-transform-typescript@npm:^7.25.2, @babel/plugin-transform-typescript@npm:^7.28.5": version: 7.28.5 resolution: "@babel/plugin-transform-typescript@npm:7.28.5" dependencies: @@ -1314,6 +1456,21 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typescript@npm:^7.27.1": + version: 7.29.7 + resolution: "@babel/plugin-transform-typescript@npm:7.29.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.29.7" + "@babel/helper-create-class-features-plugin": "npm:^7.29.7" + "@babel/helper-plugin-utils": "npm:^7.29.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.29.7" + "@babel/plugin-syntax-typescript": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/8bf6a89c6827af6f11d4b189f1f97a64b8d754cc4caa5cbae16a6a8b113294d7f310dd40efd82e87ebcff2a1c683584b872d6d8960abe88d48f441d42f94c4d1 + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-escapes@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-unicode-escapes@npm:7.27.1" @@ -1518,6 +1675,17 @@ __metadata: languageName: node linkType: hard +"@babel/template@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/template@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/8bb7f900dcab0e9e1c5ffbc33ca10e0d26b7b2e2ca804becb73ee771b9c4ed6e2908a4ae4a14c08560febb45d2b6b9a173955e42ad404d05f8b04840a14d9c58 + languageName: node + linkType: hard + "@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3, @babel/traverse@npm:^7.25.3, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.28.3, @babel/traverse@npm:^7.28.4, @babel/traverse@npm:^7.28.5": version: 7.28.5 resolution: "@babel/traverse@npm:7.28.5" @@ -1533,6 +1701,21 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/traverse@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-globals": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + debug: "npm:^4.3.1" + checksum: 10c0/e256a1fbdb956555b76f3c285b1e453f6bedec8b3afb61751d99d933efd11c7d79caf5ddf2493570058a9f7deaa1b48324380d7c1aa1443fd9508becbf56331a + languageName: node + linkType: hard + "@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.25.2, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": version: 7.28.5 resolution: "@babel/types@npm:7.28.5" @@ -1543,6 +1726,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/types@npm:7.29.7" + dependencies: + "@babel/helper-string-parser": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + checksum: 10c0/b6623994c69717fa27294f5fa46d59140338e2d86c6c1c13085c84ef7d53086ee357fbf4fe9abe3dd3da75734dc77c4c0df2f90fb29e667558bb3b3fb705e88f + languageName: node + linkType: hard + "@bcoe/v8-coverage@npm:^0.2.3": version: 0.2.3 resolution: "@bcoe/v8-coverage@npm:0.2.3" @@ -3561,6 +3754,17 @@ __metadata: languageName: node linkType: hard +"@ts-morph/common@npm:~0.29.0": + version: 0.29.0 + resolution: "@ts-morph/common@npm:0.29.0" + dependencies: + minimatch: "npm:^10.0.1" + path-browserify: "npm:^1.0.1" + tinyglobby: "npm:^0.2.14" + checksum: 10c0/97ab8ca66558b817e5475a8893ee1476ab760a3ac71fa9868413d95ddbaaf909eda81716e3a8d14291cbf449e624af66de81bcddd3ec2e36525728b4edf5e8ee + languageName: node + linkType: hard + "@types/babel__core@npm:^7.1.14": version: 7.20.5 resolution: "@types/babel__core@npm:7.20.5" @@ -4105,7 +4309,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.1.0": +"ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": version: 6.2.3 resolution: "ansi-styles@npm:6.2.3" checksum: 10c0/23b8a4ce14e18fb854693b95351e286b771d23d8844057ed2e7d083cd3e708376c3323707ec6a24365f7d7eda3ca00327fe04092e29e551499ec4c8b7bfac868 @@ -4482,6 +4686,13 @@ __metadata: languageName: node linkType: hard +"balanced-match@npm:^4.0.2": + version: 4.0.4 + resolution: "balanced-match@npm:4.0.4" + checksum: 10c0/07e86102a3eb2ee2a6a1a89164f29d0dbaebd28f2ca3f5ca786f36b8b23d9e417eb3be45a4acf754f837be5ac0a2317de90d3fcb7f4f4dc95720a1f36b26a17b + languageName: node + linkType: hard + "base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -4569,6 +4780,15 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^5.0.5": + version: 5.0.7 + resolution: "brace-expansion@npm:5.0.7" + dependencies: + balanced-match: "npm:^4.0.2" + checksum: 10c0/4769109c3c082de178e449a371bcad50d51ab468f644bce2dd9188efe0cf0a080ed102105d7fc8577382cedc45bad7e6443a91bf3d8102264ee8cf927dbaf205 + languageName: node + linkType: hard + "braces@npm:^3.0.3": version: 3.0.3 resolution: "braces@npm:3.0.3" @@ -4919,6 +5139,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^9.0.1": + version: 9.0.1 + resolution: "cliui@npm:9.0.1" + dependencies: + string-width: "npm:^7.2.0" + strip-ansi: "npm:^7.1.0" + wrap-ansi: "npm:^9.0.0" + checksum: 10c0/13441832e9efe7c7a76bd2b8e683555c478d461a9f249dc5db9b17fe8d4b47fa9277b503914b90bd00e4a151abb6b9b02b2288972ffe2e5e3ca40bcb1c2330d3 + languageName: node + linkType: hard + "clone@npm:^1.0.2": version: 1.0.4 resolution: "clone@npm:1.0.4" @@ -4933,6 +5164,13 @@ __metadata: languageName: node linkType: hard +"code-block-writer@npm:^13.0.3": + version: 13.0.3 + resolution: "code-block-writer@npm:13.0.3" + checksum: 10c0/87db97b37583f71cfd7eced8bf3f0a0a0ca53af912751a734372b36c08cd27f3e8a4878ec05591c0cd9ae11bea8add1423e132d660edd86aab952656dd41fd66 + languageName: node + linkType: hard + "collect-v8-coverage@npm:^1.0.0": version: 1.0.3 resolution: "collect-v8-coverage@npm:1.0.3" @@ -5743,6 +5981,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^10.3.0": + version: 10.6.0 + resolution: "emoji-regex@npm:10.6.0" + checksum: 10c0/1e4aa097bb007301c3b4b1913879ae27327fdc48e93eeefefe3b87e495eb33c5af155300be951b4349ff6ac084f4403dc9eff970acba7c1c572d89396a9a32d7 + languageName: node + linkType: hard + "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -6776,6 +7021,13 @@ __metadata: languageName: node linkType: hard +"get-east-asian-width@npm:^1.0.0": + version: 1.6.0 + resolution: "get-east-asian-width@npm:1.6.0" + checksum: 10c0/7e72e9550fd49ca5b246f9af6bb2afc129c96412845ff6556b3274fd44817a381702ca17028efe9866b261a3d44254cbf21e6c90cf05b4b61675630af776d431 + languageName: node + linkType: hard + "get-east-asian-width@npm:^1.3.0": version: 1.4.0 resolution: "get-east-asian-width@npm:1.4.0" @@ -9438,6 +9690,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.0.1": + version: 10.2.5 + resolution: "minimatch@npm:10.2.5" + dependencies: + brace-expansion: "npm:^5.0.5" + checksum: 10c0/6bb058bd6324104b9ec2f763476a35386d05079c1f5fe4fbf1f324a25237cd4534d6813ecd71f48208f4e635c1221899bef94c3c89f7df55698fe373aaae20fd + languageName: node + linkType: hard + "minimatch@npm:^10.1.1": version: 10.1.1 resolution: "minimatch@npm:10.1.1" @@ -9638,6 +9899,21 @@ __metadata: languageName: node linkType: hard +"nitrogen@npm:0.36.1": + version: 0.36.1 + resolution: "nitrogen@npm:0.36.1" + dependencies: + chalk: "npm:^5.3.0" + react-native-nitro-modules: "npm:^0.36.1" + ts-morph: "npm:^28.0.0" + yargs: "npm:^18.0.0" + zod: "npm:^4.4.3" + bin: + nitrogen: lib/index.js + checksum: 10c0/a639dbadeca131be7dc059558b599fc2a1d45a88c4260d936a77e9402d1f81208f770c6085c4f5f13f48a10ec1cba04425278558dc0cfd41434e41d1a555cacb + languageName: node + linkType: hard + "nocache@npm:^3.0.1": version: 3.0.4 resolution: "nocache@npm:3.0.4" @@ -10176,6 +10452,13 @@ __metadata: languageName: node linkType: hard +"path-browserify@npm:^1.0.1": + version: 1.0.1 + resolution: "path-browserify@npm:1.0.1" + checksum: 10c0/8b8c3fd5c66bd340272180590ae4ff139769e9ab79522e2eb82e3d571a89b8117c04147f65ad066dccfb42fcad902e5b7d794b3d35e0fd840491a8ddbedf8c66 + languageName: node + linkType: hard + "path-exists@npm:^4.0.0": version: 4.0.0 resolution: "path-exists@npm:4.0.0" @@ -10287,6 +10570,13 @@ __metadata: languageName: node linkType: hard +"picomatch@npm:^4.0.4": + version: 4.0.5 + resolution: "picomatch@npm:4.0.5" + checksum: 10c0/947bc6b6e1ff1e6c5aaf95b107a0839d12802f4f7b867663f67d47accba939ca1cb582cf99dfc30438efa1c4648ac5990967e783e8929c36b03e8440704ef1bd + languageName: node + linkType: hard + "pirates@npm:^4.0.4": version: 4.0.7 resolution: "pirates@npm:4.0.7" @@ -10657,6 +10947,27 @@ __metadata: languageName: node linkType: hard +"react-native-nitro-image@npm:0.15.0": + version: 0.15.0 + resolution: "react-native-nitro-image@npm:0.15.0" + peerDependencies: + react: "*" + react-native: "*" + react-native-nitro-modules: "*" + checksum: 10c0/7af289336f0340a387f723a38e2533d7b121db5c146a339f0c0c49a3d2d9cd40935c06dff5a6dd8ab5eb633515d959211f727dddafd648f957476ee36903ea5c + languageName: node + linkType: hard + +"react-native-nitro-modules@npm:0.36.1, react-native-nitro-modules@npm:^0.36.1": + version: 0.36.1 + resolution: "react-native-nitro-modules@npm:0.36.1" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/8f0a56371d2de36531208794f81c457bbd00f00ae8f9c914fb752606585297607cbc1ad38681a1efb7d8cf4322537f300e0a822954a4b15c8d8c8f2c9d61cd12 + languageName: node + linkType: hard + "react-native-permissions@npm:^5.4.4": version: 5.4.4 resolution: "react-native-permissions@npm:5.4.4" @@ -10749,14 +11060,16 @@ __metadata: react-native-gesture-handler: "npm:^2.30.0" react-native-image-picker: "npm:^8.2.1" react-native-monorepo-config: "npm:^0.1.9" + react-native-nitro-image: "npm:0.15.0" + react-native-nitro-modules: "npm:0.36.1" react-native-permissions: "npm:^5.4.4" react-native-reanimated: "npm:^4.2.1" react-native-safe-area-context: "npm:^5.6.2" react-native-screens: "npm:^4.20.0" react-native-svg: "npm:^15.15.1" - react-native-vision-camera: "npm:^4.7.3" - react-native-worklets: "npm:^0.7.1" - react-native-worklets-core: "npm:^1.6.2" + react-native-vision-camera: "npm:^5.1.0" + react-native-vision-camera-worklets: "npm:^5.0.9" + react-native-worklets: "npm:0.7.1" zustand: "npm:^5.0.9" languageName: unknown linkType: soft @@ -10765,6 +11078,7 @@ __metadata: version: 0.0.0-use.local resolution: "react-native-vision-camera-mlkit@workspace:." dependencies: + "@babel/core": "npm:^7.25.2" "@commitlint/config-conventional": "npm:^19.8.1" "@eslint/compat": "npm:^1.3.2" "@eslint/eslintrc": "npm:^3.3.1" @@ -10782,56 +11096,56 @@ __metadata: eslint-plugin-prettier: "npm:^5.5.4" jest: "npm:^29.7.0" lefthook: "npm:^2.0.3" + nitrogen: "npm:0.36.1" prettier: "npm:^2.8.8" react: "npm:19.1.0" react-native: "npm:0.81.1" react-native-builder-bob: "npm:^0.40.13" - react-native-vision-camera: "npm:^4.7.3" - react-native-worklets-core: "npm:^1.6.2" + react-native-nitro-image: "npm:0.15.0" + react-native-nitro-modules: "npm:0.36.1" + react-native-vision-camera: "npm:^5.1.0" + react-native-vision-camera-worklets: "npm:^5.0.9" + react-native-worklets: "npm:0.7.1" release-it: "npm:^19.0.4" turbo: "npm:^2.5.6" typescript: "npm:^5.9.2" peerDependencies: react: "*" react-native: "*" - react-native-vision-camera: "*" - react-native-worklets-core: "*" + react-native-nitro-image: "*" + react-native-nitro-modules: "*" + react-native-vision-camera: ">=5" + react-native-vision-camera-worklets: "*" + react-native-worklets: "*" languageName: unknown linkType: soft -"react-native-vision-camera@npm:^4.7.3": - version: 4.7.3 - resolution: "react-native-vision-camera@npm:4.7.3" +"react-native-vision-camera-worklets@npm:^5.0.9": + version: 5.1.0 + resolution: "react-native-vision-camera-worklets@npm:5.1.0" peerDependencies: - "@shopify/react-native-skia": "*" react: "*" react-native: "*" - react-native-reanimated: "*" - react-native-worklets-core: "*" - peerDependenciesMeta: - "@shopify/react-native-skia": - optional: true - react-native-reanimated: - optional: true - react-native-worklets-core: - optional: true - checksum: 10c0/46001037f4f9a85e75c023e5082b137c179fd520741e088009d217f0a6eaf3a819b9784ae68aced95fa70c3345346c79dd2ee70bc04cfd811c7eefeda675cab2 + react-native-nitro-modules: "*" + react-native-vision-camera: "*" + react-native-worklets: "*" + checksum: 10c0/fe58b63dc2ed5442c0196286e3343bf17f0698d98dc746315156086e65bc203cb467d07e91eb9ac9ae18ab2b54cf65582a00ec598d43307473b76154556d76f6 languageName: node linkType: hard -"react-native-worklets-core@npm:^1.6.2": - version: 1.6.2 - resolution: "react-native-worklets-core@npm:1.6.2" - dependencies: - string-hash-64: "npm:^1.0.3" +"react-native-vision-camera@npm:^5.1.0": + version: 5.1.0 + resolution: "react-native-vision-camera@npm:5.1.0" peerDependencies: react: "*" react-native: "*" - checksum: 10c0/c10a6edb0b8c8f7f9573ac24e0808dc01bfc90581d0b0aa6d264457ef7805f54358226b5afe4ce67ac9fa00c861ec65d1ebfd9657f97197068f10240758b1175 + react-native-nitro-image: "*" + react-native-nitro-modules: "*" + checksum: 10c0/6fbac55b041048fbc5ffd96f3df87bd9b07a4b8dc79a16d0bfb55aa08f790d8cd322b754b84f8714787cd910c9b9e4920d9cbe24ab7674fdb0ed9e1bc42e6285 languageName: node linkType: hard -"react-native-worklets@npm:^0.7.1": +"react-native-worklets@npm:0.7.1": version: 0.7.1 resolution: "react-native-worklets@npm:0.7.1" dependencies: @@ -11727,13 +12041,6 @@ __metadata: languageName: node linkType: hard -"string-hash-64@npm:^1.0.3": - version: 1.0.3 - resolution: "string-hash-64@npm:1.0.3" - checksum: 10c0/ac175f71b383af1487ab16b4cc1b42b5302bea08bdf1cc1ea04bacb293fb9b35afc6c58260e1fc728627e7265519da01976822a188d493d7e7625b9628173ea3 - languageName: node - linkType: hard - "string-length@npm:^4.0.1": version: 4.0.2 resolution: "string-length@npm:4.0.2" @@ -11773,6 +12080,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^7.0.0, string-width@npm:^7.2.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9 + languageName: node + linkType: hard + "string-width@npm:^8.1.0": version: 8.1.0 resolution: "string-width@npm:8.1.0" @@ -12033,6 +12351,16 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.14": + version: 0.2.17 + resolution: "tinyglobby@npm:0.2.17" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c + languageName: node + linkType: hard + "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -12065,6 +12393,16 @@ __metadata: languageName: node linkType: hard +"ts-morph@npm:^28.0.0": + version: 28.0.0 + resolution: "ts-morph@npm:28.0.0" + dependencies: + "@ts-morph/common": "npm:~0.29.0" + code-block-writer: "npm:^13.0.3" + checksum: 10c0/969570a5e983b4193735fff43878ce7b78787b4860bf3d23f330c86d7de707286969e5a82d486dce333eea27bf9d373a46f9de58a80c0bf86d2a462620563a55 + languageName: node + linkType: hard + "tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" @@ -12717,6 +13055,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^9.0.0": + version: 9.0.2 + resolution: "wrap-ansi@npm:9.0.2" + dependencies: + ansi-styles: "npm:^6.2.1" + string-width: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/3305839b9a0d6fb930cb63a52f34d3936013d8b0682ff3ec133c9826512620f213800ffa19ea22904876d5b7e9a3c1f40682f03597d986a4ca881fa7b033688c + languageName: node + linkType: hard + "wrappy@npm:1": version: 1.0.2 resolution: "wrappy@npm:1.0.2" @@ -12828,6 +13177,13 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^22.0.0": + version: 22.0.0 + resolution: "yargs-parser@npm:22.0.0" + checksum: 10c0/cb7ef81759c4271cb1d96b9351dbbc9a9ce35d3e1122d2b739bf6c432603824fa02c67cc12dcef6ea80283379d63495686e8f41cc7b06c6576e792aba4d33e1c + languageName: node + linkType: hard + "yargs@npm:^15.1.0": version: 15.4.1 resolution: "yargs@npm:15.4.1" @@ -12862,6 +13218,20 @@ __metadata: languageName: node linkType: hard +"yargs@npm:^18.0.0": + version: 18.0.0 + resolution: "yargs@npm:18.0.0" + dependencies: + cliui: "npm:^9.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + string-width: "npm:^7.2.0" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^22.0.0" + checksum: 10c0/bf290e4723876ea9c638c786a5c42ac28e03c9ca2325e1424bf43b94e5876456292d3ed905b853ebbba6daf43ed29e772ac2a6b3c5fb1b16533245d6211778f3 + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" @@ -12890,6 +13260,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^4.4.3": + version: 4.4.3 + resolution: "zod@npm:4.4.3" + checksum: 10c0/7ea31b558e88f9faf44f31dd185e2e1cbf51fed3081787fb96cc2534749b50c0acfc6da7f0922a7353ed092dd358c7d50c28ea96c94d04af64191bd33152eca3 + languageName: node + linkType: hard + "zustand@npm:^5.0.9": version: 5.0.9 resolution: "zustand@npm:5.0.9" From e4b96cd6f7a8d46ef6c1195ae231d439f5065dd8 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 5 Jul 2026 22:43:04 -0300 Subject: [PATCH 20/45] feat(android): wire live ML Kit frame recognition --- .../BarcodeFormat+toDomain.kt | 9 ++ .../BarcodeScannerOptions+toDomain.kt | 18 ++++ .../BarcodeScanningResult+toNitro.kt | 26 ++++++ .../visioncameramlkit/BoundingBox+toNitro.kt | 19 +++++ .../nitro/visioncameramlkit/Corner+toNitro.kt | 10 +++ .../Double+toNullableVariant.kt | 7 ++ .../visioncameramlkit/HybridBarcodeScanner.kt | 10 ++- .../HybridFrameSpec+toProcessedImage.kt | 83 +++++++++++++++++++ .../visioncameramlkit/HybridTextRecognizer.kt | 10 ++- .../TextRecognitionLanguage+toDomain.kt | 12 +++ .../TextRecognitionResult+toNitro.kt | 48 +++++++++++ .../TextRecognizerOptions+toDomain.kt | 17 ++++ .../infrastructure/image/ImagePreprocessor.kt | 2 +- 13 files changed, 268 insertions(+), 3 deletions(-) create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeFormat+toDomain.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions+toDomain.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScanningResult+toNitro.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/BoundingBox+toNitro.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/Corner+toNitro.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/Double+toNullableVariant.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridFrameSpec+toProcessedImage.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognitionLanguage+toDomain.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognitionResult+toNitro.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions+toDomain.kt diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeFormat+toDomain.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeFormat+toDomain.kt new file mode 100644 index 0000000..8e86109 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeFormat+toDomain.kt @@ -0,0 +1,9 @@ +package com.margelo.nitro.visioncameramlkit + +import com.visioncameramlkit.domain.models.BarcodeFormatOption + +internal fun BarcodeFormat.toDomainBarcodeFormat(): BarcodeFormatOption = + runCatching { BarcodeFormatOption.valueOf(name) }.getOrDefault(BarcodeFormatOption.UNKNOWN) + +internal fun Array?.toDomainBarcodeFormats(): List = + this?.map { it.toDomainBarcodeFormat() } ?: emptyList() diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions+toDomain.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions+toDomain.kt new file mode 100644 index 0000000..8525a5b --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions+toDomain.kt @@ -0,0 +1,18 @@ +package com.margelo.nitro.visioncameramlkit + +import com.visioncameramlkit.domain.models.BarcodeScanningOptions +import com.visioncameramlkit.domain.models.ImagePreprocessingOptions + +internal fun BarcodeScannerOptions.toDomainBarcodeScanningOptions(): BarcodeScanningOptions = + BarcodeScanningOptions( + formats = formats.toDomainBarcodeFormats(), + enableAllPotentialBarcodes = enableAllPotentialBarcodes ?: false, + invertColors = invertColors ?: false, + scaleFactor = scaleFactor?.toFloat() ?: 1.0f, + ) + +internal fun BarcodeScannerOptions.toImagePreprocessingOptions(): ImagePreprocessingOptions = + ImagePreprocessingOptions( + invertColors = invertColors ?: false, + scaleFactor = scaleFactor?.toFloat() ?: 1.0f, + ) diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScanningResult+toNitro.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScanningResult+toNitro.kt new file mode 100644 index 0000000..40717fe --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScanningResult+toNitro.kt @@ -0,0 +1,26 @@ +package com.margelo.nitro.visioncameramlkit + +internal fun com.visioncameramlkit.domain.models.BarcodeScanningResult.toNitroResult(): BarcodeScannerResult = + BarcodeScannerResult( + barcodes = barcodes.map { it.toNitroBarcode() }.toTypedArray(), + ) + +private fun com.visioncameramlkit.domain.models.BarcodeData.toNitroBarcode(): BarcodeScannerBarcode = + BarcodeScannerBarcode( + bounds = bounds?.toNitroBoundingBox(), + corners = corners?.toNitroCorners(), + format = format.toDouble(), + formatName = formatName.toNitroBarcodeFormat(), + valueType = valueType.toDouble(), + valueTypeName = valueTypeName.toNitroBarcodeValueTypeName(), + rawValue = rawValue, + displayValue = displayValue, + rawBytes = rawBytes?.map { it.toDouble() }?.toDoubleArray(), + isPotential = isPotential, + ) + +private fun String.toNitroBarcodeFormat(): BarcodeFormat = + runCatching { BarcodeFormat.valueOf(this) }.getOrDefault(BarcodeFormat.UNKNOWN) + +private fun String.toNitroBarcodeValueTypeName(): BarcodeValueTypeName = + runCatching { BarcodeValueTypeName.valueOf(this) }.getOrDefault(BarcodeValueTypeName.TYPE_UNKNOWN) diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BoundingBox+toNitro.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BoundingBox+toNitro.kt new file mode 100644 index 0000000..08319f6 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BoundingBox+toNitro.kt @@ -0,0 +1,19 @@ +package com.margelo.nitro.visioncameramlkit + +private val emptyBoundingBox = + BoundingBox( + x = 0.0, + y = 0.0, + width = 0.0, + height = 0.0, + ) + +internal fun com.visioncameramlkit.domain.models.BoundingBox?.toNitroBoundingBox(): BoundingBox = + this?.let { + BoundingBox( + x = it.x, + y = it.y, + width = it.width, + height = it.height, + ) + } ?: emptyBoundingBox diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/Corner+toNitro.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/Corner+toNitro.kt new file mode 100644 index 0000000..42c97bb --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/Corner+toNitro.kt @@ -0,0 +1,10 @@ +package com.margelo.nitro.visioncameramlkit + +internal fun com.visioncameramlkit.domain.models.Corner.toNitroCorner(): Corner = + Corner( + x = x, + y = y, + ) + +internal fun List?.toNitroCorners(): Array = + this?.map { it.toNitroCorner() }?.toTypedArray() ?: emptyArray() diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/Double+toNullableVariant.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/Double+toNullableVariant.kt new file mode 100644 index 0000000..bcbd1bf --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/Double+toNullableVariant.kt @@ -0,0 +1,7 @@ +package com.margelo.nitro.visioncameramlkit + +import com.margelo.nitro.core.NullType + +internal fun Double?.toNullableDoubleVariant(): Variant_NullType_Double = + this?.let { Variant_NullType_Double.create(it) } + ?: Variant_NullType_Double.create(NullType.NULL) diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt index 1740362..9eeed36 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt @@ -3,13 +3,21 @@ package com.margelo.nitro.visioncameramlkit import androidx.annotation.Keep import com.facebook.proguard.annotations.DoNotStrip import com.margelo.nitro.camera.HybridFrameSpec +import com.visioncameramlkit.infrastructure.image.ImagePreprocessor +import com.visioncameramlkit.infrastructure.mlkit.factories.BarcodeScanningServiceFactory @DoNotStrip @Keep class HybridBarcodeScanner( private val options: BarcodeScannerOptions, ) : HybridBarcodeScannerSpec() { + private val imagePreprocessor = ImagePreprocessor() + private val barcodeOptions = options.toDomainBarcodeScanningOptions() + private val imageOptions = options.toImagePreprocessingOptions() + private val recognitionService = BarcodeScanningServiceFactory.create(barcodeOptions) + override fun recognize(frame: HybridFrameSpec, args: MLKitBaseArguments?): BarcodeScannerResult { - throw Error("VisionCamera v5 barcode frame processing is not implemented yet.") + val processedImage = frame.toProcessedImage(imagePreprocessor, imageOptions) + return recognitionService.recognize(processedImage).toNitroResult() } } diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridFrameSpec+toProcessedImage.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridFrameSpec+toProcessedImage.kt new file mode 100644 index 0000000..b552278 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridFrameSpec+toProcessedImage.kt @@ -0,0 +1,83 @@ +package com.margelo.nitro.visioncameramlkit + +import androidx.camera.core.ImageProxy +import androidx.core.graphics.scale +import com.google.mlkit.vision.common.InputImage +import com.margelo.nitro.camera.HybridFrameSpec +import com.margelo.nitro.camera.extensions.toBitmap +import com.margelo.nitro.camera.public.NativeFrame +import com.visioncameramlkit.domain.models.ImageMetadata +import com.visioncameramlkit.domain.models.ImagePreprocessingOptions +import com.visioncameramlkit.domain.models.ProcessedImage +import com.visioncameramlkit.infrastructure.image.ImagePreprocessor + +internal fun HybridFrameSpec.toProcessedImage( + imagePreprocessor: ImagePreprocessor, + options: ImagePreprocessingOptions, +): ProcessedImage { + val nativeFrame = + this as? NativeFrame + ?: throw IllegalArgumentException("Expected a VisionCamera native frame.") + + return imagePreprocessor.preprocessFrame(nativeFrame.image, this, options) +} + +internal fun ImagePreprocessor.preprocessFrame( + image: ImageProxy, + frame: HybridFrameSpec, + options: ImagePreprocessingOptions, +): ProcessedImage { + val effectiveScale = options.scaleFactor.coerceIn(0.9f, 1.0f) + val needsBitmapProcessing = + options.invertColors || + effectiveScale < 1.0f || + frame.isMirrored + + if (!needsBitmapProcessing) { + val mediaImage = + image.image + ?: throw UnsupportedOperationException("VisionCamera frame does not expose a media image.") + + val inputImage = InputImage.fromMediaImage(mediaImage, image.imageInfo.rotationDegrees) + return ProcessedImage( + image = inputImage, + metadata = + ImageMetadata( + width = image.width, + height = image.height, + rotation = image.imageInfo.rotationDegrees, + isInverted = false, + ), + ) + } + + val orientedBitmap = image.toBitmap(frame.orientation, frame.isMirrored) + val scaledBitmap = + if (effectiveScale < 1.0f) { + orientedBitmap.scale( + (orientedBitmap.width * effectiveScale).toInt(), + (orientedBitmap.height * effectiveScale).toInt(), + false, + ) + } else { + orientedBitmap + } + + val processedBitmap = + if (options.invertColors) { + invertBitmap(scaledBitmap) + } else { + scaledBitmap + } + + return ProcessedImage( + image = InputImage.fromBitmap(processedBitmap, 0), + metadata = + ImageMetadata( + width = processedBitmap.width, + height = processedBitmap.height, + rotation = 0, + isInverted = options.invertColors, + ), + ) +} diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt index 97d06b3..1645be6 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt @@ -3,13 +3,21 @@ package com.margelo.nitro.visioncameramlkit import androidx.annotation.Keep import com.facebook.proguard.annotations.DoNotStrip import com.margelo.nitro.camera.HybridFrameSpec +import com.visioncameramlkit.infrastructure.image.ImagePreprocessor +import com.visioncameramlkit.infrastructure.mlkit.factories.TextRecognitionServiceFactory @DoNotStrip @Keep class HybridTextRecognizer( private val options: TextRecognizerOptions, ) : HybridTextRecognizerSpec() { + private val imagePreprocessor = ImagePreprocessor() + private val textOptions = options.toDomainTextRecognitionOptions() + private val imageOptions = options.toImagePreprocessingOptions() + private val recognitionService = TextRecognitionServiceFactory.create(textOptions.language) + override fun recognize(frame: HybridFrameSpec, args: MLKitBaseArguments?): TextRecognitionResult { - throw Error("VisionCamera v5 text recognition frame processing is not implemented yet.") + val processedImage = frame.toProcessedImage(imagePreprocessor, imageOptions) + return recognitionService.recognize(processedImage).toNitroResult() } } diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognitionLanguage+toDomain.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognitionLanguage+toDomain.kt new file mode 100644 index 0000000..d3a0b31 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognitionLanguage+toDomain.kt @@ -0,0 +1,12 @@ +package com.margelo.nitro.visioncameramlkit + +internal fun TextRecognitionLanguage?.toDomainLanguage(): com.visioncameramlkit.domain.models.TextRecognitionLanguage = + when (this) { + TextRecognitionLanguage.CHINESE -> com.visioncameramlkit.domain.models.TextRecognitionLanguage.CHINESE + TextRecognitionLanguage.DEVANAGARI -> com.visioncameramlkit.domain.models.TextRecognitionLanguage.DEVANAGARI + TextRecognitionLanguage.JAPANESE -> com.visioncameramlkit.domain.models.TextRecognitionLanguage.JAPANESE + TextRecognitionLanguage.KOREAN -> com.visioncameramlkit.domain.models.TextRecognitionLanguage.KOREAN + TextRecognitionLanguage.LATIN, + null, + -> com.visioncameramlkit.domain.models.TextRecognitionLanguage.LATIN + } diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognitionResult+toNitro.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognitionResult+toNitro.kt new file mode 100644 index 0000000..92bf029 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognitionResult+toNitro.kt @@ -0,0 +1,48 @@ +package com.margelo.nitro.visioncameramlkit + +internal fun com.visioncameramlkit.domain.models.TextRecognitionResult.toNitroResult(): TextRecognitionResult = + TextRecognitionResult( + text = text ?: "", + blocks = blocks.map { it.toNitroTextBlock() }.toTypedArray(), + ) + +private fun com.visioncameramlkit.domain.models.TextBlock.toNitroTextBlock(): TextBlock = + TextBlock( + bounds = bounds.toNitroBoundingBox(), + corners = corners.toNitroCorners(), + languages = languages.toTypedArray(), + text = text, + lines = lines.map { it.toNitroTextLine() }.toTypedArray(), + ) + +private fun com.visioncameramlkit.domain.models.TextLine.toNitroTextLine(): TextLine = + TextLine( + bounds = bounds.toNitroBoundingBox(), + corners = corners.toNitroCorners(), + confidence = confidence.toNullableDoubleVariant(), + angle = angle.toNullableDoubleVariant(), + languages = languages.toTypedArray(), + text = text, + elements = elements.map { it.toNitroTextElement() }.toTypedArray(), + ) + +private fun com.visioncameramlkit.domain.models.TextElement.toNitroTextElement(): TextElement = + TextElement( + bounds = bounds.toNitroBoundingBox(), + corners = corners.toNitroCorners(), + confidence = confidence.toNullableDoubleVariant(), + angle = angle.toNullableDoubleVariant(), + languages = languages.toTypedArray(), + text = text, + symbols = symbols.map { it.toNitroTextSymbol() }.toTypedArray(), + ) + +private fun com.visioncameramlkit.domain.models.TextSymbol.toNitroTextSymbol(): TextSymbol = + TextSymbol( + bounds = bounds.toNitroBoundingBox(), + corners = corners.toNitroCorners(), + confidence = confidence ?: 0.0, + angle = angle ?: 0.0, + languages = languages.toTypedArray(), + text = text, + ) diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions+toDomain.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions+toDomain.kt new file mode 100644 index 0000000..7fef6b4 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions+toDomain.kt @@ -0,0 +1,17 @@ +package com.margelo.nitro.visioncameramlkit + +import com.visioncameramlkit.domain.models.ImagePreprocessingOptions +import com.visioncameramlkit.domain.models.TextRecognitionOptions + +internal fun TextRecognizerOptions.toDomainTextRecognitionOptions(): TextRecognitionOptions = + TextRecognitionOptions( + language = language.toDomainLanguage(), + invertColors = invertColors ?: false, + scaleFactor = scaleFactor?.toFloat() ?: 1.0f, + ) + +internal fun TextRecognizerOptions.toImagePreprocessingOptions(): ImagePreprocessingOptions = + ImagePreprocessingOptions( + invertColors = invertColors ?: false, + scaleFactor = scaleFactor?.toFloat() ?: 1.0f, + ) diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt index 6e1fefd..3c19257 100644 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt @@ -26,7 +26,7 @@ class ImagePreprocessor : IImagePreprocessor { return scale.coerceIn(0.9f, 1.0f) } - private fun invertBitmap(bitmap: Bitmap): Bitmap = + fun invertBitmap(bitmap: Bitmap): Bitmap = createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888).apply { val canvas = Canvas(this) val paint = Paint() From 8efddf9b3401428cf9137e85912acd21f8d22eb2 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Mon, 6 Jul 2026 10:04:06 -0300 Subject: [PATCH 21/45] feat(ios): wire live ML Kit frame recognition --- .codex/config.toml | 4 -- .../UseCases/RecognizeBarcodesUseCase.swift | 2 +- .../UseCases/RecognizeTextUseCase.swift | 6 +- .../StaticBarcodeScanningHandler.swift | 2 +- .../StaticTextRecognitionHandler.swift | 6 +- .../Hybrid/BarcodeFormat+toDomain.swift | 5 ++ .../BarcodeScannerOptions+toDomain.swift | 19 ++++++ .../BarcodeScanningResult+toNitro.swift | 24 ++++++++ ...meraOrientation+asUIImageOrientation.swift | 16 +++++ .../Hybrid/DomainBoundingBox+toNitro.swift | 27 ++++++++ ios/Bridge/Hybrid/DomainCorner+toNitro.swift | 11 ++++ .../DomainTextRecognitionResult+toNitro.swift | 61 +++++++++++++++++++ .../Hybrid/Double+toNullableVariant.swift | 11 ++++ ios/Bridge/Hybrid/HybridBarcodeScanner.swift | 29 ++++++++- ios/Bridge/Hybrid/HybridTextRecognizer.swift | 29 ++++++++- .../TextRecognitionLanguage+toDomain.swift | 16 +++++ .../TextRecognizerOptions+toDomain.swift | 18 ++++++ ios/Domain/Models/BarcodeScanningResult.swift | 4 +- ...ndingBox.swift => DomainBoundingBox.swift} | 2 +- .../{Corner.swift => DomainCorner.swift} | 2 +- ...entation.swift => DomainOrientation.swift} | 6 +- .../Models/DomainTextRecognitionResult.swift | 43 +++++++++++++ .../Models/ImagePreprocessingOptions.swift | 4 +- .../Models/TextRecognitionOptions.swift | 6 +- ios/Domain/Models/TextRecognitionResult.swift | 43 ------------- ios/Domain/Services/IImagePreprocessor.swift | 2 +- .../Image/ImagePreprocessor.swift | 41 ++++++++----- .../MLKit/Adapters/MLKitBarcodeAdapter.swift | 12 ++-- .../MLKit/Adapters/MLKitTextAdapter.swift | 24 ++++---- .../BarcodeScanningServiceFactory.swift | 14 +++-- .../TextRecognitionServiceFactory.swift | 6 +- .../MLKit/MLKitTextRecognitionService.swift | 4 +- .../BarcodeScanningSerializer.swift | 4 +- .../TextRecognitionSerializer.swift | 22 +++---- 34 files changed, 398 insertions(+), 127 deletions(-) create mode 100644 ios/Bridge/Hybrid/BarcodeFormat+toDomain.swift create mode 100644 ios/Bridge/Hybrid/BarcodeScannerOptions+toDomain.swift create mode 100644 ios/Bridge/Hybrid/BarcodeScanningResult+toNitro.swift create mode 100644 ios/Bridge/Hybrid/CameraOrientation+asUIImageOrientation.swift create mode 100644 ios/Bridge/Hybrid/DomainBoundingBox+toNitro.swift create mode 100644 ios/Bridge/Hybrid/DomainCorner+toNitro.swift create mode 100644 ios/Bridge/Hybrid/DomainTextRecognitionResult+toNitro.swift create mode 100644 ios/Bridge/Hybrid/Double+toNullableVariant.swift create mode 100644 ios/Bridge/Hybrid/TextRecognitionLanguage+toDomain.swift create mode 100644 ios/Bridge/Hybrid/TextRecognizerOptions+toDomain.swift rename ios/Domain/Models/{BoundingBox.swift => DomainBoundingBox.swift} (88%) rename ios/Domain/Models/{Corner.swift => DomainCorner.swift} (70%) rename ios/Domain/Models/{Orientation.swift => DomainOrientation.swift} (93%) create mode 100644 ios/Domain/Models/DomainTextRecognitionResult.swift delete mode 100644 ios/Domain/Models/TextRecognitionResult.swift diff --git a/.codex/config.toml b/.codex/config.toml index bc1ee67..6140da8 100644 --- a/.codex/config.toml +++ b/.codex/config.toml @@ -5,10 +5,6 @@ approval_policy = "on-request" sandbox_mode = "workspace-write" web_search = "live" -[mcp_servers.github] -command = "npx" -args = ["-y", "@modelcontextprotocol/server-github"] - [mcp_servers.context7] command = "npx" args = ["-y", "@upstash/context7-mcp@latest"] diff --git a/ios/Application/UseCases/RecognizeBarcodesUseCase.swift b/ios/Application/UseCases/RecognizeBarcodesUseCase.swift index 98d1bf4..43d3059 100644 --- a/ios/Application/UseCases/RecognizeBarcodesUseCase.swift +++ b/ios/Application/UseCases/RecognizeBarcodesUseCase.swift @@ -16,7 +16,7 @@ import Foundation self.recognitionService = recognitionService } - func execute(frame: Frame, options: BarcodeScanningOptions) throws + func execute(frame: any HybridFrameSpec, options: BarcodeScanningOptions) throws -> BarcodeScanningResult { return try autoreleasepool { diff --git a/ios/Application/UseCases/RecognizeTextUseCase.swift b/ios/Application/UseCases/RecognizeTextUseCase.swift index 8c9f6f6..fbef864 100644 --- a/ios/Application/UseCases/RecognizeTextUseCase.swift +++ b/ios/Application/UseCases/RecognizeTextUseCase.swift @@ -16,8 +16,8 @@ import Foundation self.recognitionService = recognitionService } - func execute(frame: Frame, options: TextRecognitionOptions) throws - -> TextRecognitionResult + func execute(frame: any HybridFrameSpec, options: TextRecognitionOptions) throws + -> DomainTextRecognitionResult { // autoreleasepool at frame boundary to drain autoreleased Obj-C objects // after each frame is processed, preventing buildup across multiple frames @@ -49,7 +49,7 @@ import Foundation imageFile: URL, imageOptions: ImagePreprocessingOptions, textOptions: TextRecognitionOptions - ) throws -> TextRecognitionResult { + ) throws -> DomainTextRecognitionResult { // autoreleasepool here ensures predictable memory cleanup, // especially if this method is called repeatedly or in batches return try autoreleasepool { diff --git a/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift b/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift index 86a19e1..d43d8d4 100644 --- a/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift +++ b/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift @@ -90,7 +90,7 @@ import Foundation -> ImagePreprocessingOptions { let invertColors = options["invertColors"] as? Bool ?? false - let orientation = (options["orientation"] as? String).flatMap { Orientation(string: $0) } + let orientation = (options["orientation"] as? String).flatMap { DomainOrientation(string: $0) } let scaleFactor = CGFloat((options["scaleFactor"] as? NSNumber)?.doubleValue ?? 1.0) return ImagePreprocessingOptions( diff --git a/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift b/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift index 336a641..de791a7 100644 --- a/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift +++ b/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift @@ -5,7 +5,7 @@ import Foundation private let textRecognitionSerializer = TextRecognitionSerializer() private var cachedUseCase: RecognizeTextUseCase? - private func getRecognizeTextUseCase(for language: TextRecognitionLanguage) + private func getRecognizeTextUseCase(for language: DomainTextRecognitionLanguage) -> RecognizeTextUseCase { if let useCase = cachedUseCase { @@ -97,7 +97,7 @@ import Foundation -> ImagePreprocessingOptions { let invertColors = options["invertColors"] as? Bool ?? false - let orientation = (options["orientation"] as? String).flatMap { Orientation(string: $0) } + let orientation = (options["orientation"] as? String).flatMap { DomainOrientation(string: $0) } return ImagePreprocessingOptions( invertColors: invertColors, @@ -105,7 +105,7 @@ import Foundation ) } - private func parseLanguage(_ language: String) -> TextRecognitionLanguage { + private func parseLanguage(_ language: String) -> DomainTextRecognitionLanguage { switch language { case "LATIN": return .latin diff --git a/ios/Bridge/Hybrid/BarcodeFormat+toDomain.swift b/ios/Bridge/Hybrid/BarcodeFormat+toDomain.swift new file mode 100644 index 0000000..3daeedc --- /dev/null +++ b/ios/Bridge/Hybrid/BarcodeFormat+toDomain.swift @@ -0,0 +1,5 @@ +extension BarcodeFormat { + func toDomainBarcodeFormat() -> BarcodeFormatOption { + return BarcodeFormatOption(rawValue: stringValue) ?? .formatUnknown + } +} diff --git a/ios/Bridge/Hybrid/BarcodeScannerOptions+toDomain.swift b/ios/Bridge/Hybrid/BarcodeScannerOptions+toDomain.swift new file mode 100644 index 0000000..b459768 --- /dev/null +++ b/ios/Bridge/Hybrid/BarcodeScannerOptions+toDomain.swift @@ -0,0 +1,19 @@ +import Foundation + +extension BarcodeScannerOptions { + func toDomainBarcodeScanningOptions() -> BarcodeScanningOptions { + return BarcodeScanningOptions( + formats: formats?.map { $0.toDomainBarcodeFormat() } ?? [], + enableAllPotentialBarcodes: enableAllPotentialBarcodes ?? false, + invertColors: invertColors ?? false, + scaleFactor: CGFloat(scaleFactor ?? 1.0) + ) + } + + func toImagePreprocessingOptions() -> ImagePreprocessingOptions { + return ImagePreprocessingOptions( + invertColors: invertColors ?? false, + scaleFactor: CGFloat(scaleFactor ?? 1.0) + ) + } +} diff --git a/ios/Bridge/Hybrid/BarcodeScanningResult+toNitro.swift b/ios/Bridge/Hybrid/BarcodeScanningResult+toNitro.swift new file mode 100644 index 0000000..1f7e49c --- /dev/null +++ b/ios/Bridge/Hybrid/BarcodeScanningResult+toNitro.swift @@ -0,0 +1,24 @@ +extension BarcodeScanningResult { + func toNitroResult() -> BarcodeScannerResult { + return BarcodeScannerResult( + barcodes: barcodes.map { $0.toNitroBarcode() } + ) + } +} + +private extension BarcodeData { + func toNitroBarcode() -> BarcodeScannerBarcode { + return BarcodeScannerBarcode( + bounds: bounds?.toNitroBoundingBox(), + corners: corners?.map { $0.toNitroCorner() }, + format: Double(format), + formatName: BarcodeFormat(fromString: formatName) ?? .unknown, + valueType: Double(valueType), + valueTypeName: BarcodeValueTypeName(fromString: valueTypeName) ?? .typeUnknown, + rawValue: rawValue, + displayValue: displayValue, + rawBytes: rawBytes?.map { Double($0) }, + isPotential: isPotential + ) + } +} diff --git a/ios/Bridge/Hybrid/CameraOrientation+asUIImageOrientation.swift b/ios/Bridge/Hybrid/CameraOrientation+asUIImageOrientation.swift new file mode 100644 index 0000000..d45957b --- /dev/null +++ b/ios/Bridge/Hybrid/CameraOrientation+asUIImageOrientation.swift @@ -0,0 +1,16 @@ +import UIKit + +extension CameraOrientation { + var asUIImageOrientation: UIImage.Orientation { + switch self { + case .up: + return .up + case .right: + return .right + case .down: + return .down + case .left: + return .left + } + } +} diff --git a/ios/Bridge/Hybrid/DomainBoundingBox+toNitro.swift b/ios/Bridge/Hybrid/DomainBoundingBox+toNitro.swift new file mode 100644 index 0000000..0d4c304 --- /dev/null +++ b/ios/Bridge/Hybrid/DomainBoundingBox+toNitro.swift @@ -0,0 +1,27 @@ +private let emptyNitroBoundingBox = BoundingBox( + x: 0.0, + y: 0.0, + width: 0.0, + height: 0.0 +) + +extension Optional where Wrapped == DomainBoundingBox { + func toNitroBoundingBox() -> BoundingBox { + guard let bounds = self else { + return emptyNitroBoundingBox + } + + return bounds.toNitroBoundingBox() + } +} + +extension DomainBoundingBox { + func toNitroBoundingBox() -> BoundingBox { + return BoundingBox( + x: x, + y: y, + width: width, + height: height + ) + } +} diff --git a/ios/Bridge/Hybrid/DomainCorner+toNitro.swift b/ios/Bridge/Hybrid/DomainCorner+toNitro.swift new file mode 100644 index 0000000..30d188e --- /dev/null +++ b/ios/Bridge/Hybrid/DomainCorner+toNitro.swift @@ -0,0 +1,11 @@ +extension DomainCorner { + func toNitroCorner() -> Corner { + return Corner(x: x, y: y) + } +} + +extension Optional where Wrapped == [DomainCorner] { + func toNitroCorners() -> [Corner] { + return self?.map { $0.toNitroCorner() } ?? [] + } +} diff --git a/ios/Bridge/Hybrid/DomainTextRecognitionResult+toNitro.swift b/ios/Bridge/Hybrid/DomainTextRecognitionResult+toNitro.swift new file mode 100644 index 0000000..9565af9 --- /dev/null +++ b/ios/Bridge/Hybrid/DomainTextRecognitionResult+toNitro.swift @@ -0,0 +1,61 @@ +extension DomainTextRecognitionResult { + func toNitroResult() -> TextRecognitionResult { + return TextRecognitionResult( + text: text ?? "", + blocks: blocks.map { $0.toNitroTextBlock() } + ) + } +} + +private extension DomainTextBlock { + func toNitroTextBlock() -> TextBlock { + return TextBlock( + bounds: bounds.toNitroBoundingBox(), + corners: corners.toNitroCorners(), + languages: languages, + text: text, + lines: lines.map { $0.toNitroTextLine() } + ) + } +} + +private extension DomainTextLine { + func toNitroTextLine() -> TextLine { + return TextLine( + bounds: bounds.toNitroBoundingBox(), + corners: corners.toNitroCorners(), + confidence: confidence.toNullableDoubleVariant(), + angle: angle.toNullableDoubleVariant(), + languages: languages, + text: text, + elements: elements.map { $0.toNitroTextElement() } + ) + } +} + +private extension DomainTextElement { + func toNitroTextElement() -> TextElement { + return TextElement( + bounds: bounds.toNitroBoundingBox(), + corners: corners.toNitroCorners(), + confidence: confidence.toNullableDoubleVariant(), + angle: angle.toNullableDoubleVariant(), + languages: languages, + text: text, + symbols: symbols.map { $0.toNitroTextSymbol() } + ) + } +} + +private extension DomainTextSymbol { + func toNitroTextSymbol() -> TextSymbol { + return TextSymbol( + bounds: bounds.toNitroBoundingBox(), + corners: corners.toNitroCorners(), + confidence: confidence ?? 0.0, + angle: angle ?? 0.0, + languages: languages, + text: text + ) + } +} diff --git a/ios/Bridge/Hybrid/Double+toNullableVariant.swift b/ios/Bridge/Hybrid/Double+toNullableVariant.swift new file mode 100644 index 0000000..28bbbda --- /dev/null +++ b/ios/Bridge/Hybrid/Double+toNullableVariant.swift @@ -0,0 +1,11 @@ +import NitroModules + +extension Optional where Wrapped == Double { + func toNullableDoubleVariant() -> Variant_NullType_Double { + guard let value = self else { + return .first(NullType.null) + } + + return .second(value) + } +} diff --git a/ios/Bridge/Hybrid/HybridBarcodeScanner.swift b/ios/Bridge/Hybrid/HybridBarcodeScanner.swift index 2bb85b2..07cca0d 100644 --- a/ios/Bridge/Hybrid/HybridBarcodeScanner.swift +++ b/ios/Bridge/Hybrid/HybridBarcodeScanner.swift @@ -4,12 +4,39 @@ import VisionCamera final class HybridBarcodeScanner: HybridBarcodeScannerSpec { private let options: BarcodeScannerOptions + #if MLKIT_BARCODE_SCANNING + private let imagePreprocessor = ImagePreprocessor() + private let barcodeOptions: BarcodeScanningOptions + private let imageOptions: ImagePreprocessingOptions + private let recognitionService: MLKitBarcodeScanningService + #endif init(options: BarcodeScannerOptions) { self.options = options + #if MLKIT_BARCODE_SCANNING + let resolvedBarcodeOptions = options.toDomainBarcodeScanningOptions() + self.barcodeOptions = resolvedBarcodeOptions + self.imageOptions = options.toImagePreprocessingOptions() + self.recognitionService = BarcodeScanningServiceFactory.create( + options: resolvedBarcodeOptions + ) + #endif } func recognize(frame: any HybridFrameSpec, args: MLKitBaseArguments?) throws -> BarcodeScannerResult { - throw RuntimeError.error(withMessage: "VisionCamera v5 barcode frame processing is not implemented yet.") + #if MLKIT_BARCODE_SCANNING + guard + let processedImage = imagePreprocessor.preprocessFrame( + frame: frame, + options: imageOptions + ) + else { + throw RuntimeError.error(withMessage: "Failed to preprocess VisionCamera frame.") + } + + return try recognitionService.recognize(image: processedImage).toNitroResult() + #else + throw RuntimeError.error(withMessage: "BarcodeScanning is not enabled in the native ML Kit configuration.") + #endif } } diff --git a/ios/Bridge/Hybrid/HybridTextRecognizer.swift b/ios/Bridge/Hybrid/HybridTextRecognizer.swift index 31cba26..9e5e933 100644 --- a/ios/Bridge/Hybrid/HybridTextRecognizer.swift +++ b/ios/Bridge/Hybrid/HybridTextRecognizer.swift @@ -4,12 +4,39 @@ import VisionCamera final class HybridTextRecognizer: HybridTextRecognizerSpec { private let options: TextRecognizerOptions + #if MLKIT_TEXT_RECOGNITION_ANY + private let imagePreprocessor = ImagePreprocessor() + private let textOptions: TextRecognitionOptions + private let imageOptions: ImagePreprocessingOptions + private let recognitionService: MLKitTextRecognitionService + #endif init(options: TextRecognizerOptions) { self.options = options + #if MLKIT_TEXT_RECOGNITION_ANY + let resolvedTextOptions = options.toDomainTextRecognitionOptions() + self.textOptions = resolvedTextOptions + self.imageOptions = options.toImagePreprocessingOptions() + self.recognitionService = TextRecognitionServiceFactory.create( + language: resolvedTextOptions.language + ) + #endif } func recognize(frame: any HybridFrameSpec, args: MLKitBaseArguments?) throws -> TextRecognitionResult { - throw RuntimeError.error(withMessage: "VisionCamera v5 text recognition frame processing is not implemented yet.") + #if MLKIT_TEXT_RECOGNITION_ANY + guard + let processedImage = imagePreprocessor.preprocessFrame( + frame: frame, + options: imageOptions + ) + else { + throw RuntimeError.error(withMessage: "Failed to preprocess VisionCamera frame.") + } + + return try recognitionService.recognize(image: processedImage).toNitroResult() + #else + throw RuntimeError.error(withMessage: "TextRecognition is not enabled in the native ML Kit configuration.") + #endif } } diff --git a/ios/Bridge/Hybrid/TextRecognitionLanguage+toDomain.swift b/ios/Bridge/Hybrid/TextRecognitionLanguage+toDomain.swift new file mode 100644 index 0000000..5ffc947 --- /dev/null +++ b/ios/Bridge/Hybrid/TextRecognitionLanguage+toDomain.swift @@ -0,0 +1,16 @@ +extension TextRecognitionLanguage { + func toDomainLanguage() -> DomainTextRecognitionLanguage { + switch self { + case .chinese: + return .chinese + case .devanagari: + return .devanagari + case .japanese: + return .japanese + case .korean: + return .korean + case .latin: + return .latin + } + } +} diff --git a/ios/Bridge/Hybrid/TextRecognizerOptions+toDomain.swift b/ios/Bridge/Hybrid/TextRecognizerOptions+toDomain.swift new file mode 100644 index 0000000..5656281 --- /dev/null +++ b/ios/Bridge/Hybrid/TextRecognizerOptions+toDomain.swift @@ -0,0 +1,18 @@ +import Foundation + +extension TextRecognizerOptions { + func toDomainTextRecognitionOptions() -> TextRecognitionOptions { + return TextRecognitionOptions( + language: language?.toDomainLanguage() ?? .latin, + invertColors: invertColors ?? false, + scaleFactor: CGFloat(scaleFactor ?? 1.0) + ) + } + + func toImagePreprocessingOptions() -> ImagePreprocessingOptions { + return ImagePreprocessingOptions( + invertColors: invertColors ?? false, + scaleFactor: CGFloat(scaleFactor ?? 1.0) + ) + } +} diff --git a/ios/Domain/Models/BarcodeScanningResult.swift b/ios/Domain/Models/BarcodeScanningResult.swift index 270bda9..ea5be7a 100644 --- a/ios/Domain/Models/BarcodeScanningResult.swift +++ b/ios/Domain/Models/BarcodeScanningResult.swift @@ -6,8 +6,8 @@ import Foundation } struct BarcodeData { - let bounds: BoundingBox? - let corners: [Corner]? + let bounds: DomainBoundingBox? + let corners: [DomainCorner]? let format: Int let formatName: String let valueType: Int diff --git a/ios/Domain/Models/BoundingBox.swift b/ios/Domain/Models/DomainBoundingBox.swift similarity index 88% rename from ios/Domain/Models/BoundingBox.swift rename to ios/Domain/Models/DomainBoundingBox.swift index 471026d..5ed8218 100644 --- a/ios/Domain/Models/BoundingBox.swift +++ b/ios/Domain/Models/DomainBoundingBox.swift @@ -1,6 +1,6 @@ import Foundation -struct BoundingBox { +struct DomainBoundingBox { let x: Double let y: Double let centerX: Double diff --git a/ios/Domain/Models/Corner.swift b/ios/Domain/Models/DomainCorner.swift similarity index 70% rename from ios/Domain/Models/Corner.swift rename to ios/Domain/Models/DomainCorner.swift index 3c87553..ecd5841 100644 --- a/ios/Domain/Models/Corner.swift +++ b/ios/Domain/Models/DomainCorner.swift @@ -1,6 +1,6 @@ import Foundation -struct Corner { +struct DomainCorner { let x: Double let y: Double } diff --git a/ios/Domain/Models/Orientation.swift b/ios/Domain/Models/DomainOrientation.swift similarity index 93% rename from ios/Domain/Models/Orientation.swift rename to ios/Domain/Models/DomainOrientation.swift index 1b099ad..49ce536 100644 --- a/ios/Domain/Models/Orientation.swift +++ b/ios/Domain/Models/DomainOrientation.swift @@ -16,7 +16,7 @@ import Foundation } #endif -@objc public enum Orientation: Int { +@objc public enum DomainOrientation: Int { case portrait case portraitUpsideDown case landscapeLeft @@ -32,9 +32,9 @@ import Foundation } } - /// Maps a UIImage.Orientation (typically from EXIF metadata) back to an Orientation value. + /// Maps a UIImage.Orientation (typically from EXIF metadata) back to a DomainOrientation value. public static func fromUIImageOrientation(_ imageOrientation: NativeImageOrientation) - -> Orientation + -> DomainOrientation { switch imageOrientation { case .up, .upMirrored: return .portrait diff --git a/ios/Domain/Models/DomainTextRecognitionResult.swift b/ios/Domain/Models/DomainTextRecognitionResult.swift new file mode 100644 index 0000000..35aaeac --- /dev/null +++ b/ios/Domain/Models/DomainTextRecognitionResult.swift @@ -0,0 +1,43 @@ +import Foundation + +struct DomainTextRecognitionResult { + let text: String? + let blocks: [DomainTextBlock] +} + +struct DomainTextBlock { + let text: String + let bounds: DomainBoundingBox? + let corners: [DomainCorner]? + let lines: [DomainTextLine] + let languages: [String] +} + +struct DomainTextLine { + let text: String + let bounds: DomainBoundingBox? + let corners: [DomainCorner]? + let elements: [DomainTextElement] + let confidence: Double? + let angle: Double? + let languages: [String] +} + +struct DomainTextElement { + let text: String + let bounds: DomainBoundingBox? + let corners: [DomainCorner]? + let symbols: [DomainTextSymbol] + let confidence: Double? + let angle: Double? + let languages: [String] +} + +struct DomainTextSymbol { + let text: String + let bounds: DomainBoundingBox? + let corners: [DomainCorner]? + let confidence: Double? + let angle: Double? + let languages: [String] +} diff --git a/ios/Domain/Models/ImagePreprocessingOptions.swift b/ios/Domain/Models/ImagePreprocessingOptions.swift index cfceae6..d84ca41 100644 --- a/ios/Domain/Models/ImagePreprocessingOptions.swift +++ b/ios/Domain/Models/ImagePreprocessingOptions.swift @@ -4,13 +4,13 @@ public struct ImagePreprocessingOptions { let invertColors: Bool let outputOrientation: OutputOrientation? let scaleFactor: CGFloat - let orientation: Orientation? + let orientation: DomainOrientation? public init( invertColors: Bool = false, outputOrientation: OutputOrientation? = nil, scaleFactor: CGFloat = 1.0, - orientation: Orientation? = nil + orientation: DomainOrientation? = nil ) { self.invertColors = invertColors self.outputOrientation = outputOrientation diff --git a/ios/Domain/Models/TextRecognitionOptions.swift b/ios/Domain/Models/TextRecognitionOptions.swift index 7e396ba..424c2f1 100644 --- a/ios/Domain/Models/TextRecognitionOptions.swift +++ b/ios/Domain/Models/TextRecognitionOptions.swift @@ -1,13 +1,13 @@ import Foundation struct TextRecognitionOptions { - let language: TextRecognitionLanguage + let language: DomainTextRecognitionLanguage let invertColors: Bool let outputOrientation: OutputOrientation? let scaleFactor: CGFloat init( - language: TextRecognitionLanguage = .latin, + language: DomainTextRecognitionLanguage = .latin, invertColors: Bool = false, outputOrientation: OutputOrientation? = nil, scaleFactor: CGFloat = 1.0 @@ -19,7 +19,7 @@ struct TextRecognitionOptions { } } -enum TextRecognitionLanguage: String { +enum DomainTextRecognitionLanguage: String { case latin = "LATIN" case chinese = "CHINESE" case devanagari = "DEVANAGARI" diff --git a/ios/Domain/Models/TextRecognitionResult.swift b/ios/Domain/Models/TextRecognitionResult.swift deleted file mode 100644 index 5a2f2ce..0000000 --- a/ios/Domain/Models/TextRecognitionResult.swift +++ /dev/null @@ -1,43 +0,0 @@ -import Foundation - -struct TextRecognitionResult { - let text: String? - let blocks: [TextBlock] -} - -struct TextBlock { - let text: String - let bounds: BoundingBox? - let corners: [Corner]? - let lines: [TextLine] - let languages: [String] -} - -struct TextLine { - let text: String - let bounds: BoundingBox? - let corners: [Corner]? - let elements: [TextElement] - let confidence: Double? - let angle: Double? - let languages: [String] -} - -struct TextElement { - let text: String - let bounds: BoundingBox? - let corners: [Corner]? - let symbols: [TextSymbol] - let confidence: Double? - let angle: Double? - let languages: [String] -} - -struct TextSymbol { - let text: String - let bounds: BoundingBox? - let corners: [Corner]? - let confidence: Double? - let angle: Double? - let languages: [String] -} diff --git a/ios/Domain/Services/IImagePreprocessor.swift b/ios/Domain/Services/IImagePreprocessor.swift index 4ac2d39..9df1533 100644 --- a/ios/Domain/Services/IImagePreprocessor.swift +++ b/ios/Domain/Services/IImagePreprocessor.swift @@ -4,7 +4,7 @@ import Foundation import VisionCamera protocol IImagePreprocessor { - func preprocessFrame(frame: Frame, options: ImagePreprocessingOptions) + func preprocessFrame(frame: any HybridFrameSpec, options: ImagePreprocessingOptions) -> ProcessedImage? func preprocessImage(imageFile: URL, options: ImagePreprocessingOptions) diff --git a/ios/Infrastructure/Image/ImagePreprocessor.swift b/ios/Infrastructure/Image/ImagePreprocessor.swift index df98c00..1cf38db 100644 --- a/ios/Infrastructure/Image/ImagePreprocessor.swift +++ b/ios/Infrastructure/Image/ImagePreprocessor.swift @@ -71,17 +71,26 @@ import Foundation return filter } - func preprocessFrame(frame: Frame, options: ImagePreprocessingOptions) + func preprocessFrame(frame: any HybridFrameSpec, options: ImagePreprocessingOptions) -> ProcessedImage? { + guard + let nativeFrame = frame as? NativeFrame, + let sampleBuffer = nativeFrame.sampleBuffer + else { + return nil + } + let effectiveScale = clampScale(options.scaleFactor) + let frameOrientation = frame.orientation.asUIImageOrientation let image: VisionImage if options.invertColors { guard let invertedImage = createInvertedVisionImageFromFrame( - frame: frame, + sampleBuffer: sampleBuffer, + frameOrientation: frameOrientation, outputOrientation: options.outputOrientation, scaleFactor: effectiveScale ) @@ -91,14 +100,14 @@ import Foundation image = invertedImage } else { image = createVisionImageFromFrame( - frame: frame, + sampleBuffer: sampleBuffer, + frameOrientation: frameOrientation, outputOrientation: options.outputOrientation, scaleFactor: effectiveScale ) } - let buffer: CMSampleBuffer = frame.buffer - guard let imageBuffer = CMSampleBufferGetImageBuffer(buffer) else { + guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil } @@ -111,7 +120,7 @@ import Foundation let metadata = ImageMetadata( width: width, height: height, - orientation: frame.orientation, + orientation: frameOrientation, isInverted: options.invertColors ) @@ -119,15 +128,14 @@ import Foundation } private func createVisionImageFromFrame( - frame: Frame, + sampleBuffer: CMSampleBuffer, + frameOrientation: UIImage.Orientation, outputOrientation: OutputOrientation?, scaleFactor: CGFloat ) -> VisionImage { - let buffer: CMSampleBuffer = frame.buffer - // MLKit works more reliably when frames are converted to UIImage // YUV CMSampleBuffer can have orientation and format handling issues - if let pixelBuffer = CMSampleBufferGetImageBuffer(buffer) { + if let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { let pixelFormatType = CVPixelBufferGetPixelFormatType(pixelBuffer) // Only use BGRA directly with MLKit - convert everything else @@ -148,7 +156,7 @@ import Foundation if needsConversion || scaleFactor < 1.0 { if let convertedImage = convertToSupportedFormat( pixelBuffer: pixelBuffer, - frameOrientation: frame.orientation, + frameOrientation: frameOrientation, outputOrientation: outputOrientation, scaleFactor: scaleFactor ) { @@ -160,20 +168,21 @@ import Foundation } // Fallback: Use CMSampleBuffer directly (for BGRA and scaleFactor == 1.0) - let image = VisionImage(buffer: buffer) + let image = VisionImage(buffer: sampleBuffer) image.orientation = getVisionOrientation( - frameOrientation: frame.orientation, + frameOrientation: frameOrientation, outputOrientation: outputOrientation ) return image } private func createInvertedVisionImageFromFrame( - frame: Frame, + sampleBuffer: CMSampleBuffer, + frameOrientation: UIImage.Orientation, outputOrientation: OutputOrientation?, scaleFactor: CGFloat ) -> VisionImage? { - guard let buffer = CMSampleBufferGetImageBuffer(frame.buffer) else { + guard let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil } @@ -187,7 +196,7 @@ import Foundation return createScaledVisionImage( from: invertedCIImage, scaleFactor: scaleFactor, - frameOrientation: frame.orientation, + frameOrientation: frameOrientation, outputOrientation: outputOrientation ) } diff --git a/ios/Infrastructure/MLKit/Adapters/MLKitBarcodeAdapter.swift b/ios/Infrastructure/MLKit/Adapters/MLKitBarcodeAdapter.swift index 960a6dd..6041bf6 100644 --- a/ios/Infrastructure/MLKit/Adapters/MLKitBarcodeAdapter.swift +++ b/ios/Infrastructure/MLKit/Adapters/MLKitBarcodeAdapter.swift @@ -209,14 +209,14 @@ import Foundation ] } - private static func toBoundingBox(_ rect: CGRect) -> BoundingBox { + private static func toBoundingBox(_ rect: CGRect) -> DomainBoundingBox { let offsetX = (rect.midX - ceil(rect.width)) / 2.0 let offsetY = (rect.midY - ceil(rect.height)) / 2.0 let x = rect.maxX + offsetX let y = rect.minY + offsetY - return BoundingBox( + return DomainBoundingBox( x: rect.midX + (rect.midX - x), y: rect.midY + (y - rect.midY), centerX: rect.midX, @@ -230,11 +230,11 @@ import Foundation ) } - private static func toCorner(_ point: CGPoint) -> Corner { - return Corner(x: Double(point.x), y: Double(point.y)) + private static func toCorner(_ point: CGPoint) -> DomainCorner { + return DomainCorner(x: Double(point.x), y: Double(point.y)) } - private static func formatName(_ format: BarcodeFormat) -> String { + private static func formatName(_ format: MLKitBarcodeScanning.BarcodeFormat) -> String { switch Int(format.rawValue) { case 1: return "CODE_128" case 2: return "CODE_39" @@ -253,7 +253,7 @@ import Foundation } } - private static func valueTypeName(_ valueType: BarcodeValueType) -> String { + private static func valueTypeName(_ valueType: MLKitBarcodeScanning.BarcodeValueType) -> String { switch Int(valueType.rawValue) { case 1: return "TYPE_CONTACT_INFO" case 2: return "TYPE_EMAIL" diff --git a/ios/Infrastructure/MLKit/Adapters/MLKitTextAdapter.swift b/ios/Infrastructure/MLKit/Adapters/MLKitTextAdapter.swift index 0f50c9d..8ba3aae 100644 --- a/ios/Infrastructure/MLKit/Adapters/MLKitTextAdapter.swift +++ b/ios/Infrastructure/MLKit/Adapters/MLKitTextAdapter.swift @@ -5,17 +5,17 @@ import Foundation class MLKitTextAdapter { - static func toDomain(_ mlkitText: Text?) -> TextRecognitionResult { - return TextRecognitionResult( + static func toDomain(_ mlkitText: Text?) -> DomainTextRecognitionResult { + return DomainTextRecognitionResult( text: mlkitText?.text, blocks: mlkitText?.blocks.map { toTextBlock($0) } ?? [] ) } private static func toTextBlock(_ block: MLKitTextRecognition.TextBlock) - -> TextBlock + -> DomainTextBlock { - return TextBlock( + return DomainTextBlock( text: block.text, bounds: toBoundingBox(block.frame), corners: block.cornerPoints.map { toCorner($0.cgPointValue) }, @@ -25,9 +25,9 @@ import Foundation } private static func toTextLine(_ line: MLKitTextRecognition.TextLine) - -> TextLine + -> DomainTextLine { - return TextLine( + return DomainTextLine( text: line.text, bounds: toBoundingBox(line.frame), corners: line.cornerPoints.map { toCorner($0.cgPointValue) }, @@ -39,9 +39,9 @@ import Foundation } private static func toTextElement(_ element: MLKitTextRecognition.TextElement) - -> TextElement + -> DomainTextElement { - return TextElement( + return DomainTextElement( text: element.text, bounds: toBoundingBox(element.frame), corners: element.cornerPoints.map { toCorner($0.cgPointValue) }, @@ -52,14 +52,14 @@ import Foundation ) } - private static func toBoundingBox(_ bounds: CGRect) -> BoundingBox { + private static func toBoundingBox(_ bounds: CGRect) -> DomainBoundingBox { let offsetX = (bounds.midX - ceil(bounds.width)) / 2.0 let offsetY = (bounds.midY - ceil(bounds.height)) / 2.0 let x = bounds.maxX + offsetX let y = bounds.minY + offsetY - return BoundingBox( + return DomainBoundingBox( x: bounds.midX + (bounds.midX - x), y: bounds.midY + (y - bounds.midY), centerX: bounds.midX, @@ -73,8 +73,8 @@ import Foundation ) } - private static func toCorner(_ point: CGPoint) -> Corner { - return Corner(x: Double(point.x), y: Double(point.y)) + private static func toCorner(_ point: CGPoint) -> DomainCorner { + return DomainCorner(x: Double(point.x), y: Double(point.y)) } } #endif // MLKIT_TEXT_RECOGNITION_ANY diff --git a/ios/Infrastructure/MLKit/Factories/BarcodeScanningServiceFactory.swift b/ios/Infrastructure/MLKit/Factories/BarcodeScanningServiceFactory.swift index e68261d..a51dd62 100644 --- a/ios/Infrastructure/MLKit/Factories/BarcodeScanningServiceFactory.swift +++ b/ios/Infrastructure/MLKit/Factories/BarcodeScanningServiceFactory.swift @@ -8,11 +8,13 @@ import Foundation static func createBarcodeScanner(options: BarcodeScanningOptions) -> BarcodeScanner { - let scannerOptions: BarcodeScannerOptions + let scannerOptions: MLKitBarcodeScanning.BarcodeScannerOptions if options.formats.isEmpty || options.formats.contains(.formatAllFormats) { - scannerOptions = BarcodeScannerOptions() + scannerOptions = MLKitBarcodeScanning.BarcodeScannerOptions() } else { - scannerOptions = BarcodeScannerOptions(formats: toMLKitFormats(options.formats)) + scannerOptions = MLKitBarcodeScanning.BarcodeScannerOptions( + formats: toMLKitFormats(options.formats) + ) } return BarcodeScanner.barcodeScanner(options: scannerOptions) } @@ -24,7 +26,9 @@ import Foundation return MLKitBarcodeScanningService(barcodeScanner: scanner) } - private static func toMLKitFormats(_ formats: [BarcodeFormatOption]) -> BarcodeFormat { + private static func toMLKitFormats(_ formats: [BarcodeFormatOption]) + -> MLKitBarcodeScanning.BarcodeFormat + { var rawValue = 0 for format in formats { switch format { @@ -59,7 +63,7 @@ import Foundation } } - return BarcodeFormat(rawValue: BarcodeFormat.RawValue(rawValue)) + return MLKitBarcodeScanning.BarcodeFormat(rawValue: rawValue) } } #endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Infrastructure/MLKit/Factories/TextRecognitionServiceFactory.swift b/ios/Infrastructure/MLKit/Factories/TextRecognitionServiceFactory.swift index 435d610..a5353e4 100644 --- a/ios/Infrastructure/MLKit/Factories/TextRecognitionServiceFactory.swift +++ b/ios/Infrastructure/MLKit/Factories/TextRecognitionServiceFactory.swift @@ -23,7 +23,7 @@ import Foundation #if MLKIT_TEXT_RECOGNITION_ANY class TextRecognitionServiceFactory { - static func createTextRecognizer(language: TextRecognitionLanguage) + static func createTextRecognizer(language: DomainTextRecognitionLanguage) -> TextRecognizer { let options: CommonTextRecognizerOptions @@ -31,7 +31,7 @@ import Foundation switch language { #if MLKIT_TEXT_RECOGNITION case .latin: - options = TextRecognizerOptions() + options = MLKitTextRecognition.TextRecognizerOptions() #endif #if MLKIT_TEXT_RECOGNITION_CHINESE case .chinese: @@ -54,7 +54,7 @@ import Foundation return TextRecognizer.textRecognizer(options: options) } - static func create(language: TextRecognitionLanguage = .latin) + static func create(language: DomainTextRecognitionLanguage = .latin) -> MLKitTextRecognitionService { let textRecognizer = createTextRecognizer(language: language) diff --git a/ios/Infrastructure/MLKit/MLKitTextRecognitionService.swift b/ios/Infrastructure/MLKit/MLKitTextRecognitionService.swift index 1acfb10..b1d3f50 100644 --- a/ios/Infrastructure/MLKit/MLKitTextRecognitionService.swift +++ b/ios/Infrastructure/MLKit/MLKitTextRecognitionService.swift @@ -22,7 +22,7 @@ import Foundation #if MLKIT_TEXT_RECOGNITION_ANY class MLKitTextRecognitionService: IRecognitionService { - typealias ResultType = TextRecognitionResult + typealias ResultType = DomainTextRecognitionResult private let textRecognizer: TextRecognizer @@ -30,7 +30,7 @@ import Foundation self.textRecognizer = textRecognizer } - func recognize(image: ProcessedImage) throws -> TextRecognitionResult { + func recognize(image: ProcessedImage) throws -> DomainTextRecognitionResult { let text = try textRecognizer.results(in: image.image) return MLKitTextAdapter.toDomain(text) } diff --git a/ios/Infrastructure/Serializers/BarcodeScanningSerializer.swift b/ios/Infrastructure/Serializers/BarcodeScanningSerializer.swift index 51e7122..e692f93 100644 --- a/ios/Infrastructure/Serializers/BarcodeScanningSerializer.swift +++ b/ios/Infrastructure/Serializers/BarcodeScanningSerializer.swift @@ -38,7 +38,7 @@ import Foundation return map } - private static func toBoundsMap(_ bounds: BoundingBox) -> [String: Double] { + private static func toBoundsMap(_ bounds: DomainBoundingBox) -> [String: Double] { return [ "x": bounds.x, "y": bounds.y, @@ -53,7 +53,7 @@ import Foundation ] } - private static func toCornerMap(_ corner: Corner) -> [String: Double] { + private static func toCornerMap(_ corner: DomainCorner) -> [String: Double] { return [ "x": corner.x, "y": corner.y, diff --git a/ios/Infrastructure/Serializers/TextRecognitionSerializer.swift b/ios/Infrastructure/Serializers/TextRecognitionSerializer.swift index 143ca64..47bf703 100644 --- a/ios/Infrastructure/Serializers/TextRecognitionSerializer.swift +++ b/ios/Infrastructure/Serializers/TextRecognitionSerializer.swift @@ -2,18 +2,18 @@ import Foundation class TextRecognitionSerializer { - static func toReactNativeMap(_ result: TextRecognitionResult) -> [String: Any] { + static func toReactNativeMap(_ result: DomainTextRecognitionResult) -> [String: Any] { var map = [String: Any]() map["text"] = result.text map["blocks"] = toBlocksArray(result.blocks) return map } - private static func toBlocksArray(_ blocks: [TextBlock]) -> [[String: Any]] { + private static func toBlocksArray(_ blocks: [DomainTextBlock]) -> [[String: Any]] { return blocks.map { toBlockMap($0) } } - private static func toBlockMap(_ block: TextBlock) -> [String: Any] { + private static func toBlockMap(_ block: DomainTextBlock) -> [String: Any] { var map = [String: Any]() map["text"] = block.text if let bounds = block.bounds { @@ -27,11 +27,11 @@ class TextRecognitionSerializer { return map } - private static func toLinesArray(_ lines: [TextLine]) -> [[String: Any]] { + private static func toLinesArray(_ lines: [DomainTextLine]) -> [[String: Any]] { return lines.map { toLineMap($0) } } - private static func toLineMap(_ line: TextLine) -> [String: Any] { + private static func toLineMap(_ line: DomainTextLine) -> [String: Any] { var map = [String: Any]() map["text"] = line.text if let bounds = line.bounds { @@ -47,13 +47,13 @@ class TextRecognitionSerializer { return map } - private static func toElementsArray(_ elements: [TextElement]) -> [[String: + private static func toElementsArray(_ elements: [DomainTextElement]) -> [[String: Any]] { return elements.map { toElementMap($0) } } - private static func toElementMap(_ element: TextElement) -> [String: Any] { + private static func toElementMap(_ element: DomainTextElement) -> [String: Any] { var map = [String: Any]() map["text"] = element.text if let bounds = element.bounds { @@ -69,7 +69,7 @@ class TextRecognitionSerializer { return map } - private static func toSymbolMap(_ symbol: TextSymbol) -> [String: Any] { + private static func toSymbolMap(_ symbol: DomainTextSymbol) -> [String: Any] { var map = [String: Any]() map["text"] = symbol.text if let bounds = symbol.bounds { @@ -84,7 +84,7 @@ class TextRecognitionSerializer { return map } - private static func toBoundsMap(_ bounds: BoundingBox) -> [String: Double] { + private static func toBoundsMap(_ bounds: DomainBoundingBox) -> [String: Double] { return [ "x": bounds.x, "y": bounds.y, @@ -99,11 +99,11 @@ class TextRecognitionSerializer { ] } - private static func toCornersArray(_ corners: [Corner]) -> [[String: Double]] { + private static func toCornersArray(_ corners: [DomainCorner]) -> [[String: Double]] { return corners.map { toCornerMap($0) } } - private static func toCornerMap(_ corner: Corner) -> [String: Double] { + private static func toCornerMap(_ corner: DomainCorner) -> [String: Double] { return [ "x": corner.x, "y": corner.y, From e0761b261cd8ec767a370d1d53767ce46ba70a2f Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Mon, 6 Jul 2026 10:49:05 -0300 Subject: [PATCH 22/45] fix(ios): restore Nitro ML Kit example build --- example/ios/Podfile | 53 +++ example/ios/Podfile.lock | 309 ++++++++++++------ ...meraOrientation+asUIImageOrientation.swift | 1 + .../Hybrid/DomainBoundingBox+toNitro.swift | 16 +- ios/Domain/Models/DomainOrientation.swift | 24 +- .../Models/ImagePreprocessingOptions.swift | 2 +- nitro.json | 2 +- 7 files changed, 275 insertions(+), 132 deletions(-) diff --git a/example/ios/Podfile b/example/ios/Podfile index 31e7ddf..54543a4 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -72,6 +72,57 @@ target 'VisionCameraMLKitExample' do Pod::UI.puts('Applied Xcode 26 fmt workaround (FMT_USE_CONSTEVAL=0)'.yellow) end + def apply_glog_xcode_26_workaround(installer) + modulemap = File.join(installer.sandbox.root, 'Target Support Files', 'glog', 'glog.modulemap') + patched = false + + if File.exist?(modulemap) + content = File.read(modulemap) + modulemap_content = content.gsub(/\n module \* \{ export \*\ }/, '') + + if modulemap_content != content + File.write(modulemap, modulemap_content) + patched = true + end + end + + Dir.glob(File.join(installer.sandbox.root, 'Target Support Files', '**', '*.xcconfig')).each do |xcconfig| + content = File.read(xcconfig) + xcconfig_content = content + .gsub(' -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/glog/glog.modulemap"', '') + .gsub(' -fmodule-map-file="${PODS_ROOT}/Headers/Public/glog/glog.modulemap"', '') + + if xcconfig_content != content + File.write(xcconfig, xcconfig_content) + patched = true + end + end + + Pod::UI.puts('Applied Xcode 26 glog workaround'.yellow) if patched + end + + def apply_worklets_header_search_paths_workaround(installer) + header_path = '"${PODS_ROOT}/Headers/Private/React-Core"' + patched = false + + Dir.glob(File.join(installer.sandbox.root, 'Target Support Files', 'VisionCameraWorklets', '*.xcconfig')).each do |xcconfig| + content = File.read(xcconfig) + next if content.include?(header_path) + + xcconfig_content = content.gsub( + /^(HEADER_SEARCH_PATHS = .*)$/, + "\\1 #{header_path}" + ) + + next if xcconfig_content == content + + File.write(xcconfig, xcconfig_content) + patched = true + end + + Pod::UI.puts('Applied VisionCameraWorklets React-Core header path workaround'.yellow) if patched + end + post_install do |installer| # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( @@ -82,5 +133,7 @@ target 'VisionCameraMLKitExample' do ) apply_fmt_xcode_26_workaround(installer) + apply_glog_xcode_26_workaround(installer) + apply_worklets_header_search_paths_workaround(installer) end end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 4d9e30f..b43d1a4 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -95,6 +95,65 @@ PODS: - nanopb/encode (= 2.30910.0) - nanopb/decode (2.30910.0) - nanopb/encode (2.30910.0) + - NitroImage (0.15.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - NitroModules + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-callinvoker + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - NitroModules (0.36.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-callinvoker + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - PromisesObjC (2.4.0) - RCT-Folly (2024.11.18.00): - boost @@ -2036,34 +2095,6 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - react-native-worklets-core (1.6.2): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - React-NativeModulesApple (0.81.1): - boost - DoubleConversion @@ -2923,18 +2954,37 @@ PODS: - SocketRocket - Yoga - SocketRocket (0.7.1) - - VisionCamera (4.7.3): - - VisionCamera/Core (= 4.7.3) - - VisionCamera/FrameProcessors (= 4.7.3) - - VisionCamera/React (= 4.7.3) - - VisionCamera/Core (4.7.3) - - VisionCamera/FrameProcessors (4.7.3): - - React + - VisionCamera (5.1.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - NitroImage + - NitroModules + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety - React-callinvoker - - react-native-worklets-core - - VisionCamera/React (4.7.3): - React-Core - - VisionCamera/FrameProcessors + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - VisionCameraMLKit (1.0.1): - boost - DoubleConversion @@ -2948,10 +2998,42 @@ PODS: - GoogleMLKit/TextRecognitionJapanese - GoogleMLKit/TextRecognitionKorean - hermes-engine + - NitroModules + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - VisionCamera + - Yoga + - VisionCameraWorklets (5.1.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - NitroModules - RCT-Folly - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety + - React-callinvoker - React-Core - React-debug - React-Fabric @@ -2967,6 +3049,7 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core + - RNWorklets - SocketRocket - VisionCamera - Yoga @@ -2980,6 +3063,8 @@ DEPENDENCIES: - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) + - NitroImage (from `../node_modules/react-native-nitro-image`) + - NitroModules (from `../node_modules/react-native-nitro-modules`) - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) - RCTRequired (from `../node_modules/react-native/Libraries/Required`) @@ -3018,7 +3103,6 @@ DEPENDENCIES: - react-native-image-picker (from `../node_modules/react-native-image-picker`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - "react-native-slider (from `../node_modules/@react-native-community/slider`)" - - react-native-worklets-core (from `../node_modules/react-native-worklets-core`) - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - React-oscompat (from `../node_modules/react-native/ReactCommon/oscompat`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) @@ -3058,6 +3142,7 @@ DEPENDENCIES: - SocketRocket (~> 0.7.1) - VisionCamera (from `../node_modules/react-native-vision-camera`) - VisionCameraMLKit (from `../..`) + - VisionCameraWorklets (from `../node_modules/react-native-vision-camera-worklets`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: @@ -3098,6 +3183,10 @@ EXTERNAL SOURCES: hermes-engine: :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" :tag: hermes-2025-07-07-RNv0.81.0-e0fc67142ec0763c6b6153ca2bf96df815539782 + NitroImage: + :path: "../node_modules/react-native-nitro-image" + NitroModules: + :path: "../node_modules/react-native-nitro-modules" RCT-Folly: :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTDeprecation: @@ -3172,8 +3261,6 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-safe-area-context" react-native-slider: :path: "../node_modules/@react-native-community/slider" - react-native-worklets-core: - :path: "../node_modules/react-native-worklets-core" React-NativeModulesApple: :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" React-oscompat: @@ -3250,6 +3337,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-vision-camera" VisionCameraMLKit: :path: "../.." + VisionCameraWorklets: + :path: "../node_modules/react-native-vision-camera-worklets" Yoga: :path: "../node_modules/react-native/ReactCommon/yoga" @@ -3278,6 +3367,8 @@ SPEC CHECKSUMS: MLKitTextRecognitionKorean: 4877b6a8d95cd8846586e83f85b2f3e9ac93a737 MLKitVision: e858c5f125ecc288e4a31127928301eaba9ae0c1 nanopb: 438bc412db1928dac798aa6fd75726007be04262 + NitroImage: cef8d9c91d6d9247ec0569bb68148f7fba717026 + NitroModules: 8d376059cb66130ebac6a84982c068d69012b567 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f RCTDeprecation: c4b9e2fd0ab200e3af72b013ed6113187c607077 @@ -3285,79 +3376,79 @@ SPEC CHECKSUMS: RCTTypeSafety: 720403058b7c1380c6a3ae5706981d6362962c89 React: f1486d005993b0af01943af1850d3d4f3b597545 React-callinvoker: 133f69368c8559e744efa345223625d412f5dfbe - React-Core: d6d8c1fd33697cec596d33b820456505ee305686 - React-CoreModules: 81ab751a7668ba161440f9623b994e1a6a3019fe - React-cxxreact: 16f2a2751d0dce8b569f23c1914edc90f655b01b + React-Core: 559823921b4f294c2840fa8238ca958a29ddc211 + React-CoreModules: c41e7bbfabbc420783bb926f45837a0d5e53341e + React-cxxreact: 9cb9fa738274a1b36b97ede09c8a6717dec1a20b React-debug: e01581e1589f329e61c95b332bf7f4969b10564b - React-defaultsnativemodule: e956b1d8fe15cc79d23061db229bf88170565f2f - React-domnativemodule: a18b0f7a31b9c75f12fa369baece5542d1265b36 - React-Fabric: c0237a32c3c0dbea2d2b294c8e95605e1dfe2f57 - React-FabricComponents: 65b03884bd5d9f24c79a631d7d26f0fa079bc4aa - React-FabricImage: de1ea2f2a0b32ad02e5cbb64827d1eec0439cf0d - React-featureflags: 02de9c35256cc624269b01d670d99e1fd706ea8d - React-featureflagsnativemodule: 8b84e67edbaa7b9318390c5bd3ae19790a74f356 - React-graphics: 004b40c1b236ea3bb8de6693439bef9797922ba9 - React-hermes: 2179a018b2f86652f6f33ef23efd9e5ac284b247 - React-idlecallbacksnativemodule: f54ea68f984b12e42feed1e7110623b2c38df4d1 - React-ImageManager: 9dd04b7b62bc5397f876ca5fb1b712e700ce390c - React-jserrorhandler: 2f90bf50fffea1d012e7f3d717c6adf748b1813d - React-jsi: b27208f8866e53238534f65f304903e4eff25e05 - React-jsiexecutor: 1d3e827797f592c393860dea91aaa6d53c7715e7 - React-jsinspector: bda319277ae779bc476b736fe3a497c6aed304cd - React-jsinspectorcdp: 69e1736edfd5420037680b7b4557fa748c3c8216 - React-jsinspectornetwork: 7aa707b057c6129b4af59e0c9160436bbab25022 - React-jsinspectortracing: b4a8a328ad2697f9638daa4b7cc054e0303fa47f - React-jsitooling: a6c7e2829437b28665e97a398b3374d443125e24 - React-jsitracing: d87ae17dd0eef7844e605945da926c5433fe2b51 - React-logger: d27dd2000f520bf891d24f6e141cde34df41f0ee - React-Mapbuffer: 0746ffab5ac0f49b7c9347338e3d0c1d9dd634c8 - React-microtasksnativemodule: b0fb3f97372df39bda3e657536039f1af227cc29 - react-native-blur: ba0e9ad6274783c8d45f42da82acae02e25784ad - react-native-image-picker: 43e6cd4231e670030fe09b079d696fa5a634ccfc - react-native-safe-area-context: 54d812805f3c4e08a4580ad086cbde1d8780c2e4 - react-native-slider: 5da4af460d0dd1346f927e829dfecbb81bd46f91 - react-native-worklets-core: 5abf7405ae179fd6b3a45899b1a0e5777aa389d3 - React-NativeModulesApple: 9ec9240159974c94886ebbe4caec18e3395f6aef + React-defaultsnativemodule: bbb39447caa6b6cf9405fa0099f828c083640faa + React-domnativemodule: 03744d12b6d56d098531a933730bf1d4cb79bdfb + React-Fabric: 530b3993a12a96e8a7cdb9f0ef48e605277b572e + React-FabricComponents: 271ec2a9b2c00ac66fd6d1fd24e9e964d907751d + React-FabricImage: d0af66e976dbab7f8b81e36dd369fc70727d2695 + React-featureflags: 269704c8eff86e0485c9d384e286350fcda6eb70 + React-featureflagsnativemodule: db1e5d88a912fb08a5ece33fcf64e1b732da8467 + React-graphics: b19d03a01b0722b4dc82f47acb56dc3ed41937e7 + React-hermes: 811606c0aca5a3f9c6fa8e4994e02ca8f677e68e + React-idlecallbacksnativemodule: 3a3df629cd50046c7e4354f9025aefe8f2c84601 + React-ImageManager: 0d53866c63132791e37bb2373f93044fdef14aa3 + React-jserrorhandler: d5700d6ab7162fd575287502a3c5d601d98e7f09 + React-jsi: ece95417fedbed0e7153a855cb8342b7c72ab75e + React-jsiexecutor: 2b0bb644b533df2f5c0cd6ade9a4560d0bf1dd84 + React-jsinspector: 0c160f8510a8852bdf2dac12f0b1949efc18200b + React-jsinspectorcdp: f4b84409f453f61ddd8614ad45139bc594ec6bb5 + React-jsinspectornetwork: 8f2f0ca8c871ca19b571f426002c0012e7fb2aee + React-jsinspectortracing: 33f6b977eb8a4bc1e3d1a4b948809aca083143f9 + React-jsitooling: 2c61529b589e17229a9f0a4a4fc35aa7ad495850 + React-jsitracing: 838a7b0c013c4aff7d382d7fdc78cf442013ba1d + React-logger: 7aef4d74123e5e3d267e5af1fbf5135b5a0d8381 + React-Mapbuffer: 91e0eab42a6ae7f3e34091a126d70fc53bd3823e + React-microtasksnativemodule: 1ead4fe154df3b1ba34b5a9e35ef3c4bdfa72ccb + react-native-blur: 6af83e7e3c4c1446a188d9b2c493600fc4beb173 + react-native-image-picker: 0314366753615115fa55c3cc937ac44cb7e75702 + react-native-safe-area-context: c00143b4823773bba23f2f19f85663ae89ceb460 + react-native-slider: 34064ca1a6864d7b263e44dd76a2d794e8d26744 + React-NativeModulesApple: eff2eba56030eb0d107b1642b8f853bc36a833ac React-oscompat: b12c633e9c00f1f99467b1e0e0b8038895dae436 - React-perflogger: ccf4fd2664b00818645e588623c7531a8b32d114 - React-performancetimeline: a866ba759d8e06e9ba174b4421677edcae487baf + React-perflogger: 58d12c4e5df1403030c97b9c621375c312cca454 + React-performancetimeline: 0ee0a3236c77a4ee6d8a6189089e41e4003d292e React-RCTActionSheet: 3f741a3712653611a6bfc5abceb8260af9d0b218 - React-RCTAnimation: 2edeebfba175cc2e937e2752209ab605d3c48f21 - React-RCTAppDelegate: e292321e83ee966897244a032216a70930b758d6 - React-RCTBlob: 8dfb24b6dd4a5af45e1e59e2fd925b2df1e44d08 - React-RCTFabric: b25b02a2016f5cb15926a60c77a8d75865aa3558 - React-RCTFBReactNativeSpec: 20338571a1ed853d01da6c68576aa6e8e107b6f6 - React-RCTImage: c7fe8c2f2ae8bad98ab4d944d5d50a889da4b652 - React-RCTLinking: 9ac21ce9f1af914bb01c06af3752db2ec840d0ee - React-RCTNetwork: 09a5de71d757dbad4b3fe3615839290200b932aa - React-RCTRuntime: da3f1e0ce088c20350044cdf1efcd7f8d9b9b40c - React-RCTSettings: fee112652ac7569ea9abe910206e1685f5f9adba - React-RCTText: 7ee9d0bc16b3a8149f8df6d70c48e724d0db1d89 - React-RCTVibration: 619d613abaeb05f6fbc2b2e5e33f724f05df8eb8 + React-RCTAnimation: 408ad69ea136e99a463dd33eadecc29e586b3d72 + React-RCTAppDelegate: f03b46e80b8a3dbfa84b35abfe123e02f3ceef83 + React-RCTBlob: bd42e92a00ad22eaab92ffe5c137e7a2f725887a + React-RCTFabric: b99ab638c73cf2d57b886eafdbfb2e4909b0eb9a + React-RCTFBReactNativeSpec: 7ad9aba0e0655e3f29be0a1c3fd4a888fab04dcf + React-RCTImage: 0f1c74f7cd20027f8c34976a211b35d4263a0add + React-RCTLinking: 6d7dfc3a74110df56c3a73cc7626bf4415656542 + React-RCTNetwork: 6a25d8645a80d5b86098675ca39bf8fcf1afa08b + React-RCTRuntime: 38bfe9766565ae3293ca230bc51c9c020a8bc98a + React-RCTSettings: 651d9ae2cdd32f547ad0d225a2c13886d6ad2358 + React-RCTText: 9bc66cd288478e23195e01f5cb45eba79986b2b4 + React-RCTVibration: 371226f5667a00c76d792dcdb5c2e0fcbcde0c3b React-rendererconsistency: a05f6c37f9389c53213d1e28798e441fa6fbdbcd - React-renderercss: 3decb27a81648fcdee837c59994b51fff5be5a67 - React-rendererdebug: 3b9a92d36932af52e1b473f2a89ea4b05dbdecdf - React-RuntimeApple: 4e35fb74be4b721c2e1fd6d54ec66456fa7043e9 - React-RuntimeCore: 0fd7ac6e3e9dd20cb47e87c6b9f35832dd445d5e - React-runtimeexecutor: 7680156c9f3a5a49c688bc33f9ec5ea1b00527f5 - React-RuntimeHermes: 435b7104a3c749af6251353dcb7317a8e53cbd73 - React-runtimescheduler: 8056b916168e446ea44531883928034e62e76a81 - React-timing: 36da85e32e53008ce73f87528818191e7f2508ba - React-utils: 71e53d55ce778c6e7c7c9db4b1b9d63ef8f55289 - ReactAppDependencyProvider: 448b422f8af1dedf81374eacc90a15439a0ed7f5 - ReactCodegen: 3baedb0c33f963250c866151b825a3c5194b12f1 - ReactCommon: e897f9a1b4afab370cfefaaf5fb3c80371bc3937 - RNGestureHandler: 92ad734ef0da16d69d0a325b7e1e9ae80bdce1f9 - RNPermissions: ff3bee717e3df88d22727cc9ee672d9401c8108e - RNReanimated: f2cdef8c5ec70e440498b949f9af3ea39f70fcec - RNScreens: b2a5c76af24a02a2fd71bfce42780fdd9c79cc6d - RNSVG: 046b3f72f0ddbe895a88cdd3bfc3b19822c9efab - RNWorklets: ab7740c2a152f77ff76a40d52be860d8f128fab1 + React-renderercss: 6e4febfa014b0f53bc171a62b0f713ddbdbb9860 + React-rendererdebug: e94bf27b9d55ef2795caa8e43aa92abc4a373b8b + React-RuntimeApple: 723be5159519eba1cd92449acb29436d21571b82 + React-RuntimeCore: f58eb0f01065c9d27d91de10b2e4ab4c76d83b0e + React-runtimeexecutor: f615ec8742d0b5820170f7c8b4d2c7cb75d93ac9 + React-RuntimeHermes: fddb258e03d330d1132bb19e78fe51ac2f3f41ac + React-runtimescheduler: e92a31460e654ced8587debeec37553315e1b6a5 + React-timing: 97ada2c47b4c5932e7f773c7d239c52b90d6ca68 + React-utils: f0949d247a46b4c09f03e5a3cb1167602d0b729a + ReactAppDependencyProvider: 3eb9096cb139eb433965693bbe541d96eb3d3ec9 + ReactCodegen: 4d203eddf6f977caa324640a20f92e70408d648b + ReactCommon: ce5d4226dfaf9d5dacbef57b4528819e39d3a120 + RNGestureHandler: e37bdb684df1ac17c7e1d8f71a3311b2793c186b + RNPermissions: 455d17943b9aa25ea450fe02802bb9ea99354427 + RNReanimated: fabac5a7aee92d454d7aef06335a7bef955d5603 + RNScreens: 714e10b6b554f7dc7ad9f78dcf36dc8e3fc73415 + RNSVG: d260beec1775108e1bd065a0ed666ff2c5565158 + RNWorklets: 416ef974c176d76634e34c0aeda83f6b67084a88 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - VisionCamera: 47f10351a1b6a8d251b90986ae72ca5f52331954 - VisionCameraMLKit: 771681486e3ec5b6590e28170f9ed74248b8912c + VisionCamera: 8407cd5a5a61543e8b35b1a9dab1ee39dc025130 + VisionCameraMLKit: c5215e7401725161b8a5b61f1b87f6aef4e25913 + VisionCameraWorklets: 425cff0639eaf1669efd11d02b89b969fe3b4fbc Yoga: fa23995c18b65978347b096d0836f4f5093df545 -PODFILE CHECKSUM: 0180fc6b621205cc4ae68612463a743f47d460b3 +PODFILE CHECKSUM: a0675b0805e9ef23607b6c81c757f2485c98537e -COCOAPODS: 1.16.2 +COCOAPODS: 1.15.2 diff --git a/ios/Bridge/Hybrid/CameraOrientation+asUIImageOrientation.swift b/ios/Bridge/Hybrid/CameraOrientation+asUIImageOrientation.swift index d45957b..3f02335 100644 --- a/ios/Bridge/Hybrid/CameraOrientation+asUIImageOrientation.swift +++ b/ios/Bridge/Hybrid/CameraOrientation+asUIImageOrientation.swift @@ -1,4 +1,5 @@ import UIKit +import VisionCamera extension CameraOrientation { var asUIImageOrientation: UIImage.Orientation { diff --git a/ios/Bridge/Hybrid/DomainBoundingBox+toNitro.swift b/ios/Bridge/Hybrid/DomainBoundingBox+toNitro.swift index 0d4c304..221b821 100644 --- a/ios/Bridge/Hybrid/DomainBoundingBox+toNitro.swift +++ b/ios/Bridge/Hybrid/DomainBoundingBox+toNitro.swift @@ -1,8 +1,14 @@ private let emptyNitroBoundingBox = BoundingBox( x: 0.0, y: 0.0, + centerX: 0.0, + centerY: 0.0, width: 0.0, - height: 0.0 + height: 0.0, + top: 0.0, + left: 0.0, + bottom: 0.0, + right: 0.0 ) extension Optional where Wrapped == DomainBoundingBox { @@ -20,8 +26,14 @@ extension DomainBoundingBox { return BoundingBox( x: x, y: y, + centerX: centerX, + centerY: centerY, width: width, - height: height + height: height, + top: top, + left: left, + bottom: bottom, + right: right ) } } diff --git a/ios/Domain/Models/DomainOrientation.swift b/ios/Domain/Models/DomainOrientation.swift index 49ce536..a0cb59a 100644 --- a/ios/Domain/Models/DomainOrientation.swift +++ b/ios/Domain/Models/DomainOrientation.swift @@ -1,28 +1,14 @@ import Foundation -#if canImport(UIKit) - import UIKit - public typealias NativeImageOrientation = UIImage.Orientation -#else - public enum NativeImageOrientation { - case up - case down - case left - case right - case upMirrored - case downMirrored - case leftMirrored - case rightMirrored - } -#endif +import UIKit -@objc public enum DomainOrientation: Int { +enum DomainOrientation: Int { case portrait case portraitUpsideDown case landscapeLeft case landscapeRight - public init?(string: String?) { + init?(string: String?) { switch string { case "portrait": self = .portrait case "portrait-upside-down": self = .portraitUpsideDown @@ -33,7 +19,7 @@ import Foundation } /// Maps a UIImage.Orientation (typically from EXIF metadata) back to a DomainOrientation value. - public static func fromUIImageOrientation(_ imageOrientation: NativeImageOrientation) + static func fromUIImageOrientation(_ imageOrientation: UIImage.Orientation) -> DomainOrientation { switch imageOrientation { @@ -45,7 +31,7 @@ import Foundation } } - public var asUIImageOrientation: NativeImageOrientation { + var asUIImageOrientation: UIImage.Orientation { switch self { case .portrait: return .up case .portraitUpsideDown: return .down diff --git a/ios/Domain/Models/ImagePreprocessingOptions.swift b/ios/Domain/Models/ImagePreprocessingOptions.swift index d84ca41..68a2b1a 100644 --- a/ios/Domain/Models/ImagePreprocessingOptions.swift +++ b/ios/Domain/Models/ImagePreprocessingOptions.swift @@ -6,7 +6,7 @@ public struct ImagePreprocessingOptions { let scaleFactor: CGFloat let orientation: DomainOrientation? - public init( + init( invertColors: Bool = false, outputOrientation: OutputOrientation? = nil, scaleFactor: CGFloat = 1.0, diff --git a/nitro.json b/nitro.json index ea6151d..1952026 100644 --- a/nitro.json +++ b/nitro.json @@ -20,6 +20,6 @@ } } }, - "ignorePaths": ["**/example/**"], + "ignorePaths": ["**/example/**", "**/node_modules/**"], "gitAttributesGeneratedFlag": true } From 8a7a0b7e3c541e9ab9c129980b7365ccd18048d6 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 12 Jul 2026 18:33:45 -0300 Subject: [PATCH 23/45] chore(deps): upgrade to react-native 0.86 and refresh native toolchain Bumps react-native 0.81.1 -> 0.86.0, react 19.1.0 -> 19.2.3, and related packages (reanimated, screens, svg, worklets, nitro-image, RN CLI) across the workspace and example app. Also bumps react-native-gesture-handler 2.30.0 -> 3.0.2, required because RN 0.86 (New Architecture only) removed the legacy Paper-only renderer shim (react-native/Libraries/Renderer/shims/ReactNative) that gesture-handler 2.x still imported, breaking the Metro bundle step. Regenerates native project files that follow from the upgrade: Gradle wrapper 8.14.3 -> 9.3.1, MainApplication.kt bridgeless-only template, AndroidManifest cleartext-traffic placeholder (folding the debug-only manifest into main), Podfile.lock, Xcode project settings, Info.plist, and jest preset. --- example/Gemfile | 1 + example/Gemfile.lock | 124 + example/android/app/build.gradle | 4 +- .../android/app/src/debug/AndroidManifest.xml | 5 - .../android/app/src/main/AndroidManifest.xml | 2 +- .../example/MainApplication.kt | 31 +- .../android/gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 46175 bytes .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/gradlew | 5 +- example/android/gradlew.bat | 3 +- example/ios/Podfile | 1 - example/ios/Podfile.lock | 2289 +++++++---------- .../project.pbxproj | 80 +- .../ios/VisionCameraMLKitExample/Info.plist | 2 + example/jest.config.js | 2 +- example/package.json | 34 +- package.json | 21 +- yarn.lock | 1755 +++++++------ 18 files changed, 1958 insertions(+), 2403 deletions(-) create mode 100644 example/Gemfile.lock delete mode 100644 example/android/app/src/debug/AndroidManifest.xml diff --git a/example/Gemfile b/example/Gemfile index 6a4c5f1..5151523 100644 --- a/example/Gemfile +++ b/example/Gemfile @@ -14,3 +14,4 @@ gem 'bigdecimal' gem 'logger' gem 'benchmark' gem 'mutex_m' +gem 'nkf' diff --git a/example/Gemfile.lock b/example/Gemfile.lock new file mode 100644 index 0000000..bc2bd1b --- /dev/null +++ b/example/Gemfile.lock @@ -0,0 +1,124 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.9) + activesupport (7.1.6) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + mutex_m + securerandom (>= 0.3) + tzinfo (~> 2.0) + addressable (2.9.0) + public_suffix (>= 2.0.2, < 8.0) + algoliasearch (1.27.5) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) + atomos (0.1.3) + base64 (0.3.0) + benchmark (0.5.0) + bigdecimal (4.1.2) + claide (1.1.0) + cocoapods (1.15.2) + addressable (~> 2.8) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.15.2) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 2.1, < 3.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.6.0, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.3.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.8.0) + nap (~> 1.0) + ruby-macho (>= 2.3.0, < 3.0) + xcodeproj (>= 1.23.0, < 2.0) + cocoapods-core (1.15.2) + activesupport (>= 5.0, < 8) + addressable (~> 2.8) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + netrc (~> 0.11) + public_suffix (~> 4.0) + typhoeus (~> 1.0) + cocoapods-deintegrate (1.0.5) + cocoapods-downloader (2.1) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.1) + cocoapods-trunk (1.6.0) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.2.0) + colored2 (3.1.2) + concurrent-ruby (1.3.3) + connection_pool (2.5.5) + drb (2.2.3) + escape (0.0.4) + ethon (0.18.0) + ffi (>= 1.15.0) + logger + ffi (1.17.4) + fourflusher (2.3.1) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + httpclient (2.9.0) + mutex_m + i18n (1.14.8) + concurrent-ruby (~> 1.0) + json (2.20.0) + logger (1.7.0) + minitest (5.26.1) + molinillo (0.8.0) + mutex_m (0.3.0) + nanaimo (0.3.0) + nap (1.1.0) + netrc (0.11.0) + nkf (0.3.0) + public_suffix (4.0.7) + rexml (3.4.4) + ruby-macho (2.5.1) + securerandom (0.3.2) + typhoeus (1.6.0) + ethon (>= 0.18.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + xcodeproj (1.25.1) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (>= 3.3.6, < 4.0) + +PLATFORMS + ruby + +DEPENDENCIES + activesupport (>= 6.1.7.5, != 7.1.0) + benchmark + bigdecimal + cocoapods (>= 1.13, != 1.15.1, != 1.15.0) + concurrent-ruby (< 1.3.4) + logger + mutex_m + nkf + xcodeproj (< 1.26.0) + +RUBY VERSION + ruby 2.7.4p191 + +BUNDLED WITH + 2.1.4 diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index e5897f4..59d0d41 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -19,9 +19,9 @@ react { /* Variants */ // The list of variants to that are debuggable. For those we're going to - // skip the bundling of the JS bundle and the assets. By default is just 'debug'. + // skip the bundling of the JS bundle and the assets. Default is "debug", "debugOptimized". // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. - // debuggableVariants = ["liteDebug", "prodDebug"] + // debuggableVariants = ["liteDebug", "liteDebugOptimized", "prodDebug", "prodDebugOptimized"] /* Bundling */ // A list containing the node command and its flags. Default is just 'node'. diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 11c23b4..0000000 --- a/example/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 8c2000f..2901510 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -5,7 +5,7 @@ - + diff --git a/example/android/app/src/main/java/visioncameramlkit/example/MainApplication.kt b/example/android/app/src/main/java/visioncameramlkit/example/MainApplication.kt index 25e969a..ac5880e 100644 --- a/example/android/app/src/main/java/visioncameramlkit/example/MainApplication.kt +++ b/example/android/app/src/main/java/visioncameramlkit/example/MainApplication.kt @@ -5,31 +5,20 @@ import com.facebook.react.PackageList import com.facebook.react.ReactApplication import com.facebook.react.ReactHost import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative -import com.facebook.react.ReactNativeHost -import com.facebook.react.ReactPackage import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost -import com.facebook.react.defaults.DefaultReactNativeHost class MainApplication : Application(), ReactApplication { - override val reactNativeHost: ReactNativeHost = - object : DefaultReactNativeHost(this) { - override fun getPackages(): List = - PackageList(this).packages.apply { - // Packages that cannot be autolinked yet can be added manually here, for example: - // add(MyReactNativePackage()) - } - - override fun getJSMainModuleName(): String = "index" - - override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG - - override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED - } - - override val reactHost: ReactHost - get() = getDefaultReactHost(applicationContext, reactNativeHost) + override val reactHost: ReactHost by lazy { + getDefaultReactHost( + context = applicationContext, + packageList = + PackageList(this).packages.apply { + // Packages that cannot be autolinked yet can be added manually here, for example: + // add(MyReactNativePackage()) + }, + ) + } override fun onCreate() { super.onCreate() diff --git a/example/android/gradle/wrapper/gradle-wrapper.jar b/example/android/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb587c669f562ae36f953de2481846..61285a659d17295f1de7c53e24fdf13ad755c379 100644 GIT binary patch delta 37988 zcmX6@V|bli*Gyxa*tTsajcwbu)95rhv2ELp-Pl&+oY+>=r1|>1-;aI&zOTJz_N+B) z9#P&Gv}#H23%q7=tU;GV-|;-tohf=I~hj-MKNe%d3eh&|2-wPA>Ee zMNrvEdyVSI`lvDy^z`gwVkn}LK|GSq#|4JnxoIRO@sx!|eY?;bc>3*K_2AXEE_$R{ zzCVzv3UKiDb5r_h5D*ZZ5Gi+pL@8*f(!iG1x>gdb9^Yv>r}mh(L3OFb;);CzTapwz zf*i|?OK0?9kw_P?-0dFJtLi=zod6Wn?%aD|P%jYTC%iX)k0Vd>Vbm^Cr+C9_<`m40 zM^@Lgu3F}@42za*z}FZG8H@~yghPxY23DilF(fmO%LhdnWy_>0YdUU%{5;eNLSXXl zUnx6g^xx`|70?R~2k4=9*}u3!x!&jn08l8EddKmclPN&pp#^~95+?=Q%XO*?SHv{B znC+V^K--hOSb9;Y5YgB1Ej3fir@e4s?^di<$}xQHuJ$q9?N!Bv!`7I6)5sbU#Lw1u^S7sb(g%zNh3L5MJQsdwRb zgNo>59Bh1jM9C5avMI{R90Kw05r4looJRr#4qh*PUNQS31%o#@dU69Nb{wvnpC=mn zcY`1@hbV^re0-c7@lN8bd570AB1N~=VPVbKaVf?8DYvLWmcdQ+38(I$J+%Pl`B!V> zZq%>Y`%Vt>v8{!@1PO9E2 z9VBjQeL_arEHz!fGJkWBR?n$iC=29KvaHy#Um6^Nh-;kO0n9to04%I0VDXCW&Btps3x;+T81snc7-CrYOO1 zBwrOxlYveR3vF?GBNGFK(sl#BMS^d}9ZAd|AuI~qjv&_lj;$rIO{nGxgiPL|9DKXC z#tSNLxrea`Z|G>h1HZmhBsPlM5D1TDN+yJ5N^b0-)-S?aZIOvmD<@f5T70wrgb#~L za_t)z{ST{ujY^K=AR!<&q5jdEF{PgoCnXw_80e&eDTWr54k^=6hJ}7>BqX-6Sfa*O zwVX*76(t8X%1|D_zS(`{=Gwk?X;d>jj(W%YDu$UVi3$8JI>{GEPR3=|qu_0AlW$|~ z?fv{xK-v#cOEGqPc6)1e7piu!+LzeYWUcE(>7pCF>ncpr8O-(Z6US0#5K{>2aOl)rz?K{}OE1UoTv+&+5Y0HkKiPPx_ zSUMqKtF!#T&Cp4YDQDIn9b;#M?Zx0qqt5TlH)Vr5!Xg@RQo&-HV|Ik@n=3Oamu2lh z49_-ckP&y{-UyE%0O8+5(Hp5n`BHJk0y(H^HQ+mE^&zM2ap4%A~gIc#u*(J)5 zFWKk7)#JdESw8EXxkA^MIWExbt1a0@>sH2q;J4sfvuv@)8Yj~R*bKk6ac2b^px5*s z;S0EVw7#6!03@ahulYnKSqr8O6fq3?}t12hy0(&)WAI^$5R$gvv z@QTmbJl?Dt^=s$=+Cf{OGen!c|6sH&L~`a>NPKsP3{U~V7nSYIyfY5F10YH9u)4{jG`)}f1N+J)`LBYoYP2OG7uKJx2PLrk zT)QlSITdY?d3m;=xZj!6HJ(ntr@OTsF?H7IVL#|XSiL@jJFN!e0Ny9P&*E-|UWWDt z{>&}87BdLGssBIReYp{#Gx&$QHt7G!3L!aliV6-bP-9aYO&He@IvTzqQ6`5*8V1l7 zj;RBnl;-^y~v2}NI+ zyRv85wprnBpSkOu717VWKvYx2T@E4O^Q9TsrgiWM*-U3ePs>E7x%yfcdFZeY{44uN z69!xlWP^EuW?t>AIP)rU@l~4AuvzOoi>lqIw8L?+mEJ4S&zn>+p2A#XzT9ZwRZ4+q zm~E|jq`S;ELjn_c$IUB&{aSFr;Zg6BVl~l9PZ`Q=u$_loMn+rQiUVv{9j%5lM_L+( zo=fA*eCZ=s=NRAK;=A)*BTh!T4lp*K-qo9di~1RfrX3^>-HY&#vrca9;i&=FNC?CL z;-KwYPy<_~f}3;naG%*P5HJP2qbu}5Mj2M)?>lm*1(SDrK4_zgHO@yZF=z*J9xI18 z5{=F=b8U}5tr*<3XBnh6?shxr2>l?LFYL&wztoJ~>N zHvJ8pWBAHvq`zfaPQcFUiDGRr0L@xX3=JqX(^Tpz1GR0mdn@^%aIw`r^AmT2itv2^LM4_W^UDgrqS>F zMf918L|84oF#mQq*L! z&>!lVl~2Q=vbfpt<_`DU-D;n>J`H!XRsHknkuab)&H#9ADq_iOw+^_w?g5l^%e+u+ zh?oj&sTU`Oqw>XsVaem8eA%jSNPV2Yh-qm7b5)(vhLTg1(QU{TD=(cBxan3lB42%e z`HyiWi<_#NP!JF)aQ|c66uu(x!h~oAKL~{hz?336j+|^ulS9bVZ3@W%sRR>CpqI{t zb~>s_?2R%Nww^UJ%>-R1cZ1u?>p!xwp}^Hz?$k1sJl;@O@K&_D2`v2hCHixfA#iS* z#nDtlynj0PA^+vRXU*g9{hdD$dOnI8q{C_=vh);SE3TyMp@HNTk(>f7lBKgNavnvN*|4K_6V&(sTiVPiK40Sb>EmM&r^LVwdWJUm@F-}i0yg8QMUWddZmW)J&?gyYq#0bS3AO_c+c%_lCemS1~~WLa!>+C>l}I5AS5ra>86N z`IK^Ng`*ayum9rwCR{DhQ=hvP&rYCj1En1mIeWF1KNtOH;ACV?m!bG~@GEH0>aZH$ za*8sc1Cmj~|B~hD6;c?m(gz8e|3rJTitJ|TlxL#c&zhHqmMfh^H^txJ0cr2&e&s14 z(7R^4j5SiVS$?jqA-oD~tD7D19K#0mc2#xL;??uGEMCPKZT|u`&vY(vjH20!tZ>kj zd=U&uY)mop$M2`Qw61h~#?J|{9VWqlDeS}1`bAp;+iLDr5KDGGET2Sf5u+P!={UmE zBp{h)mlf#EkaJua@h)3a(;SPFfI3+3V< zDekSd)8r^4K~*u_lWVUy%8iX!U>4svfQb|u$1_|${+P)DM8vA>NhXSa$bnV`6>v0M z@70rW&6j<0WJI_Spa8vr<%3K3KFahU)hsPyY6}C-u2CSj)#8sd@fRuN#k$wH8Y1CK zBBz=GxoCu@Qmx6CA*=fj&8+XWC&_nw`DjT(tu&yK>bpsIw#cAiJOSA2?=lNa*L3Aa z4D|vt*eiy2QHi7Ucg2`mn{f{cEF+O(i+L%7KCA_)6)ND^@g?}7P{Ly?Ss$XoV_b4O zM!;AzR}%2Xk7aA8dT{KHZ1mPHBzylFv$}ahv8m$;XepbuvP~~_puD&$Y~L;tfaJibfB*SWC z*(ZJ7);`c5sj-&vSbyvMZdWu+_K=f3fuYF0G!8^S%6eL|-hNLr<#c?YynLL)R&!$Q zWTbP_kgx}R?Hpen8~`AfdW~VQsI91_G0G>z=gWwMTV`Y+g@DXZDHkJ`@lQ+JI<2p(#48RjPr zbnYy^9C6!&o=z}=(MF; zE5nC_*BY;~gOU3+u^wjCWz`Sg7s9ero~sds8{StZw1j;hEdQK}I()6MM(R(*VR8Ede!NZxC)Y&G=G$whxxiXV?JTemD6@ysi~^qCQG`UFaY%z zB!ab{_Otdr&XWF}sDDQe?4}Ogl-I{==aXAna|)!&IH4_sCqpLc@O< ziJGO-OiuVR=tV$_*fAP8dJ62oT4UMR${9NqI;4)J9*mLQf=9Atq>+`qd%U6O4}*z9 zX~XzNdWGVvCKAC3L)hy{uL<}C9I*UIF4=w zLLt?jz)0+mW#&Gn1D~iSbulJxaCLw+)x5;Cg0H{=vFK24)HLFgkF*(w=j$-Hpv$SH$qlmCws_|Dgk$AEG_31BM?)Q4dq|=EHV-k@ zAFppMXXxdu{bl{9Rtq-pE6@9_lBUVfUyjZk*5g?pFm2PS!7I#=P&P%2UkW(Tb5zbs zb9uD&2>42{J9+SiXv1W9=ty1)cYwa%D`sm8o;g$Wm5t4-Fd`~CNB+8*Y^dwg<85M}zjlEOeif~q zos6Yr%nrx#+DnSN7XmB^L?#6JE(rHA z;aOD@PYAQoTjZK;sQ@+c(pTdTNuY&F8^z!K<2{T20DPWO;5FOuq@OC%n6bZAexswS z9;e6{!lS{X5&ql83{N0QXqRslc!j)sfSjSw-Go2VYE=8eWycEqo$>yR_M7~RigxYDK{J+N>F--=v2I{}q^*ea`6v2^t08s< zd(IfOp_&^B?mmSB0LG;LkNfRr0!Zu0*DRUKj=(=ZF)8{YGr?k~(3)x;H8Y`XByk~S zv|+K%EHiTZLb!oBB%|jFQBmyyyQ@3an+vIhuq2QKM!&{0TN;+q5(%k+bJw-{4LhX( zT~y;SU45QO%v>{3ojLE!;@F~Cr^G9lNui|*kKPxA#c3IXWWu&SwqjqG;837G!#`co z=w6)_W40pl_@y)?Tr^QvFg$UwCUjbtLNsLB<-aCx`H1XL@!wdL@&9AhCZz&U<3brz z4C^llQb7TIQt7Lvp_su&nPHh>m}UqFS^-Kj1UT;Lql@F+Zs{F^Mv1!5`6_{&A&E)) zGlC=ENvxB4Tglp|?;-ET@Ob)0R5a)d-k8wP$-%+Ov`p*ICt)zbc}ulR4ZRktz@PM) zz?xHgw+kxJgqaT~4rd0!QOfJrkX@(>;=VICf3{j}km zONL_(giC}2Weaw_U8lJ06gPq}+G2@rmRNdX-Q3`nx*ba<@c zV%x8LY_04qeD9THm8xet1f%olCOb!PLQWoQiVeR9I;W|2IO*=bu@)gy6S@|>JF8=P zVvc}CW%UN{$vXZw%q3xXA zT2Ucb;d&DdtaXQN?(6L_2EQ-g-uCU|i~h*_sb}sdp5F(O8_5IR;Lq5&045Fr)KD-b z0OaypT*qs*G^e}aG^QysgjfrT5cVF^`Dz})ZY4EFxMTWpHo#W)L^mx<^cj5lS6a>P zuR3`}{A@?^%3|YQ#*HxBy#m=^3t^i~;e*q4*($=qevbD?t!A`bNHLRJl5A}$<`?sO z#;-2ZZ}gM(w_BOlk!$nL1A}qjtTIbl^ZNz}hbSqu#=l`8kAH;b(AtU) ze7y%u#9?v)$HqtTX?TOo?L86|PclE=RH`Cc&QC=Z`;Oa8C!~Acmp{11pDya~%qY`XEq@3rN91-8K?!hC%*VR{nvRzZFtf&dgxU1D zlBl(;ZEU-!)jRhuqk7Osu~A+F%h0r#u!?aQXuM-L=1qp%+YL6IL@C=IoOGm!)R(+K*l_8GAFvONs^N&5_*jIZ*%?}Noj$@^hb33^e}BjbST`=&KJPdl zXh9KYlWS>a(^fKt9!R^aD(!Z`c0Hdky>19Y zF6ry-H1XmU$}X~^jS|n(L|;k4eFB= zcA;nZQS%f7XGAG67&%23U?md>>O`npF|NrR6GvHd3oW|8`A7-A$`ohlcq({2glYHC z9VX@ohWj!DqbEx587lz9-PLRgJJQ{+kJg(Wu?-Ji=(Y!(F=xYr0%(hi{6|AGdI*8e z=%i4?H?OHU|{{Sa1EGYfY5W$oLhQG$Rkijy6WS+NNK(dXthd zBEe{cTPLn|S4amhH4+Wyvc7%BldUB0ZGg4_cgHM*KoS5!DxbUj1_Oi3^Ke)2PLtKs z+usBElZJ`iH^7&#VV7S7)mb%swhgl-w;J=bgOW-mT-&);qO?aWN=OW2Q^+lp2bNck zS2_0zCj$Yfou_;_+H-(71wS;iF{&MBuk_&ntYM_4PUi7hqnE@+2)7N3rrVTAQDvQ6 zTeElY;vLTS5QU8uE2`?I`AJEhB&L-!9s@w7_6x?^368g@AB0Vs?U0+V4al~h)R-Qk z3ti;CaZ_=}{#Nmq8`h4*9pK(A9_5)JR&Lmt8^#XAWBp2k2#}r{OPhkUtTT!JU6&BX zab`EB95Nu@c{k)x{(LK##t17__jlO{xr*>d^WBY?NN_((>yW8<4Q5@R{uQd=;^$uf zc(QiCJc^cV3SXg(1)CEl?e;GjkAc7_u0^J}$sm4HZ}%1!RW6D2q?vk=q2ZJj1?_yI z@hMAg8Cds)NdOKU;66$KW@#SCMxiH&fG*SLhAO(={8u#`-WC9K$?xE_zwQJO&ncU zZxKrdi3)nyT=RQaTi-P7iUvXI{$v_D2@S0`+*Ma z+Vud1INZ~aKFqyLVj-|dyjtqc-L6lQ$FZpac>cv=ycW*ODr&5r7VjEvlAZY9ZXq-M zB%3k##=}l0@{C`nNOh^w3fn0BU>x7U30UA@&fC7A)5+ctV{jEU z@xs+#R78r)DN9*H9@=MI%2p~i6#k2FVLo){7oo+ekK|)m#METfA93mBAeMe9@K=no zXzh_dw*2{Hjx4|(F>6RU9sB&qFp!+0W##^gHSpD>RmMi~Xe+#2AdVe5Prq0jOO&3N zBbF$mUZ&oLg>ghwbBj%Xku5H#w;|H=N0Jx~^_C)6Ig!98jgGZ}ZK|f+0b?y}yePA%_4BOHau;X2?92kYeFPzTm zm(C{oPyI{zq!X}tcnR?WSIMN1w}Skk54@lb*uM`r%FC(>F7r1bbBjgkqG164hr5q> zdasmtHeH|&rVL)tC^YY|E_ERnj#Z95LU1C3F8X^kIwK4QRb`y*S)(8oW6pL*H@HNb z)z5F+qbGH0$9Gd3sV#rQ_@!L5xWAKL0rPa<=DTn)62JX6*0X9IFe&qgj&K z?}?PCzcP3D_0tFvaj3&->%KmQ?2KAUC-K${ueIpP%yiHx+bifEJ&QN^29Q8Q;#il)}{(0pv_GbV**ux;%MO z&wulG^8cnX93Lb|m;%5ddd*mFMoTcEj{0Su6Z_RHi_!IE&DLdu$X=!vV{Mq|7R1qJ>Gbh){7;VHu=b53kqphD1CqF+|l0{^k-(GgfnVmxFu(@BZ zV0>e&*oIs3=I~jxsmYR~NC{F}@U|77rMB?JN?FLk&iBSmUuV~Dq0t%^s5LJSa5}q9 zCX)2d?oi`rh_&r3HRx;cgEw@9D<1$s2>?{;=A+_@SO_VHr{Mb)2}@)JZ&X&E#~=IysPiAE0z<)0sNf>1GO%|YB{$5Np5$Z5xFV8*+%SmI!{$!~mVk2i zulpRi3W_Thn}Tow;c_of(RO@>oR{cL!>*Ry8bQ*4F8R{=gXo;{%yMzQkAmSlm6Uk} zd|SaS_e;TSjh%s3Fos*+$e0;Jr3aOCxS%F6j>L_(W7~9Hh_5ath~iw9uSrmDCoiKFuNKvZo3k`{s2>Fky5yX|JPXiWy;9D?i%Bk(a1-}9wS|x_Xi*kj2};Jr)l*{9LVp^ zw9*TCGW*cl-_=jM_F|TwuS50`2Rphte_<49c@3p+ur?nZh(SQ52;pEW{WHfFgPL|` zQ%={{f@xk<(Q!AKOGOwNio}1<7MS9vCX-~P%#XpD0T`M*6HzfbL@C$ArY2yzju ze+y4&22eN95H-#{m4vW8)p{+;) zwDPiZJ9y;Uw0o`#n9l|1FP*3blpD6j?O;#Rtq*0p$Cz{6lSD*#_;C`d16W_v;9|v; zv!ob@TY7Vs}))4t(V+I)8aZomfN1K!*q$ zA_Q7IH1%zQ2(z(!_6k|glw9sz#f-iX|*rR*)| ztG=r|A7u#;HHcN{^jw^FwpCY+4dKMf5KWt-D}2Sk*tceJQ&We~7Xy-m1tEdt5wUv; zKr#bP-_K0{DzpLd+gnNWVg#)|T5$;Qcl8EC8lTF1hQLUztSiDe4E;I$`%+NKFR=n39PLSJHv&u(EPdM)f>ha@TGaM~RAbw~9$yz<`R zJv3Sv$cle;0?%<`vg_T?hyQS-ORfCn#Yy9FI^z-~^|X+BY^21v-9us5qQ|nOXOooC zZ*Bq7)KeGPW5`AeIRG4gg?0~9_*LqvMFjA0x&eQ($8YRYRHI95 zHkLBeibPQGHI1M-p5l0kRGdEf>jve5xy6AR1#dM~kV6vBu0PrGzE3toeE#xm zu=MtlH7E=8qeLv8%~0mD6tU*<4X)q%oU(#L5?Pal=E+dgUFF8bk{gK3snCPM*+D=T zc9lWsAqT}YwmfAL@V3Ns@_>Gez3N|PtUA5vxenBM;0`hW@Zjjf{S7NTRH*WtSjS|h z1ROZowrWoWno2Q6x4eut$S5R``*l~9pG6?02qS%|MyBH^-#>g4Fd>V)^g1FdY_*Sn zm4KdR7U=;D8(s&Ub(q2U8Rd(%I z?0v1zIJP}U=?1>Q8k<`@LFk;y5}78(A4ZXqmGQ0_7NrX2p2wEHpPecM{4k-Pj!?PN zW1dHZIWP3!l3bG{<7_yS)&WY}*!Dx`d&jWV)Vod=ASHdGdj|=?ZbNB<3h;UmET!mK zDxvE8|F@nFiMd4!`}aJlrxbA$r|1X>0;lAMzG4YmI4rHcRj$;Ypu?c@B-Cw^$UE8X63ZAf0mt&Q zy~4n552vLQdP{GDqSLL`1M#IkU7~Y)?y!l4!Vrr&W#>%A)Rv5o~2ZUj=LX*GR zQFtk9n>Gu+JSf_I4&wd(rNtw7WbiXfUelXJ_1AiDO;L0i1JN=x!RH}yt!7iSKC+c2 zgRZ1e;VMOD)*xNk? zkT)HcC8r?ulef=RMd$JtLw-t#aI``~m)nW@9Jpb=5p@#%h;86qg`Dpj320#`AKgGq%ChgU;Ny-eAy zAz&1Y5s_mDlvCzI+`#W*eQ?X?=+u(WBkE=HR2fhgpE+bfu>r;B5zVqUJ^;uwaKoYT z_puRMn6m@-Gajg>pak==g<*D6>EYSwK>j`IPc-F5H=}d z)uw2%6{o2K&=H5DE*_WBv9hvZ6@mgT9Mvd6Z5kf%Vw|&VpWiR^!fnhW0=Z$ju?ySm z{(RtJ(Sh}QD2r>g{3sMBwm-H+7Zf&qs%DPkS9E=sRLY_DWUKPx>m|XY6|y) zXO|R;c%*qmEV_lR-+UwB=Js=*A6J|xiVRFMvD|FcMSaOsDOGyxAlJxhUeU5W(zv*` zCNu<8Bz7Yb$>KI-IB*8tHpe4AFH$NDri3eQa!){xc%WN%7Obt-y6bJ77c45YxpG27k`w;2g1afr1&yaeL6VJ zO_;=HA|<`iD-96p&MF^id(fLTR~;oSKDh;oo2fYN&PdlW-ssIf zQ^GyOG(K6@pxsyo*j!Ol{kjf_65Az7YT2N!w^$!wc<%i*h!3@ewm;B_H^PqR#UJGMguO%A$!i8a6S6s67|z}yS3l})Sb;CiiPgTL-uUd%h>%In z&2C^0$}Q0Ve;ncHw+Bqgyz2HHVVeI-Aj0?$YTeOu`($eiGNaie2RbI|%-yPWmSx!3 zHKVRDlnmZcw}fYWEfOUHJ-L5b)FI3h^GrVUz}OvChn9Wwsg1|hcs&r&-Vp=R;TRi@ zz0tqpAh}dpEMWyjhx7g+O|WjH_9xwZi0G2JHrJ~aT+xuZ*%PikJyYHusndOlWIdf3 zt4OLYVJ*lT$bUDzk(i&%9|cG7O(vFIFamrUMU+h%INu`!QQeY@;~l++7#CsCc3+VzA)c6_`+jHlaud(lFpWkhi zrBR54UrUy=5k^A;E3f@-R%+y*=1hEEAW&{K_oilM{@hiVQmo|u;NwGF=K6)KnUAwE zu70AIdCbo4Jdx-*e5lx^IwLz{l-9Lp3uK5Z*)EhF)Wj*O7v6t0eZ2e~+3Nk?9;A;y z^gU<7wLoJMBM)6Kk2;oRT;O`-)z_tk7sWy!x6?X3`2s+*)M=~m{sYup_ zdR;Nwc2d*Qo!h47bR~@=Z7HUFuT1@LD{IpQL>U}k zy(F@_@Gl(#3rYA1+HqbvgtrL?u0K1*5(WG2i$y-C|!Qv;*4o4lPnl447`49&@0`ghufkx$E@* z-|{(o9hb@fXEoCN&ua7$Qv|9T{mV9E2~&oT{2)UdxoLPRv(3>Yr? z&Ii3~N#HNkN$ucpqlV*fJ5ddN{WO{&(YQ!AiEK;dfL|${q|bCh<3PBNt<6*UJdK$t z^L|!N6Kwus+upyga(SKQZq+v^E!Jc=a-ZlCslyE991KzT#K{w#I0td9Z~8+Cwx@xa z;b?U2i@^wIWs6kvvPwk5($d)>!CC^UQPe52#66rGQ{z3Vo!r%&bOgJcew<2toEk)_ z&^Rwgs<8SrZns^{D!?KyHp)f}Wm%h2NYokwi(vWCVn2_I_8zO4zML9NI xt3G?g z4&W%tBqXPbR`Dfgu>2G0TRV&4bOw6_Oz;EyI?i0{oODJ_4RFbbY2RF|&r|=*#sMY0 z^C#;!#u8O8tq#gKuO^NKL!6Empu_>FK3#2qJJ>Gg2Xel_lYOijF0X4d*)|59)BO|T zbURajp;K0G60wrdr5zug$aA=!F{BTlUpz*+?~5|qnzM^2-)J~`dY|bTblUUB2E0zZ zTVU9xOlwKgGs`C~SzmLe70MsFd5R%^#gG5Fe3Ew+Y@7c|KK8HV)<8aT1VdaD@53hs zK3VM$cX*5nGAbswuZ9B6S5JK1%{5s~{ABm!0TL{o--m-y_e#*5;Gks<%U4#kR=7EL zfdL<|duXWGB1Xo2BtemLawd&4`z5*w2gbEEKX_nZG}cRNIdHbz1`hQ=nDsa0xI&=n84=S@gGA2DHTxQF0jjouQnFsD*`p#;*Xg)EAP?cklFQb>(sA} zqNk`Su_t$9u&Lr6nlrh_xaFqd&PVrLKB?HbeLk9Nmy1i)h$6BN$+5&R?!ns!q3|_` zIU3m-`iV13Iwu;dUqV*uj0CvF&z5e_ryG5NqxHN$QUepAY&@Sf0-)FUqLKGPE?Tuybs0 zflp2+u!wi|uhII!bB}KtElo6FEaxecon!{PqX04^gX-k2woe$JhG1$>RKotzO>vVX zf~U5Pf+n937U@14ClFNVgE3AL>-48h;3<)wwUITF_%(1WH zt`@dMMY1U6RpwZhRTz1f@oPD?J~KglvhXauZl_U5!YyO@N!e(v>YEx#ue9$_$}Klc zs*8wd-HSapLJ!kDq;u0txcz@oOi2^~qFdeV`n?vl9v&LL=}oqoP8OqVAI@`b-wuJV z#+?@iA-7*ULLx$N1cjJ#h|QcqZoFJLn_I{uu?x*pMmvmx_kgMF0znV&_ztnBr@!8p zUC?2~#v)1@;PrS~$vt15qCoU8teD&L%Pq%N$EZFxAGBC8hc`FVXvTO(Jo_M1oy+eA z^_7k=J!_a^M?dmq@x)*>Kzt@6Y;3xou%6JGW&y8B> z%%tcqpf79fPUvikJUbYLNldGFu*~+sGnDd&gPW10*$Bj5mAH|82V>xVA~7jHbf_8) zkrU#%C=m-jZC@4f5pIxXQAfE2o*puTv=@LPB{+ngnBZA~wL)Sn@ezgnuokGZkd2Xp+j2&bAixl>UdhtWT=nGFR8N_Zz(q8aI!vtcZ;N^| z_PMlgO?9N@79w`#!HdJ_Bwwt`%JX-w>Wr?i5{#LOhtl6{jm^1C9Oaw(Ts4^QQJGmt z?>sWdDdptyT>&Adp*tjGx)@ko6sv(%+W1Jaia(KUfv12MZ&K9|@NE-I_&1qmt`**Am7|)DZk7<}0lEb(bp|OCIpv*7ETM(?Lz-@t>PU0~)D1BEIs_YYB&Z7Oh6Mb&2M1vC4_z5qw(S9VpM z(nw04WrEHJBmi=6!AM5Kz2u>-^?$<^=|x*Z=SbD^Quu;#a7yW-gd-TEj7IIvbGYQY zQ8Vby5K2EmJ!6U^gGiQ|bZ3v_9_J&t&?p*X@#E{>6R_(qR}F=yq_Ior>zBGeUfW8H~Sw?Z6`>8E+J9iP_v1=!S{< z7cL7;5UE+f;AR)G+fPuRb7yyAU!d8}<3ABR2{@VN{c~_q!3x1*kqkaMK6X6r?3z1<@E1h5xo+)q%j zQeUw}SQ(M@cv>Ykax~-it9ui1yu#%7$Qpo;opj*sy1)IXM>x}9{$ZmYTBW#%;qVqi zhZibvl2%4P>LkNfDwL&iLfuZ3WSr5XiNO嵍U=YZ;=-zZAgMtk6W|B6MQ%mLGOi!VUaes}cKdr_muGq32|u=mv-jYNgoNsOp@ zB;K72fu=13R*PL^aT$!#sVQGJ?f~HB!%Ib9tE@^1WK1dYJ7R5DOp{+4-+E-}$8FT@ zyL1$dsV3Yi*RoD%Pd%6nB|H~}^YKF^EW{ZID$iO!8{-Fgd>S0m`RThtkJ*neX0c2s zx0$b4PKdu^^5G{7j1+45W96FJBE!-2{^S&n0M8pY4s8ecvB0N^Bls;;h>z)k{)uRawEo87!8`(;s^s+}iCoVW8wG*9`O#GKq~&;Izsux{YH0QP!kVr1Q!N&Nz2#>9mxer6DN6DN zGDzbyF)74=YyIaGP6tt1vI`p*!QDtLNG-MsPdc6Ecg7u)wU1|@&WKa21K;=A+r8_5 z&npWTw=nKJZ|Gaz$y7WU5VP}(G$XJJs>QyrHCo$Gq|^xaitx0K#@)d7=JWe6gRp)m zvty%aMlK3*m#oUd3*z|xQF#iTfiuLjJ7Mzzv%|NHHnaw^^{jFkm<$n$rSA=Rvr2(O zs>LK+>Tubw@yD|{m<4WvGSt@qwCkOmVH}d4b!!o^ITEh-K@1ASIGXnZ+ztGnSm!K~ zl5KyY-(mlgA4rd?Ww?I~5&M6Jq$#ojbrF0GwXpxXaHhNn5X*Cxg%@EJ6a{-GX9crl z(l|PGg)@aIFLJc#npiv2G~|7A@x4*PNsLBfh(@A-ucULvhH*+$rc087sqQEYi7qbQ z?_;fOCA-`DW4$QHu^P0&y1V5w+k3L*G533}^W^z1;~I$elK~dA`Et3w=`T+2n5FDrp(1XtkIn(K3nBNyxff9Qa@)? z%r8vJ9TxBDw=#)cqqC^rUB)D;J8m5X2AnN;L>^j?vhlYgb4qNPISaPn-WixPS}-qg zk=FN1!&tym)rREl6UH&w=$fwsvwTNam-I+To1OylH}9TSBePl`TOdh?LX6%TN#@S2 zX@VwxvgEsCFLNwg*C~QFsdlxb#4>UnDZxEXR*u(vMxX%?-yq`oi>n}brS&$L|; zLnoEiNEBQ-8ta?fIn7&P+!)fK(MIh6w2C?Et^yMTEbU%v6d5e}G@EMsDUisl1rZrV z`Q%*w#seI%;fZTQGDdd5rz5f4sOICMFL3}7jUOru7xiBu?BWaMY8|X~`DR|Gcp@?B zBa=iqwyq8>#B!NeN8M1?FZB_0_B_gs6BPV(%(Wm8wU_iUcB^fWgd%l(5%tT17ckZ4 z<;zc`A`J*^2nBL9p14lH0qBg;+tH&Id!2{`0_iT;&nzh7BU_!oTMHvJv0SpgRnC?V zQdOPCB=Dm})O%9k#{Jk)V{#R~GLVO)_heQ}3C3TG| zS_XXN+kyg{>!a9?3!%HrH+lI&-_P{;AN`Y55{=2)^J8oo6lsO$X(g&o*moCWJnC{= zGwBDgNsqo6{yoOPspolVo|~W8=ErGe_}j#Y`&v8p+Y&#^V4N|-)JZsJ*b*`corF_L zLZR7D?+{dekFL84kSLFR(j5QZLCd)*rqDf08O}UAFCmoH5Mte`C7W_RSA#J?#0n;A z$mFGu;yKrhTgrB@`?l@fA?>Gmh)*#lqP?5w6n6@xXUs7UMEMDc-SS(aJ}2QwYW@$g za}Awh4fzktI8`i;;>TP)YPah03B3C32sJ|`Y@Jgd2Hp-3xi?eG9CTr&XhTugGGB?e z|B0^?#M-`>=6X4ipu{7xXB__)^AVatw})zt>oi0$ttymFMqiz(H3?l2Qy8&<27x z+M!q*J`4KJHZ60GC7t>k;*^qayQ&-Kk-x>CCfq(5rXH>L+GlmSUprYNbZpx;I(G8+^M3zPjj_hsXZxUP zuA0|W{mWH3HIR@vKKIu^kvwhQ9r2(5h@gKk+0gVDf<>%{;?D=He$MTQtL?y?QGa_`0J}F>T%-%%h~vZ*z=b|*<5}j2jv=B~`7-~&DU@q-mHO3Msxi|a z+@LHR>!iLVD|3rYZLaH!Girz9?=(0x-2lqN9bc9#nmQXL{3-4SlfJJ&I zU+IB_YDoFFUBTy3td2G9DAo*jt&QT|=jf{x(92#KoJMj~vt$ts_NgUlD35D&G&|mO zv+gwY(SvKpBAa+OledW_ro(k<^e6Uu=0Z~iNRNLz6{iggne3B9s9(But{CK(Sw%~D z3IPL+zurXdn&S=CL)#Lqru@_rz(sfX7P?qeR_=Q~Yw3C^K2jj6RyfyOblBh=+OajV zZ(eSGZwME!rs0oO5@3>+U+ptUk;QVIYda?(lLo6UFtdXHIggVh@CW2+{u)f51`9fE zC&+d1DdJMQTkEW5MP&@$QO8+PA}3u~L6XoLXUX)> zn6A0BdsvtlV3G7^px-P3a1@JwsJZD1bUh7OZv>K+m?L-C&gl%KCxe(@7B2H5@|hJy zFPU8IKq1|JJOKSj84^muaA~S1W_!zEduI`3i4s)GKwzQ|X*uEVxegulFqBK~R1>t%rTGqgY^?8O&aSSEP4{KvEK>r@%@ z2zib>Y$CqD{A1dVRh4 za=PPtt}W_yhvY{-(?5}}_*%mr_Cr_zt#MX;qA_s{T-n$Ppk;V_ggTv2T5jQsJbV2j z{O-b7?Bc{$Tjq0weZNHS+>P!rb|=3@Kl*iq$;2=H1z#Z+;Q z`oXy<$+1NN@W2t08b#hS)>ncR=^)1w^$6wbf<0^eYtLaWILs<#6#HmZwgcZqGA@vp z@?t@R;{h2YU$CnCX3Bj z%uHo)vApBwbC4z=a4U+!$&ni8GbO z!y8+xhcJadT#9=F#PmKIYcGB>v-xbxH*Ifj0(Q{K zoBdh1rAn_Fez#hA+3!&(fH^NOYk)ieDEZ{{W+_r6v<$snvUNtllOxmrhy@5wELelC zl{t=v|PQa*DnQM3ex>m#f-U;N3^u`Zpq!}*{b?pB%!OEyZj3j$a z9RQ@S&T_|3Z!V=ecaHpSB-SJjiuHwG5y!{=YnHRs) z&BL=xOhW(UaCKUaU)yera$PGurH!O`TmWmbqLhFMNeeSMvmx2Xp@V(dDcN^a^QC_8 ziE!Ng=78xN#|^@Bb`pujb1^~&{VH`Bh{Mm&XZJjpw+EOQozXdzN zsBkCGl|g_4={mX$jyO%~)xZT4Y08L}rh!IRrF)KmHC~l(j`&n@Th_Yj&>CJ8ExKog9a}K*KJf~KbL@1txe4Trm0tZu%TGmy|jWOI4xGKnw1 zuM}GiGLZD+8vD;sXo*ioX>>LF$l!UiGiD*zgjO;{^+NY-T(pQ#b{!2bS>HKvqhIulB|1Y7o) z2$z*~Aze{6z;a=1>3SUlG&BYLG! znOp^X#x^XDjA-N$#fn%Dy2^E+9Y+maYdTQH3Oi}Boe7rE1W;KH6U%lP zTJoW%D&n70S%J~o$QhC07HGab<5-F-t~+B2D)Kk4(wG>AIVKd+hSn-Yp4O5r$>+0I z+NNK5q?y-QlMx7+wqIZ3a?*+(yk8EQL*@J;kYntaUBz2FX})89ilS(`A~{3ZN=2x` ztC2=;vKU4J43fdR-gOm998eoeZjvEq;>vQhz+hcE&Aq&cF-gHQNKKmIq!dM@sIW;_ zXty7{Uzp#r0#gWS;{a8LVbu(}qq3RAjI5QBp0IMJwnuCBI5bE?qGDCCk3Y*R{#W9gK`(lD3KXAK;&vC@+57Qeg zna|k>Gcz^#iNg{U1i)o0Jq8;)Cf2A#stEO$Qzu6b6NN(4y{RuoKI1KN4}a5Q+Bv)L=5y$>!Xnnn1)YL!WEh>nbNldr%U6mhbXBFe-t_AGRDX=eRvZyyeRz6AILCQLkq26( zf53r@h4+-3D#cwkEk_R3$$=7h&bPU93SWMCRY?zO@_+E>+~hA7#)FSKh|7$pA^o{2Z7u?`~|Fvwzm!TOI1w8c88_Oju82$ zI&*K!e91=c*3FB^eruo=_o7~%JS0d5kpMAhoW`9!f_tMezkfm2?s}GM7mfZ+fU;1` zSY&ul+$+uOjb4bKaW8(j(NkJhX4V0u%)p+FCU{MoCNzIAHi2~)mSg-;N*!!4D5`@^ z`Qlid_yOP@zJ(_5u!nj z*BGkbzpI$miIu|^W)J3M8e1U)lrX=xXq@^4D*-bmuZYmW2I!eG;|hMjEW0J2;$^#$ z3t9wBN!YFp=hxzOQT~*j4JwZSS(eerh4`4^qQdWQf?-JDzmPe(m^Be9thZ8VngFRg zN+M9HA5j1X*>~V)0#l6~W)Brk#_74kFAQ(6;^9DVy+_1O1Iv><kaT0{P+rMNNC5l`a_P4EhWQdkFf(_adEK^GWA!cQmhrKG;bieE2>s`^Hl!#sH)1 z_HG_sOZ9ti-~cD%KjjM%q4~6UX~4U_B*sTIA@jzu!eD$_lio3k{^lY5PPF=2Au0Q0U(V8dT+st&jd#SHD^9SrCzL?Ma&QhT5{7_moH!* zggcE@z0_;Io(et>wSzhy&Fo$6*nk>*j=Pinkq&j%nU+SUlbV&^nJ>}$?I&g>KvaIX z4wwzdVCxMQ)>{y`LDdPoCkHtgMCNH$Zx0eNtXnh9|HxCJS%!~$d|1Cc8CBGvhojqD zi5k$2&?E{EFRMR5Y2PIvzXF*o81N6n7&jQ?mR?K z2k|gKAD~5vbbpa7=<$&@sHYMo^s!j+8;Ld+;vdL+_DHhGNMD%Px;M^5pKlFw?8$Qa zTfi;1P!Yy(RVodbsuT5l66-!v@J^cHc*vR$6g4Sqxf;`+J~(1pmHfo;ulhB4{YyD#!~;NtZ|FSTtcjlz zwQGb9Jf#+09n2*y7seCASAn(o32pBZDz!U)ivcsj$wqCm(#Fx+e53>BpVuvD6s_30 zQd-68FTh*>Z|!~)YE5YGh8^?bE+ zWl+|DH|IS7pX)vp+d7KcZSvTjeF@e*EDSP=YA? z#97>m0JWcoNnx@pX26ym&Q+%&yI+#NNtp^wMei+m0QXGjbz#x-fg<)KZ(1?>ik6GhGq$QJ`7vo4vk$WMe{IA!af>T zvKxwHB4x{5_f&0#NJs{bdw3e_VmD|OoL;pm^%cJ6Hv-BV!Qv?Wefb{Sj6G`(H`yqV zk{0>Nl9Pw-7nTY~DVLC97SuC6+;?OUvGHjG839nisRgtk15O(TmVn)Eg~{1aQ^}9z zs7&h9Ieh7H&k-n-#Chqb7cBkCKR*J09|{HY zXt$&JS^hA7Y6QHKWOF86po@xeXF@4lfu7%fBIe{-z!Bnp6Z%)*%UGHo<%pPU$>P?b zta{z;Bmk}g7LP-*u1$G-62e^lwF(NoOg%WJ{+qBCu};VY(A!Z~~u!%-9gIqui`!G~9tG#B+w3*rrjA zaj(PTv8Udb`;cvvoPwW?Gsdkzy*1AjAq z=tackkd9&BRk{yy*V2CMNY9i{B6kHaXpB*?Q7you!N+O`^|s3KofJ42IBV$tI)^A% z(DIuVL}^#I4GX7&4h!_j{-$pVXcW~jr&XgeprRsqrz?u-NorJg+G8%!)~tmHEFM;n z`X>{jW{|jBXvq2IDpxH*KOQ@pX;!oE(vA+Un-=4KbuPcd`dCd!8xb3xs4N|5oUK1d z$!OK+l0Vjq4+>HO;w;&iw<*9nTfqWA522zdGMKrCb1ZYjfSXrofpaEO;KevFh7~It zk)@bZdCdvLK(q~vp=1)GLzyyG!MlT+a>!HT+ zw_(FQJ*8m7jM3x_W#ZelRCZ?|$@sEv{Owq;vG1{ACfGpZR|2|KenuGuzSt@lK>(m~ zlMUy{K*k*=K24aNIWPy$y%W=Z#_ zNf{10oX3~48PEnp(Kz@>orVLXA%IrqUFzl+3YLA;qN8i1Hi5J~J{r%kx{YCn&N0xj z73TYV!tf-Rc|K>PO>a zHe>Q5e|(_rIsq96to$~(h3kUfe(^U@Z?kh<&W}6(omF}Kza$A^!14U+dh%|(!uiM1 z9KCZ8=PfeiAH3>By7P>@;Q+;;*0B)s52HPmA4bF!a=68eKpOeE|KyS1PmCvLv}NP4 ztmqS7162ew^C5VA_>Pb8+hJd~V_(`00&XU;{`KNO>Oha!u+0NT0wlY+WAh4N3r<8| zvLtU{iGE;9RooK&?);K{-BISiud66)>k`L7kDH1DU$YiMm&aASFTE?h-*a5=t5)}DU@dFyuWXF` z3=nv+Y}z3wYA)4VE_cZ2;ZwzlJw}6zp+}Rrb2`%G!NguJ{L8TzGwkHkOo4Z4hT?@6Zd_ zHnv&i<{thI;4!YRay&L`L>{Qwp=ZX6w-AnJY1;#KY!Kgu6P9ln3Mk!gc?R8#jgGCX zUj~3LK@mJKS44ozj`nFjY6d8)az#TEaYD9(sZK z!LWw{SCyIv!bI%i=X1nZ(TfKsC;V^58k{!V{o}iY3o?araW779{Q12@r&ok}k@vnc zS$y$QvG!K+ZUCoQ5N{`nv?LDk4&YJ}ZUT9aFAsLV5nFQh+o0cT|*r(4Sq$il67n>K4@tq$giRh*r|+(RLSpej?m1Yfv8 z%={ctt9eP(lX-)f~zN^Oe(Kk7ks|}6+@qLwi+aQa$MKCep zSxP-5+yaQveh%caLdRK{O5efXHxUQuxU3BdAQy}ZFz$UF;I%pR^Y`~<%=F25zIsp& z+Ti0U_FNn`Q}%HWI-PDeyT#|hj=#REz;nL3#Qsp=pBogkj#}}PZ1d;;`YxSErG!LV zQ`x;y+k9^ap{|5~#(%$h<`;j=DZAA;MZ?^J)CMS)0*T<&{WVb~Y!(8 zQx2cV6dFdgzSbyRJxbOpSb1P=IZ^ruld5Z|vArgRY+K1LdloWtKQlx^a&eycRR17l zWi+j_B2>gUPNw}xDFKZnq^$c(RpmUPeU%y($o?|o9@mI4sbL~CJkaPqqA}Q;z3|CZ zzz*o>-d2aNr=NsKz7{q)^gL~K0Y@KXCn!ZuP%)_6tf9oFhQmz?&D1OL6`A&Y|JE&Nx5_;PzxJjJdwl4D>S<)FwLIYo8 z4^l@A;1er}p`j;JKhGr2%>IE`Cw_;jSPA>=(l6Om>(nUu2-h!Uqfukj^gBlC5Ca6U z28&D4Lc{(7?uD8q-p~a(8wZ&~+Nf-du&iHmAoDBqyHo(0&IW?I*8%F4apDf1nc?b| zTPY1;lg8xLu+L63H7Gp}{L{DiNxVUuT!5G$AImf+sxjpgF>%Ky>Nkk@cbcZGzz>=_ z_>=OSao!c9lM0@+!#5)_^GmE^K?*Q<5F3<4v=E;)^hZokbxy12#4?ffhx)ZQ##UGX z#i~{;gK^B^j&ix7gmgU!ec)j>E+;o>Y%#x`WPUzUY`s8 z2lpAr?-K=jVpWeUjf_GbLzb>NJw6MWJk)~TE=^^~`|38>@?%^c!ZcQhv8UgYkJBGh z)*lf;=ukQjWcb5y0}?4tVIIvpv$RouaZS$tnB_=`p!9r#t5UI7^dCykvUFOa$u?@- zMn}5I&c-sfCgEhmeSuepV;ay#ZWVAHbJVmguCdlDm(q%9Rj@4{bwxwps!%|CZLe)y zS4iUnPv25kYWCcsYFAf=^Wk>XMp&rr6MS(+Y1Q#%(mb#uL3@ojNAnS9+43dUIq}V? zeQ2nCsVRDi=dXRMDGw|GYv*{CUxg37amFd0h1cU6bLi(p#o&Qjm7b z1-8e!TO^8TKqekIKVK(C8i8uh-+x-hYK?w8WcJuDrN;;?t&v!k@7HF0U$ zod`Dha=)68WzVRHd(@7I-c}Wu!<5$sHIncgIxMTv>T!p+TD*)r^=XO#!({$a_e59X zSw1bc<+(0?Z|~fcx1fr;yv$r>t%DuC-hOgmEkFa&vuDR|;^2YSF)GLc+}NDU=-dTw zH&WWc!>ygCKC6y2p+uBa!6j2q?U-G+YNM!E)*Tn-bX}!eIk6!RMFKk;Og&{}<(hQX}B zZfnU;+^x_SZro^`o*)BJ}0YaOCuuulw%sl3U%4{o1`&@PG6 zh>96Ui>jw#!OLT12+LpJFM_%zq-L^^;fXT3rf7_BR%5G;MDWK^m zn{hFYa-u9MwqaI{mh_9YK1hm#2o_pBcw<~SLZ}yn4_G&Ll{wl8!!S<$YATFr@rhc`7(Ot2X9-Wx69S$~hqlC?M&(c4&;HJC9r+irx*}(-Bx|FA7 z63cKxV`Cin_5vZ-kS;QJF1b-$qMN`0YR}D+R0>rW`ObACJM?&yawVE$f12c$zMBm7 z8LqnpOM2Q{gxk(417e4upP!n=A7`To@pl!K{gEt}`MM<3~S^qpoa8nM2y^l>1pUKE30_LO3) zmp>~|KKly9{Y*}}3!JdRl-0gu8JGM8YpK(-VP+}gHGL_cph7E85lU`+yr1$MI}+|- zQTUr0;2UZ*X3=C{>_TxA@_GSepCmp*E55C7SCl=8O(1Efk+@K zkATon9Sa@}r_E_pRl>T`U9=uuj_=(csJ%V|B-v6X4-bzrt0?s;AkY*oyN+?0s4Ycf=XjtFp&Lt)=e zB7oTw^3^Hu-CUX8ccbX7n&TAL959)veU=20xZ|U?8jU?xqqzbI{2qJ-W`ye);^7np zK-`f!ts8x&8=F9L45s8w=sID=p*vJ#s2tqo#cZx^xaS_~uq;KKW$yvZ5G&G_J&cMJG*Z@V>UQ!dMW4N44G>qWXg9gWp^Av>u>SqQhC7 zLFr4&dT9={X1{~%43eK{uYgMVOnT%4Y|=KD5q;C$yn%lUFK@}ZM5@F_^f}qaC(Cp# z?V7;z9izjuB!7DF5*iYIN;I6DPNr``b>14l^xEl-avP5mD175Gsvm%Vh86O)4Q>za z3}OzapDABRiTz+QLOP<0Ta6J=qI)S9S<<#bI8z+lhn>fu|nY zrTYJgsN*Y_SA_+&4mhynOR7u(Y_aYfCa$+X!ayt=t5Jr@6RfU}v08cMV)c>br(T@< zYME&t=~0FR%S|zmp?~I zvuBERxgCZg>YJs4i_{ngQSmTuz)P$UJ`F zV@NeIt}XF~KJilH48@uoETH6Y^fCsmmTNxK2C+prZIED{IXwgbE45<B!|FEDh-&nES)7<=aR+@KVZ?k*;rZ6Ryrsaw3E0ZHsQu?ffqDx3<- ziGn^Kr19{52R2%5+QG~&Et?yOd}=(JNn%`@S)*I}a-0!1wc3BpLJ&x^`6!fUJk#VeL1u+X66s9op zj7lwCfhpcuJThMFO+2bZ5vgZ94#GaqG9u#sJn`qRusqQzJOacFD&v8mSF&2YYBQV+{jut>{|JJ>0()ARO|sOW>S;KPDDBO# z+Pr0XH|0g0(x2K!9JZ#a?iP}=lO^>>SNIB4XKVgNiZYCA330r6YWr|3Vuoc6fi00d zJ{;z=aO9R`R}pLM?Ke~uJRy~U7>%)ca5XaDIk#We!K2p5!mA?1pHUeJ~~FgVZMuRUvMy~UYmMPSs4`np87Fb z>gMnL+-X?q5;*}{0J(cOC}9dYvyDFZ7vE7baC9tlZzSfi&Nr}FRI9LkO?T+c7@GtU4;gLRLw~9vS_AIKCGJlh#v$@0vYe3M+>Pv9xO>oUha1ZW3M_@U< zlJPsE{W=B55|n#+9x7aIMU<=@dV4U#`T<(u_sr_AYn^&iz*?}s|hk^-AbMokn zF-IiZ^d^2M>J7mp@%#@EcVMd~YD&cNaGE7R?Dga62Rb*D-G4YZsC7BhllTXoyrAnE z!mjNG!YZ4Z-X(=XKRa}&lUQy6#lig_0RjP>qZL=jql>TVoxo@fU~6m7 zmd;j__vWr)gJCP-jk~3cQV~2>PG;O9F>mvX{tjML%MUfBsj{rga z{|OKdF;mRU-%7?V6;B5qJB$qKBl-PHEmu+olVtY_b+y!ElUU|`b$5fT=6w-|{6)%r ze|L=v=u>WRxQT7dhD5Jl?N^U^8SN*R{?i@nHSWGPPyr^;f*F2%)W%H=5b#ZGT)W)HhPdC3OJb&odn_|6CG$rvh^+iJ0oz z)0itN^SDbdSPLV6o6*wPnucWm?(-cWXn@ck;-+|o3l1DBy*V>;|7I7~x{?L&Q&0WL z<40c$z)E9_t2S|6KDmeGXv<7`rf*~n=h-cgf8ibY-9JCnh5lyws)1l~fo0Ij)`5p;Y;nV#NHoe)Djuy8 zwfXv|piLNFNL$*C7-oI|=YGBs3A{YCPd%7QvgOkad#hqx&t|pGfYH)*2DTGn8zXIo zbxNLps-)N^%vOB5wH0x#djbJ|K_9lXc4yov+6{t;v(%gZT4Gc&Y4!~UW<>(=k zSEYbn=)zcG6=%hr6mfd$Q#8ERona{k)|xuMuYrx9C${x&xURs)OCY|ZT-zzc9;p~> z1)*0j+`FPA)w^6_?wUjDww(Icam+r5j9I-gqqg~b@R!^yw2tba$4K@;fU7Mi7 z_!=UFAEkH`OV4hZecT#*29w<)hs)5HC$AWK<85Da|FDJEzzv7(0aOdoO)zT zl#BAY16*eAgiG;$UmmPK%>E|AZmXMIu!xF0ad;sqq}xcr^+s4;1ZT@W`T`FDLKyBO z)pDF-{}$dQ`~2O=9)SI8nn@b+93gOIlD6e?_5_Aq^h{fvI4Mb_Xh7aQQB+j-52Bkxj z1D!*x&0sd|ZCJMHM)M=m7pF#w(>v8s@ll8)N~5MjI6-lqA%Ivwt5-cl${SXkd${GO zEa(x5w8B2Lh!9+AB_l7u&hEkdE4b+tMN&cHadv(*@UiLfVGAgKvwl?g(Wx^e=o2;F zn9j`UPOZi-?ZzEFAl@L~z*LW)nu@&-_H^AV(j!;=u3@^dRz~Mk^(pO)W$FWKHIoPS ztW5Y3jB=Cx9bm>z#LbAy{#Bh+1^}lf+`GVTopSxN(oq|BrlBRwA{Pn%4I1r3N}a{A zjF&+xyo3y|C{<%M-fD_^R(EnVZi#x4?yjp5=EhgGp$*XxC(2^KW`a+@&dzwDNh6*j^uI-yA1BtLyDXWLAv8hi#gEd$06b;i!T&fTdz0`yJ4(<+bD>Zu;PpxCl;#t*loL!-d}OKy#+Bhw3qkay5G#&!u^&SF&{x6YwU zR;_QwMQ7+ox7e=nsz0uN#-(7$H?SivKw>@Q&rd|vhQNZy>uFIptQaj-!5B3Oej($6 zm-yKWZPi(Bq~JSdH`x0W$Ut%yT89e!T%WVJ1eh1sJLuzWAXwnNfKjj)p&LxkmCf6QIVxrSl+%3Duk94Q~gORhu~<0LTxs zPrBgbp0P@;O)m{bE4!@abm%_CJuhU#7tpBQSiJY{-{s&Vts2D#zcs_n&p>wyBQ@b% zS?FF)=P~r=xn;7fnaM6h{S@ht;hSJ?x@&yI{-DSF>d>UHypGS6ur2r6W;kk+yx_Rq zY{N`W5wRBgW>evwqE=5P^tm~Y1oVS0TV#De9->n?VI2PRP`C3^sYlXQwhf~PXdIHD zG1W$BopyvR@!0%K;gGRKoaAegu8>gf4KG(|i8q%0Uigi6jaHID(P^9U7EJ8=PtC*M zgmakn24dHeEOOX%IDgAkl7x$ZG7LPXG8|}P;=)I2a2D?rEhph7o>;gR0=SZ%Z9jLC z<2e+0`J8Hzi`pXL1{wFPfh`Jtw`T~nA|8*@5h+D}$FI?~E(w+~sG?^d67Ws$+a3F> z=+bv;LG0W{6*Fs3979FebW!jsg{GP{1347(Bq>&SN~9 zI|&vA$-t)#^Bww0McOB0MJx(OiQ0YieopK&n?|^9*?dB-iEo{J1ng*SI(^$6P<6dM zUkEUKIe=3eW3hE^0Ur!oVOIAt>8hqNrseQp(@_^diYZ4E{gHq1Jj^V?VQIU83TG-8 zf%1h3HyNw(dgI=<2o(+8j5iM$tE!rEXzYQ5awUJVU)-wMh|ODG`TaYB7rTm{2xwyr za&+X8XS~AwfK!od1Q6f`LH-$JLmqd%;QWyeSs#O|<>x`nQhj<1w_E^5?U({-09^S; z1OG=)E|yq3Tqyt%bKV%m7-Wh2uMbmgG+C^y(wCQZCRTdk$XHV|?>k+{W?o5(Me(hT zCA!{?ElQnn6rqV7t8y_LWK{DFEBLI^X+N1BA^k$>P(STa9)RwP$T_zY<&JveeZVc3 zen3PkW;1Smb!x>t|&Z(3XoaaW^g+p|9 zQ4Z)Sb#8dn7}|A4u|gp1+%v!acJtM1SP9y!Kb)Kc`@i6{v{g#fRZME(`EbV=C%yObc!Sr!U$Ps9XBBmvpHB8zbqWP^eHdqrag3n|M`GryEdVxd# zs+hK3paX^>1wz~wJUEGCJ0b3iu{+#A?4S-|(AwxP9sqQ28&I@IeHmCU&Vtp@6HOh9 zYwyN4jMwZ^Tu#x|%|o(`t51=N=`wu*BY3Bu5o6cY%@D@VzlUYm58jw>lCL$gExA0) zM@G-=uWxg@EBb|Tk}tGpf#5Hd$9jvXx_L~WeW{CZws-brrSo2wN6A9mEW(gKHiJ9l zmr;)uK>(z<3I#&mb5N4rof&5Wg)4@Iq_TWvR;A*y->qebFh1l?#haHAa9%|sz0bq| z+w$+#LH$v&S+c$S-EN|mb|6^R1+!mVz<;KlZ3k~ptit@V1AAYrGK4*-l2ytT3(?6Q z@Fh`T_!NyF1`c9T%ii{CqtK{?I{SUmUO z?NFH?d!rJLpEj?x`x5cEtsmsF;jN-%@g`WgW}mfBx?MCdv~|W~5`|UlAa|!{9g9Z~ zye`hbw(sW8a@>^yVKw$A88bRw>RMeXe+W{$x8)V2P?c zPS-m`e%DF@cCTw5W&&5rtemj5cXR0cC{rEp9@)PYQf~;u_lnyXn=#LQEM8AR8nuu# z&Qrv7Xhx5VCmJHOkJhAhjy&1)C`a*|RRFP08>FK`+$5uV z4}0RICF0xH2&BfUd64KMaZWu=0}7#p4TGS#jLDUAxzBPpVpTBrmt2gkl;{{~o1Hm) zaK*%zer4i_wr|pQ`W0?6zNu4oq67Y!@qZV9$NMKKk!z#;#{M<*gL>VcVo4kM3^DK2 zOjXNG+ln#FcK`C8COS*Kf&JV=&$C=19zZNnJe zUu^c!x^B!A-wl%Jv~FhbwSLv4i8Zw*_<{>bU1s+t^nGNdkE1`7>Zv-Zp8M5O-AW}s z7~rlzYJA*9S5VbjW#0G*cDYFer%sE55d789bPx-X5DGt85Y&ko-q=I>H^S>LT;L3vVoCAXY%Hjd; zeJ(QxR8VSiKDRbgK{&ZQHkQT^POLTAZ}atHqYx3l?1={b0gdCpE?|-aXDRb&^N#B9 zCp8oe#hRvxVJN$&Dfhax)h>&gsLH2M6t*e1#zh8IJf0c`b@fFM53y&S$gXWE%Nsvl zOCFjs;#I*Ogbmy1rd1*k*j)3hd4{vPq%J$Hd8NkAKs#x~4@fG|Na6C;Px z6~g@|;(t1dR6x=x3NjGTBGLc!E0-t+fX#m(meAdTeqrilWkDoGUG&X?#w}KOHj2=p z;9`Aac=292kFe{3oi*5zf5yU37`Dg=!hew_Bu_ziaP-3UDLGf>&p8j>r?N9sFS`bP zz?y?7gY*0Q5BEAPGckPo|Jq0Et8&=N|D~F&J@ZZfOEvMP!uvxi6a`OUB@Ph5@=-bv zzj#ORAZf^lNIkh1Y$^$}s;#Nr(kof3_no*Uixm-G+S_3Mf|+gPBNpCllH@}&677&= zWUOUKWmCZ`zkTn=T3{1^hQAwg2OBIV)b2$87i;o<84no%^;GGgMWQ-4{i}NtvHiwz zb|G)YBLtcD%nXZ9g%D*wqZ@Do1?~sO_YpyF4ADA0=d=5K^$INFVbs+=98ZJR#yn<7 z1rNRsw}5pff#}?P@QN|0S>S!56Lri_lgOhXaxI4j`w!~L39gKL1h(G#`FXE{TW#E+MAr1OTKhR_Q>^M? z*Ezz6y^vt+Mm6qW1(Y_10aS3Viw2NTe{Yw@b5{j#%iKRier)f!5}5ErdfrdqgDQDU z_rUk##BuZLQ@XSiI`1~Y38cjW2N$1AwUdIO!==XxaL6oCxb)^V!$+A@f=L}ujgPrQ zwJH~wjdNBTGRes$gV(8pe6EW?_i|xE=fC(|q6fg^2t{+eisgADbE?w0M+N{@`LLo4 z&RkEB|NV26wIC7yWhra_U+7o;FT|ugO{gTiSV{nP)f%PZ^OE#pehpG36p1GaTF6dY zmQ~}mIjZ}jC(_41<&^SI5aOOl1nPWH(=WnZhrSFmfxaC9AVUxzIDPi4kF$u`5Z!`^ zx8zB1Lgx&NkcC3k4(LW+@dFGtm@wB27|b!W1eqvpbL&^m#1w*t^!M7uSkx%NUvK}4!d)Bo4ina4xb{c(I08Zkzbv9Dt{ks15G z6Ov>}_AQ}Ek}ZsNYza+vGWJr*mXz!vvWrQ!XKYy_Q)r6#P2*RddFJ)Hf8F=%^F8O> zx#ym9?mgensM?)k*PA zVV7XB^gGM`qlmM{Y;|enA9c_9X5^r3zbS~pvonU_whrHS%UN?yd6afx5M{aLp67Z} z*g9t45EtNcuB&d_yGBhO_gWsiKQi*0w;Zoj2mn1kI}BP6W=@>Xsv=aKEq^QTtRCIM z_C9y5L8;j*Gne1HVZm>FG(p(p`m5|YANf$+^JO81b%UHseXx?p*rQ@-3VND6hATa`^K~k%kBv>x^n`1UULQQ@q-CpbZpJv3ev-YCz&~7m5sUQWm_BE{ zMo8T`p)*(lyMti}HJy#zFxW@0!($VD4pWGwg8ot%7@R?6<8v2?FcI4d3+QY9B5rXhpHQ<7K1nIR%sNX~1)Pj0 z0etN=Ot_r^kvnKSF?Zs%#8I$wW@XOHeoy-tLsO=U2pDF?_LGgGBx8mVOgweeZxB34egjnFnimyT3M%7{jKm&<;CM_Fs0jC zH)CEn#~mjKhB2EVm*vzf5mA@WIF;sq)=CFi^OfadO?>pNuROvD8>dkVyY=c;hU-46 zH~LilIs#7|@=acAf%u}nTBmoXo@q%@>?EMHrrHw(+deUrg(S*Fm?CQChf zC6&o8R$rc`;;Jc;#o6AYdCI=U%pr|sz(UE3wztjN;6yY&#H?!GrtRTZmaewx`r6H} z>DM!LUn;vp&~COk|DqhIx9-BV6s48V15UL%0)7(F!_AXUQZ^_kl#6yMVbYAnsdTSs z|5!D47UyIbSH3HN7P=dMU$vAtwQ=`2_Y7pb%wvBze1ZCD;L1vMZNci>;h%OJk7?Fv zAs;hD;<~@;*J{V{&?YmwED3@(j0(|aCRi2ynHsOnPv6Fs>}NV1!@^{;Ox~Y(ihs&6 zJ>Gd{?;N+Q%Jvb|NrrFTti*6ULU?(OLp!VhoufJ;+pFrmn^pK_-3RpTNB3P0wO=m@ z`p#IF`^EXGh3MU$kbE0!^DT;7^pOMdSb31*+40kpteF*11xz34dtU8W$O+5#z>#!s{L7c04ZO`_sk? zYq!$=K1)+x|1{{Z8v)yZ54QXiHFfea=F_sCCuKP$0UNK*20w*znO-$?~p z%8z08G2$pV-x-T}rX4_u7QLm$eUUWkj@rnGl$4SeUf_y=zNzPe96$KNXdT?0U1sUk z!+CoYCK^UAFiy<7`G4BanaUggwYAe_0(jd-8)3rA`eX9gax}|8rm&IGg(|_mGrEn| zX6Bw^uhPp`knKTHkM3kML_s}E6QGtV(yJC`*5an9QCH~;PjkzMtqt*tu8KKX4sWqz zZ(oKyswh`*vUK-kj6Dm7bGV=j({9wYGMvP<{g}MwCYN#VlJ-wFfEJ(C;HqA#>P2%s zzEVz7?}4rE8U953bTl(H?8*el4lcIvYUpDPZwPf;S*_-X_-N1a6voW5c6ZQ7BLzBw zypK^r;Z6Y8YRx%nhHw8^yIlNnZZ**HNNOG`>p08Ijk0ynb8%|)MA>nuQgfx%!;5sp z8Mv(S$?w;^fEQJ8Ni{DF)0S@~0>*`=^70Q`nDsut1@oDwl&_oUA?=F zavrE`Eew*KO}ze1JBhy*Ww(>sNiXHLr$3v>`&Fh16yn7<`*N4qMxY_2WA%xl?w4At ze%%Er`nObl*iG*kD*_e8%7dC$6?z;kBjwUn6cm(xG76;H7{}?Y%Q)rUJ~^4g?1C3Q z`y-oa=lHIO78m4PB+w7fi9mZ1QUY9qT~QEJkwg182S;LC9*mrA$p34-NFCNp=pf z4S_xg{s+N|qP=QgKVtPdgSZyCPJVM+lQzP$P8x-rr#(oo!3+QbNme7jl$kIx8LFsK zg9d&bzJjzDoub2~22rn{sF+)8z`ew%2brNSx&`fCjVCqaq{oIQfX|1hYj(UKYS5&4 zL7@W#uCNXDudIZA?_oi&6+&)q?{wDAR9C>2^df-Z%{A20(wHZa*_ZIz zHV)pVCtTyURsQ_30dDEkT#c?p3EfSryEB?FoNq$s%K~@BZ0-fLL6dt^jms_lD=O{! z0{Wqj412})GQ%M`g}&%G?|F!#cK<^agxlMgBk~aNtGTf#6}wtL?Z^zLX)08Z`MHPH z=|-Q~lv+m8qlJ+cQhvj=%-+b!535Ve61v6=XZx=^W||H8=Jnr@g{tzq4!aw!T3dD_ zYPb5iOkNb3$#5~!xEB?QjZHDYt*@?)CKq{~jw*ZZbscb1Y5+{9skLWv9kRGHbjJ}u zH7P}(RF44gy2U?>p&}w|LL^XbK=UqsCo!6Cuz2ZZrHW%m>*?fosu-MRHAWZC3cUuv zGFL_5FOZXtK8iIlj_9VY^X%C+6T$FJ5eZW~i0V|=+L6jgfTB0(EiLX;#!S#8%y|$4hItGk24PEi>cm@j z)Z==c+~F@XU}bF+_hG>6wejb(b@`E2>s_Ox~Xi8VUDSf_#_lvJS2?Kf}q?!@)F zCm-uu<}1)C8O73@M>ruaO~W4>>eUhNn?b_)`%%(oBRH8sP67#SL{WS%`;7{@AjLTC zBVn0!S|!vr;L@PhwF@RGSv_qPg>I&q7OP!s&QrmrebKU0#}gTuC7(C?l<3FSpdWyr zS$S71ZQ#4J$G%wFpxQ>>f2e+yn(_2!j+?`m_p60_n!&UKKW`ivKUR~pn+e8G# z{Y;o>G5L9etBj4s{Ch6X)AO~!cMGqhS>dY1llN?wjKXp|-@Uh3oq|B@g{o-~waM@e z@ppIAa0E2(Gkl*fx2J{R>%PHE%Ue~KXP_B<$_i#?)!lxAuk}$=r_F4d-CUaQ`KZZwI4}*f+C>#^>t*Gp=ea4c`jBJ|xCgwvCTIpPj|epp>UR$Eq&u6SJ)& z-}rK=)Yy9S$ts4vL3it+U*qQd%IU?}dVDY6nb=E(8Q9pSr5FtdO@f+_Ua<0@Xhd9x z?azIXng@2n!Gka~=ptBR zbYZNMiZh5ad7|>#opA+U9;2O#e*$}UIE&4j%T(Ee#nmD{k@nQ!SA+~DA|*p&ik3PV zuW+>p^brtlfT= z#VU}d%q(=RYDvL9emwA9=eUu~gGR&P8iU(v%k_FujyQu&;GzkA&X*D9#ymsX?*()q znTo`{Xx)OFa%xRz4H>_?LGk&Cu#47K)s3ctxhq@I{V1WvA=jl5|DIs3d+ur z8ql2|Ixb(49eP_jJgm>z>LetWmc|koiR;GV#Fpt&mu3XhYwYz>k+EsCfqP_&6xwH| zIfbOBm<|Zjn|F!MbKsqI8UO%ziIIGu#dUIl?;(sdW%MB(2mmRa)ZZTy7-O2xg^>jF zVUqKixc2Od7L!QN!wFK-QDF)&n~4lEq=s zp(4TGLiYs8I$VGKdr75Npa{4oa=_7Istd%q4y{`MTf<*se>fL0D7g}UeL+c@ND9sP zQ{+m-6pC~GHgWVL0bQgfQ>TD3CCSiwp*+{W%+CJP_>l;VW|1cMU%MgTf3Hrr901cQ zEA_iT++?#H(uICPI!;l;E$WUm}8ULZ^Fs=stWX*r)P~)edZ=e4|12LRsywv23TWV!2N1s?9 zoH)p`#!Z$v+}8vEh*QWc+y0@YxDUSt1OQ+ZRQJvQ2YV>W{rAf?QiDiPP>bPzC?Mu$ zFef?k;rc89z(+yXM*g9XVKyJ~av!d$0RX%dB=`LTG9i)tx8?r3ibN^_<(=N2en4{F zR delta 35498 zcmYJZV|d(c_x+v5Y}`!T*tTukwrxx}vDLV-Z8x@UyRrTBx_`(2c;3&e*?X_c5Y~Jq5mmz5jvSG{Od97tU`OPXP+>qbcF10PeF@RU@oSZs(ke|NfDZC$1zMoE*m!hNL}YBz=hV=S$^_F>V3I{gY7)1T%b*Lnx@~#5gFTC5KedGdUJTX`UrW%qQmhgJV$(hO{xU6zNTUMJgGt`)XFRm@RQ=cW0O63)px9 z;W6Pcj?_6R)iXc7PLZHvXf9lxCU;IuUoEn+7PZn!p71Su^>H(1%oTHI2pI#Y^n%nF zrIpLvVe$LElL_uuzCpj$2*lzn;+OhC{P&-M_d#wGe**(^OsWgQPs(B&g>L1xAip@(u6SRMdf$&AxlQ>rKGo|OOIl7tgU}z1gTEiyns9@?Rpsx zUsCN~HX1|iSCkDN@xnUHQ+}yuD%HWGA@;BPrk%5U(D^lW(?u%^2?XyA0>cB08gU@B z^BNz9c`X-2C1VuZ9GdWUK{hp+q$y?YnyGdKkdRWD#Eib!cZ|`lMAk<4s6nK3+cs<* zrYnXgJrsJ_TNJ&zX~t^MyUPCAc^qj58cZRw@bKc2H>zszL&_t@qJv`e`Y_4EjQt5b#Yh=XD_#a`_O@8utwrd9tdbS3cL zF4~$_%h`j2fi)xrq8k7&L=6MuMLaKW8AxW(Q!d^P)Z0(NHp2FUegjj&fYC(s?}mzg}(-{(!?H=ElbA ztMdcc>@N_kaAiPh9MT}nXQbu*1YF5^WLu#(Y0sdrpsWsF)+(T$(M6b?0Bh>m27=hA zC1>$8ZZYm~DINWk4niDk1$D{0_xznD$;ROkFI}jsE>(zgkw^!OaAI!_++~3uw`y0VJXju zGlqyX2_TqiG;kcoCgiNTzz@^!S=~O2?76x>N97>yG?{wGgV-PV4%0FZwH)fDw0D@; z2$4gBn0@1K55bSY_rq$0Gd{SIxQROYe$55{~Ck4buZxw$6j64YDEFEdJ;Cvc* zg39!R*fxwu!k-fMXvVBwg~f?B4S3vGoV#u#mEUX6K(o#`9*!Il>%WWv=ioDjjHIo0 z29^OaYdN*V)&Z==Oa=S=1d1Y6b2KI+L&Qs&{&J-nokwsJDk@g@OW5N31O-|GlWv8U z6SLN6aAx-ja+uBN6eActMh7%|4#}T1_=+G~#4{P+%jY4-rv1z!NVxjd++Qp7cqWC< z&5l9m2HISAtltymnr^rMOnxt$8O@-wMc)TJMQ$^_9Yq%;c?29u)mLJ6BS-Z7w{A9- zFzHD`KkR`D^M)Ay`hxIHlvp?Z*qGS1H0U#2zc~Kr5dU_PW|n2WYEP@08=rIk&GgD5 zd*2`+h>l5eb2~XeXvV8&0Vy`||Zc@E)^4JPl=KENu}M`yBlQTSYNfDj=;H&*)xi3VR%qpL*Nw<26fRh6HF&DA zfa4Cznu`+h$!K!C=>Y4@Gf1zgb#e2GYR5rzAvP;!iS!7|BnG6@-{X)hni4hDbDIL0 zM!ZUn&h|2l;6c&*sqdy>VVU=Z>tYHz8}6Ofr##WSj`iG>Ay4 z^*r?xF<}l7Ukl%(;9rlUCqfJ&v|}+L5QspZ>~;o+GbziEJPKCbJ0uprV4oB%{Yfg$ zkp2tKa1unl%w{q?6H9u8Tq8riS)D-%;U_A&Oi_6GpXB?Tug!ufzjrX8!OfjOHzNb~Gut!iB8VLi0?%}7E-oBYlEjPh_=Qa2Rn!<(MD4)9 zr51V3?Z#-xlXuduDxT9+o7CEmia57f$z4j3<88BeM^Ii(LXswl_arGqT`i6Ajf%c+ zD^fbrSkD`?%y5!;$Y9y!zD@`b_@-|88$MfI71ds(Ete$zqprErQU^7m@Q|#@FSu2f zMPWX0R5mNUQ5EXRZ+mc41JYx*C2Gv+3;>6J7aD!b}1VBgk-WY~9K~#@-P$PRD~jzEW;} zt+_U1vZ>Bq0nE_O;=NF_3tLb7%T%S*z(Eo4mRMNPt}7g1bXPfq`S*xrB$YzG9h^pl z)<4kSH~2f~r5ge?v;FIpy-+VL2UVT%rg}yiakOv^)eu|TB1|1EOOQPu$Lo@jHiA%9 zVnK6jF-Eh*uENA=(j+C%7)eS*Cg@E=Rp==Pay5sYc*OB~eZOHHLo zF7bIs=Itf!{INBD>nX17qKcv(Aktgk%@uRa5$$iUppur?m|Al6m@z@|AeVRr7ECn| z-{wi3n6GSEQS)ZH3?~zZj+LHKa;@XcvQ<-w08^_l+8M0l9R?kA&Gx-5Ol*+c(I^TN z)u^^LR7akWFkz{d6YCI`;mJL6`;F?XJL@;lMX~k}b=s$cC1z$@@q?cRvi@8y-0}TQ$=kwHMlq} zWE+41IZYvxmVx-ZJmoF>KNL(`~l|g1~ z5WOq%9GweJ7aAf{JFLwQ^A|-b+y*pfAR^YAD7T)8m}-=?I=GnyXWkKPGE&KpmY_sc z19w`ZkjSXS(b~K@M@pqk-`{D{uoGgkPL^ED;$wn+xhM_xJ1f{qP!y>W9*?MlE^ z>LwJI*gI_wx?)ZVJiS2LH8GRW*Vce+yc6Qv*)^JeF{dnAmQL6_l{aP$T-P$THA{2K zG?HTGsOS9gO;x?{b*nE=_&&H85J@e#C7Dvp!i&Ma9Gw$;V9_(JwZ&@*hlqO{3*ms1 z|0lU$!jg{cmW02sz>nibL-5V}KM7(x?hJrqn>T@KfKX#oe@g?qAZ2e($bjWAj5g&FJIJwdX9s{Lx-OgCs zl?$896?SNJvm-S^laQ7hePhpya}tJ6U|idyrsfaGiuWq)YQ#!9&a#+%alN zC5KMhx-rI;Up4t6x=rF0fCU1WIl^bEnt#&WJCcP_V#%C40(2|$Ynll1-s)<0xWz5{ z9`co!m3K(N)d=Nz;1iW+mAW)fyP4hsN$*b6Y1u9_hurjj<;QX0NvcMz_&_45aZo_u z7y9CG6sdl2rco^ z?T<*1JH54VCQ65#o?o7DUcQ0HP4owNyzQ?K-$3%IZ=rF*onJoOAR`%%>wDQRu`wQ7LJggaQPzTwg5L@Za^j3b@SLfCd9I{tou<8bUx~fWg8hRgj`3eG;hv;dK%MukHcl=IBhAg_>G)NJ={f67_ylV?thzOHBUcC# z*Mc*{dIW%@YR0Fa99&Ca1^y*uexVy(|Dj%gEUWf|_hRKHQROz^mWOYcNTPN;h8jP# z(UvO2K_;rxZx;q5>OJPR%DLY%s{7F&KANcO@WEGww+_Eo@yWNs_@(nAJo0oU#ckTH z&6Y#zlN?z@j@{Pyg=|{ZRbP0f-{3=fhG}Ee?xZ zKB>6w7#_R4H8zFv2+3MJ>m&X}vl(02d*j)yoei-e!KdK3Ipiy`1uu}A%^vo1KkJ&@~hCRz7apeoHO)RMhsT_G{0j>ECI8U+6CLK4n zqZS9(CmqCCP4B}pg=y8MMRL4Q$#08JflQTQEg{i#0`r3%Ex3k3v!Xyz_I8WMjyfEU ze?|Il&jtc|dE}C^NNs_tHVX5K0RQi}$uUmY9N(1kg6wV0bRmbtMo29s9ccwgpF^|U z!jvs}oY#m3Mv!kfo`sOoMA3l(0%Ona5QnLia`HYoPhPoC&RdU9xr9An(MEy0ccY`) zuLT*$uicLDXVK+XrBZ4PYcR!wd<`d-?sY$5)B4ahFikfOtBZkG@P6BLtJT~M{d5|n zix46WFM;N-I4`54N`AIMt;~LhJu3CI;2V0?F>~sipi@{Px#6Gpijrx@s5t}#gz`2} zPNn|kFeb1ySTf33HF7eHvY15)%%lvO#6>#h)^(Qa8&s9?%CRyUBY}$$0>=|D^b84Z zaX!LA@kCmSFCd@)&fbPfyte5QpbdWC!q z0skj`5F?IosTjHlWAk4K)W(p!pEyE%!rf&td4os8UP7VLHFJ!h*p)E?fdi^29&zi< zyJ<7_&m5r$x>hGUPNg`Q1CPx7-%rye z;EY9$Dt{u91s z2%$RJT9a3Bn*~#%C?dSk$AI?C)of`DQmP4SdlN6Vj_k=!y|pPbcJ|bzdc@7;xuiZ6 z3DvY?xKp5FWWQwSZ=(b8eHv^^fJrGwNQC+lh42GK_T|u$}lp~$rbdDF-Jwwya zTjh3te?>`vUM4)1P^v%xSmPFZqk=|1mbA^uf8i15m}Y8vL7&$VF>U{|u?&>vD^!!Y zBD%FB4Rx2Uj*9Pgjuvdo`eX3lrWZkCldSxMKhjYDW4ADr62M-0TqYF!r1%-Iz|bsQ4RQU^my>Mk={$PZmfUuw5BF{U= zUJgfI=9iaWpAZ4xD;b|%{5}eNFb8yX^W}ps1QCNC1P`=`Fg=);jZ!HjJB30wh$BSo zMVLgLti~sx*MSh#wACl&F_}*e$YYfI(L)mynYl+AqR$H@f+@{&|rcqA;0ZR!=_c>J&}{K?*orclanXd?wtc{`{dM9yDA7Be*157#GEX?`Tr2I zANctkmgBLTW$l=Uf zr{=8q3Oe>6Vc!RIt`p=)R=AV*d;B{xQUl4veC;{t)jSh8uBiI(hh3KdAYVDVwo;mT zK252?R{eZYoeUX*Z@+L#$-90Nq8Mv|zJ^X+*aGWN$){%nNIf~dVlhNqh3Kh}y%|dg z>b*|UYrt$NEKQ#)vwN!^=d%e*er$yg)!std%RtW8!y+bC(KZX?9zTlfxO1s1P~Ohxf6^BcI4Mq z&tL92H0qeQuRLUK&-g(!f_yMU7Lja?A1SUB4)T_Q$m`K?X5!7F0p4C;0f zVZ#3#G={fBa%&i=m|%f(tZ&}Ra2K-XGpNY?uvM`$#6NbGKj&WQHDnLA`W+5PPA`)3 zh=J)BM#Dr&ar3{pq}$?w`u(4{MgEap{GwAiJO8*27-zT%KI#j=DSpi85AZZ z_`o;OB5Bm0LH|$Ov7MQcuqnv944gy{e_-wlsE3FYA9e<=AH@u^YiW_kb0#4!_;R4~!}h3g3xVl9n}^i> z#oG!&$xjfctoc<{Zgq81ZOsqPJv@q$;97Ao=Lh-nh2o9M6d3sVlchffb!-Hdw1uKY zzMg0qp;Kb9H3J2TgrVh3k{IiF)dBEi{SZmTy2EzL+`H@|9j}PlS;r?r5keP<$X=zb z@_qX!XkwQ_=|Wx_*6CMFl)xq2<2yzKLh2o%P@%HV3Mc(QY>Q=Oe|#dT)%Vsb?q0*T zt z27pe=goFDJ!-q$-ZFbQjyv=TG<`4ZJhA%YSLniza#ymwQhD&Po+`!^tK9$bm^7Q%o zu}=};HNou5&*`c3S*p?2L;U6m7;ny(nKV|z$JY%=ogH#K3LCQrWLb$FGM7>4*j6luR!?%rl&z)cA zbs4{u{gwo1gZ(qwMon67&sWeU297xZiESk>NJt;8zl3nKkg)F` zh(n2xaOf!=L`9IjQ#giZuEL+;;!=xLp2Se*pWEJlg!Ttensrid4UbzBHnhEaVWq=C zP?vcT|G*(G1ZNSfs!FZPzpJNhEiZgp_re~tc(Bi0*&?;-(xBQtKg)Clz* zt;qR0edJ4%9)H!0;c2BG3WG5;Uwn?P5@^@$x zNxDk^`M!O6zexXaXuVnc_e0r*n}hdk?#!2xk9ohw%k`=L->EFG;|Z=SuDeUZulHLV zL1b&hX#&2C6Cg_N?pkN<{j;EMW{k%WTZb~6>?L+{3b1^o{bvV>Y_FbBl-Xr*WBtt0 zruJYA@`Up|X7-IWmD=uNGLhfJ{ezngUkTj#ea%H~RXREL2D5_Oys9QyKUyDCC7Kpi z$o`y`>DATQ#hJpXG0`U@6XRb#3r&zmAW^(!52_IQV_4pAV0xZcCNdOEA7}UxEU{+^Dw3rUY<9uI z`S%QV=E)3<${>J96uoXgs<%1abI@>C#yOz}P0-UU&JZeI%+F}QxkD$=(!fQO!FDzO z$S?J(TSsfZLV6>CAS(RH+hg^QK z%73szbu9!|593dFS$Q7PbuS4M8~4*K?-C=8XiQOJzTk=34^fxf4(Lpjo+QiflsAb z%=Yk}(Yv1h^W?$o>9ISn`Q=esXEfP@FD>N?{wW(aHgX3Vbo-pX3G`-xfZfZ|T{IF3 z^p4{zheXD)Pj34Vrie^Z;)tq>hm9yB*?fkB;rjU(yZ}P1P)qch8Uv1SM=>M zi{w`rl@q3Nv2~2$2hYZQ%hOmp4X&D3%GF$g*6U_G^H}+Tbqe;btn{upM?{pcK-W)W zWt)HxCaM5atJF1V1CYmuSAclo=)Jp@`$R6ptf>?BtOlnhp4L#_fdc0{5aIy6&*yw) ze`VhUSrj+bk@oo+YQfSMDgsM#saIhpa7yjk07DVNFO5e(7g?8c?G?XU+8yr#8@793 zSJM%WP@um_lX0yfUfV_9{NRMptG|%GxqX}|MNK* zAULaTJa)(dKyCrMDo>>3sJeLoH_3|Tp*no{TCPW1o|=>Iiw1m09QIiBV>2pOC#p+d zKg^)Wi`)IBhqm8J6UUrWI_5~M29e6wFT0_-ncb+ZZ3v*{+Bi&Y(F*hEW8Oe#48ywc ztL_Oxx(&-Ipyvs7PRJi++CXdh%XwCiyc;>vE!NN6X@giY)M1*_I=q9M+gh>%0GUd{ zG9$X(Q|%T;8v!9PA!oMZ^s_Dnm6+iyOtUa4u_3@8iouKQyTn}FtQ3SLhs02CprN5I z?LYy$P7)8c60W?nbGKC>pinO*eU!8bV(-)y8aaSec@o-A$*%nkK0h4kUziQvE^kv{ zcWFV9NJsaS*eT{7b;#?I4+_Bsjen6Pkh`l7Wv%G80ss6pqVE?GQ)WDa2nl5vz;d5d=;_QR9hj_q)wAu(q8Ft zv+V;&(zxX7REG+;cq$^|$*eM>EVu@hEyI~SI~y6=QN?iu9RbQQGr2rf+LelNV3?=E z^@lwMFwaU^GKw1)(UvxVr;upcKBwJ12X_=>Lrgrxx6lx(f|)9As#kV@WGYO2Q?vBL zchT=6Oz|1~IRs~VU0#VyyxQIBy|+i@w!GlY^QQ^idUEv^OuzB6-!m|hMBA5?P1zDF z#dUE2OEHw(8FBYVbNEY;HT#p%Mcxo@5P6O`@T^GKq~KC>q`oVD>TV%`A6rmAyhG-x zDau>|I!s8yDdo~ycJ@-{{%dDAzEyt&2MtToP7Q}f519>evw~B8n$PVu@QYH2^N+*Z+qY<977=cXVMbu> z&Rv2W#NT;$v8^K9L8B{!(EUxe`Gj7m{Pz*Wka#1((p4wRhzpEW$c*6cKaLS?NG=Uq zihxBDZ)!?wggNiY=eVte&v}SqJ@2+8*9=$mh3e>8P_A8z=ez#+A2+l;5^ClBXD_?| zwX=9h13WyyN$Gw;l+UH|vZl|*vqh2b`NoK9$;x6Vy-}f&K|4+z>BTl2Qmbvf?Sc zG@)RkqB{AAN_i7=2~+%Ooy`W&danJY~Yxkx~5p#TKpe%^L@r}O5rB-ue&T6ub<(B+^b zCx!Dd166cf=EX5S+1V3}CyMe5vFszYC5es8q@)?8S~F&Z`7jt~0&nY6m+J+wJUp`F zjYhsofr4*qyS+)7Jf~$mZ32m!J zj7!4D%8Fl;Q>?rGlfIiL+yq=TZu!WHW{y!hG+qe@t)0FwUpHjAORPr!JlyTL3~or# z$_xTKoJNTjV4aTmp?E7#`%bwiT+I8}VS4M}xYDFF^jN6OHD!F~=2Vx14D9x$S=X&R3Qn%i^UsUg%9C&9e$(t{d9{+IHy2 zn4sQ1=Oe;INItCX3*O(&vOC81X7X1TUr4T672DY#2OGIy-{-p-B{gg`vRFM?ss4Qz zJZYt8jP_tjcbs%I57Yl#d-ptWU-E;C(S0(@*NK^v%zew9ZPgjXd{^xCdLL1=pRqGu zSuPxR@r!Q^?VMb%qf{`$LOehQ_Cv~gV^_~!BKHlS^c=?4I)wje;>X?HL3z?Uk1}?e zZ|cU`9YNU#8m~3Q#_S;0ooR3X*vYrarbJn8GKV)EYYIObx%$<3q;L3%Egx5FoK0uj zvC*Ty``&fC&3MTsV?W;Rc?-3avwzkTTAh2sBjs&5W~n+XX#m%vD6VOUPF5eAcMts^ zHCSe2EdTcJok&*FZ&LguL#hAt@jXetnh9v8*eky-h~lS%CM&Egndcke69{V(T22Wb z5~AosUA`uL-?=55MRxAiXe06SAKew0VWEJ8iN$*^EjNiOhk0Zy@Vc7JT=jZ&`2sV< z!UgZCuIWLl=@TudT2*Q#EO%{P^$4|u1IeOvGREdVi= z1fRM=D3-u!nUa$vc2dXq4p#YWJnYCOx)Cj_Jtmd9(Z%Gdy*R9~Wc}%LkV({(FK+5;|B<4Lf*+yp$HSS#3y)#MW&nE6a$3 z!|InM@6ZC#(2%*h+6{Oi`h?GsCXT2f|D@cfg<`YZBTX9RbD83Mn(+O)Iiv|ds$}%# z=iRXLL-f|gi@$`?2?RjK2o_3_W416a8YMoC?t(CGvNjImkMjVDoFh>{Qsic6-NOag zdQMr-A7k|r4lXWww!AL7^W4kG@I`p>$X>0N(sOkSqT**Oc_hqjcg~&_FO944Z>;eA zlR-?XJXS`KU5SwZ?Xrl1mFw>O(pqIPWgX^>ij&}7E%BqGiK>LId_3Rj(hJp&so1@| zKE|GD30`I0;o0>qez5y(hORuZcaQQ?fI1Uy~5$j9fnrPE}*ZM_0rGAZWK-Jk{g zUBCue4U~SYEb|tWr~Vc8*@aA~QU0r64K&WQ8tbz>0Hs%6^C|bl6c?K4`>}Cq%Ze;B zwUgGFln@U=wdG{uN2|cf0U-Dgm^l?}DKFh+?{AdTLwvG-KoC;)+ZgOhbj{7HzvynM z53=&4OdAi>YQZLGFOPsP-oVZ>tX}uKdj>2IX}*I7(HGinVwmg+_NM^Iu{aVIXNi7r zDgyrl^h0V&M}7ZBTkwCQwULklVoB|^JJyoaXnZHGM;rJ?D*x}f&|W}mKNOj10EYj@&_L?dBG=1Y3tb60#K(9gbUO%Av4$;f*!Y{%(zsF^ z9E6tC9llqUw43J&l$8VWS*J%^jYtVsx34HPHCA$FgPdimM+~CPD@}srLW41_%Y9Ez zOLe`Y$c03V?h>5H`PDUTfFAlyGXpW?XpN?V+AjE*mrc`OOS#!!t4z`3>sfC4GmV}&5e0UwBwgapAF4oZM;KP{?=J}W!pl3%~&Yx=(xiUBh-LwLJBgyM*q(p&sy*K zW0M4=oPbIW^XeF%mvakMPBPNCl&bNI1+;K-A_yUsfk^70WVEF+A+ZGVI^4G*r}LHF zHTeXT&g-H698V+U_5GZLC1tz0A@ro?FV+iPh=NS8$yDEe!s|HZIri-azCYa6fTdn@ z^?M)_RBBwM+u7T}ZL_DzRhUd=s?VHv5X5c#0Wv&*>nQ5ND_kqin5Tu2RnSycFS!$ppIIa^XeNrJHN|bxuZmJ_p(Yz?eowzfIWTvE(RkE8W6iV z>st-A9{mAv_X!IK-a<6Cim>^|U9AIM$6^nfDaO_lpWcL1YoOP=u^itH)Mj_u)vROyQRDU;i)36-*hRQ|+O<~z-5bKUjL|*V^GF14 zYBL`9Ie>J2xi~Wr(NP!746oye8*B@ ziI)%>9;Rf*^Rt-7IkMuW-cwn%xP7}-WT~gR9O!EiwG-$~ft*~=`I!in?3U~Z1WDe~ zTnW)pEogyE98k{d2;2B>2K8j_7rSO0bBw%!a2y@XEAq;n7>7Zz@a3eZeXyxWc!NyY zuB{`C=~+&}&VF_V+;cvbp_lOez0i6w4Ey>zBbn5MKTfm6#|jzqNXZ?9;j%2nZWBKQ z%Rs^RU9uH;AWCrAA36pC{dx01o?pg1oLOtV_w%BAaB7n@*SUaVEYDxKe(!wURG%UJ zJq0PVERTN+HVO>kS#-9G?XP+_`gO&h>^*c+?$K4@II`uw0-TvL$$`|FTm7ecwB|?D zr^l3L^BtTXQLw;)l?MYwE6m*8q;2PhxLiZoUgSksADM)v>YhKAOdK6NQ8Ef;)7V+U z2)R_KXg=ST(K;d@2G~z4X!|j#y|c`nv);MRJd%8036rb}KmV63;#m1zssAO5ga0K9 z5;+mzy7IgthF+MC46Fv~04|Q=ys*xI2$geU}}hVnZB98 zVcXroF=sYJxy$PF2wF8|ifIgbk^K!+{;PC#+ni%@Z0jM}_4^@g?fZIN%alB2mt7O`-gq&;HL|Dz8R+RcC7Cz)8Khw7m-o=Blf#Qx^t3Vx6zeo2 zQ9g01-}1S!7xrYqIIz#&1Eq#t5QmK|`RJNkvt#Lex>ZVru-Y*#TL)+KbF#-W)um@f zx0HStU%9_>_pp2ijNdRT-+tNoSzs|gJPVRairZ}pc0Db8L>tNaDSN; zfDz1@@=+x0Ix9oNW)vk>>OY0Od?uzJh&OKk{T7J}QPvdyhGOvmnTlFCRil2Q~IfEsYVcxu>R zJ*FHyoAyMj&4ERYu|#=o?(^%cfx=1pL@-2|h4gewqnG368Jp>5=Ik&j-aqvNA|>e5 zlsc&+*I+kUMSojcVIqs(h>8uq@hjfs`#YF(Y?9jiRk#~>=y~aW>fZNTe%msc7I zE)h2AI#+3lql!jeQzJPTEJcQ?8X6ram$@XqY**MNZW0S*%$7vy#ZwNlfjiXJUF76Y zrITMkwf|KZbrO^JkT+x)9jCfM8_49@@4Xg&i*FsoQaKajDYBtx420Waw5BY>;J|4< zEzlO$FeYdPNI%h#NQ(&1?jbFI|9h-79*hP`3?Ybf34Z2*hdrsq%8cMLJ8=rmG!L`Z z)-qLvInC9(eE9?1Cg zVD46{(@u*UF8-!((DNHL;QelyuhjHf@yP9lX}tN>n~;7Ocs5un%oZ%uFgosBf5#j^ zx|}cnx57e`d;BS7eK3L-HbG!-!}@rizY^64w>H(b`M#Fy)}>lEOxk(bBp8dpwyM2zc5$f;#-8vq4ZD-}6)WQhk z{8RfYoClFCr!e>##pPt7PI>VT{n(e%?h7|_|E*P#f536H?IDMii*vLkPRvW%b2a!w z6^RGKx`JtS?l9T-moE9TlqBNDoVEb=L{8>4E>lV$pycp3hhJ_~+;#Um?#f&~9xcWhu5g2- zoeA{0ZFqE6Y^dmiU*-|Ws&hI7VmlJSDp;lVdY=0{14I3L z76mGXd!lx0zc2fFvcY|@g4e&dg>;OP`^HlbN-C;s zfF&CiiOOCY4h}80Zw5K|U|JDgOLv7xDoZ1 zFlw$Yi6Is4dLxHRn)TBJf!X`|=Y#1kZRUvLg8}pKmiQTeXjtd3p?+0u(?fSU={r)g zN2!v24`*)n!`(%w!i#hYI)mEG3_etU!NMy&0AWPtsMo23#R z6#RIygqn?IV4s?t);!&+Y>BdK!#tTpqm#Gv$nw31^6m#E2f`Zp!KSi6v507jD63oz zAwAC25~ozrr%A~G>;rFh=Uv0!Kd58;x}@*KI$C)N2V8qKmO(R<@x`QVz>sZ4aCGK| zXC4bBrhE~!&!6TKp`+1=!fSo*OgEAJ>%7uEp@JT}1(*t%_RC#xz=!#_VQ*@sgr>)8r*DJv>t{%-O_ zRGh_{*nBuO%A5EUcG;tKSjX*Wqzum{^fsZhKM}j`x6?k+g+!3(1KDC6MI}dm_hip* zNm@X2*pBWL$yFof%UvQuXG&Y2pJ_nre)ITkigs4_XoV>8KDs7VNKh9_Tig;TL_pP$ zM>uY$DSt)sO~Wt+$xPS0pV~VTfwEJw-(l%@_5qK0ZS~`~#@8yU4OP*9zphJLU9n1* z+cy+dB)=I$_q+(X045xtE=Lio&OKQNnR(*tU%{Hej7pF}GSm)km`8C%@DS^SNZRk z@UQT190g_y&Do zCD)BexxEeSuE2K#c_<_)OJud%xd@qpU!1}1GXEa{m_TR0CE*JazqhHR*#B(U<^N0A9SoNQ zTwzbZ9hPdtr6qOYQcr!@|6HKt1pb+;s$%*rLh-)=P)i30;icn224)EW04frbp(GrV zfftkAS}1=(6g@*L-F~20QBYK5RVWGD4H!v-!~~_lLk*^-BtA9M-P`Tb{mSfa4KeaV z{1?UqjVAs8f0XgIXpG{6FEew_+;i`_cjnvo&tCzoV@crM>1ng}M(;{%K!L4q>Q+x* z)veHvTu&x$7#MzN6Z48Zk}>gRU&e;jCu+o~tXb=i zIabwv>3gZ?F%kErvBr=B#|?;-8#v4kNyS`?`C9c+wPx5f)Zc0l0)W@rE4MO~oW_^oIqBWF(pv@OeX12=gpkg2R33C#W-^elBfn^X=Z zfyu3LYzdc9EMN*(1oA0ctM=KOhO2+LYMsOh`8iw@C_0q9R3Z11oCqvcE;?DcNR@CM zHwu`+EEgUPBd`UG|I+^S%qec-*2w5QcWPf&&qu4_4x=PI4;7fH{ImE1?v0d-C1}X! zaS8VYvd{Ukvx^LJ{J{ig=ezMqLjgtJA2M3T1fPKUFPM7u5!2=JC(NDUcKI$ZXV5?3 z!FymV%kVmZ%nwjY2MDcD`jnI5Tw8w&d>m!9KWFwavy<&Bo0Kl4Wl3ARX|f3|khWV= znpfMjo3u0yW&5B^b|=Zw-JP&I+cv0p1uCG|3tkm1a=nURe4rq`*qB%GQMYwPaSW zuNfK$rL>_?LeS`IYFZsza~WVW>x%gOxnvRx*+DI|8n1eKAd%MfOd>si)x&xwi?gu4 zuHlk~b)mR^xaN%tF_YS3`PXTOwZ^2D9%$Urcby(HWpXn)Q`l!(7~B_`-0v|36B}x;VwyL(+LqL^S(#KO z-+*rJ%orw!fW>yhrco2DwP|GaST2(=ha0EEZ19qo=BQLbbD5T&9aev)`AlY7yEtL0B z7+0ZSiPda4nN~5m_3M9g@G++9U}U;kH`MO+Qay!Ks-p(j%H||tGzyxHJ2i6Z)>qJ43K#WK*pcaS zCRz9rhJS8)X|qkVTTAI) z+G?-CUhe%3*J+vM3T=l2Gz?`71c#Z>vkG;AuZ%vF)I?Bave3%9GUt}zq?{3V&`zQG zE16cF8xc#K9>L^p+u?0-go3_WdI?oXJm@Ps6m_F zK9%;;epp{iCXIh1z3D?~<4AhPkZ^c-4Z}mOp@Sa4T#L5>h5BGOn|LS(TA@KB1^r67<@Qp!Vz z2yF573JoDBug@iPQ=tr2+7*HcE3(5`Q%{A2p%psJG}nJ3lQR>^#z-QI>~|DG_2_26 z1`HHDVmM&*2h2e|uG(OXl?228C|G32{9e%Onc=sVwIV zZ=g2{K5s0>v2}V&CZi1_2LA=x)v|&YrWGaHEe3L=lw}aSiEdWu&2-C5U0O~MpQ2Hj z-U8)KQrLg0Wd|XyOt&Gc+g8oC4%@84Q6i;~UD^(7ILW`xAcSq1{tW_H3V};4 z3Qpy=%}6HgWDX*C(mPbTgZ`b#A1n`J`|P_^x}DxFYEfhc*9DOGsB|m6m#OKsf?;{9 z-fv{=aPG1$)qI2 zn`vZ(R8tkySy+d9K1lag&7%F>R(e|_M^wtOmO}n{57Qw_vv`gm^%s{UN#wnolnujDm_G>W|Bf7 zg-(AmgR@NtZ2eh!Qb2zWnb$~{NW1qOOTcT2Y7?BIUmW`dIxST86w{i29$%&} zBAXT16@Jl@frJ+a&w-axF1}39sPrZJ3aEbtugKOG^x537N}*?=(nLD0AKlRpFN5+r zz4Uc@PUz|z!k0T|Q|Gq?$bX?pHPS7GG|tpo&U5}*Zofm%3vR!Q0%370n6-F)0oiLg z>VhceaHsY}R>WW2OFytn+z*ke3mBmT0^!HS{?Ov5rHI*)$%ugasY*W+rL!Vtq)mS` zqS@{Gu$O)=8mc?!f0)jjE=p@Ik&KJ_`%4rb1i-IUdQr3{Zqa|IQA0yz#h--?B>gS@ zPLTLt6F=3 z=v*e6s_6w`a%Y2=WmZ&nvqvZtioX0@ykkZ-m~1cDi>knLm|k~oI5N*eLWoQ&$b|xX zCok~ue6B1u&ZPh{SE*bray2(AeBLZMQN#*kfT&{(5Tr1M2FFltdRtjY)3bk;{gPbH zOBtiZ9gNYUs+?A3#)#p@AuY)y3dz(8Dk?cLCoks}DlcP97juU)dKR8D(GN~9{-WS| zImophC>G;}QVazzTZ6^z91{5<+mRYFhrQeg|Kn=LOySHXZqU8F1`dXWOJ?NViPE%& zFB1@$8!ntuI?)geXh|#JJC1+G^n$h4F)g-P4WJMPQn{p=fQtw0)}uk;u*&O2z+G5? ziW_=1kTy(!AJzj}de{a9WHY+*SqJ7` z={VTi)3NK|)*W3PUT#5a$D6oyqH%5zjdO$5ICHx_V;1Z)4A(rT6aasvZ{{r`HnxK7 z^fMLS1{;H{o<8j5hz*F@WkKQmDI*Q%Kf$Mo!EpQ)=HV^lsj9KSz->ROVI zrXAI0!Q?WUosf8t6CR*rl382^sU3q@($L~EC(AoyIjS&2(el|I$a*8oAtqGQs+O~huhBCOFw(^b&bol)F zWsp15Sra3v%&#wXz*!kSi!sV>mhe(I z=_Zxmz&E1>i6=yB*_X4M#ktdNg7_G}MVRGQ7^zX=+mQ}1xtg7JN9E(QI&?4}=tP2#z2<7N%zf9rxzynL~ z!MgNpRvXaU69c*^X2(c?$=h&o~Fvv06*{JdsM!gF$KALcW(}@Q&Alo`@ z3h!H3j^@5rFMp8l6-q!cb?1iS$oZfU+}A2<)&2ZoL34kkSnbf=4>qd%guV7zM1p=amds@nhpkK7 zmRJlb?9zYI&?4ftd8+RvAYdk~CGE?#q!Bv=bv1U(iVppMjz8~#Q+|Qzg4qLZ`D&Rl zZDh_GOr@SyE+h)n%I=lThPD;HsPfbNCEF{kD;(61l99D=ufxyqS5%Vut1xOqGImJe zufdwBLvf7pUVhHb`8`+K+G9>llAJ&Yz^XE0;ErC#SR#-@%O3X5^A_ zt2Kyaba-4~$hvC_#EaAd{YEAr)E*E92q=tkV;;C}>B}0)oT=NEeZjg^LHx}pic<&Fy$hApNZFROZbBJ@g_Jp>@Gn*V zg{XhVs!-LSmQL#^6Bh-iT+7Dn)vRT+0ti(1YyOQu{Vmgyvx3Tuxk5HG!x2a+(#>q7 z#Xji%f&ZxT@A*$m8~z`DDl?{&1=gKHThhqtSBmSpx#kQc$Dh6W76k!dHlhS6V2(R4jj! z#3(W?oQfEJB+-dxZOV?gj++sK_7-?qEM1^V=Sxex)M5X+P{^{c^h3!k*jCU>7pYQ} zgsEf>>V^n1+ji40tL#-AxLjHx42bchIx9Z51CG4Iboc%m0DAfvd3@b}vv4%oR zoYZpZ*dW?+yTcduQlxreAz&6V(Tac9Xw3_`NotT9g&r{F_{!Xb%hDPJqn`CWqDwai z4M@7F4CQ?@C{H~rqxXwD(MFpB4!uljQmH~(TXJJj3MEVHkt7r8!^R;bp!H=&%-OG& zONKIOgLJtng(VD0u9%2LuXKe7h$?9lQ^#cLOo}gOx^+ixt2Izmb6{J`u0VexU0j}8 zIs+?LWLGvQ66Pg0ax4n^G+xW-rwp&fIZ0}lI?y~wn^6o3{jj*VSEQ}tBVn1#sVTQB z(l&Gf(sriC0DKR8#{);Sgb5%k`%l#BfM#W|fN5C8APnl5w%nrNi{BWrDgudYAZLGE zQKTzz^rV(Bst!UI7|8?nB_w}@?_pYX_G?9igK?yo0}({MC^6DiO!bB88kijN>+BCQ8v!rg{Yz$`Hf$tB*WdxSPHMMkJ{&p0(lyXx|^X_VUQBdh9)?_2P1TViiYqy+ z91$zg%3%OjzWyY=X^f7I)2-34bDVCEhECAi^YqS9x@(kD(Bto;VDKfgIo-)s_q)d2mr4O;DTUTgjOe4f51kd6T9 z`xa6_AUP*N{jz%!Z0E!Dqq}JlfPZ2EyGN*EoPHJ^rT;z^0vaI03Z(WcdHTh1suHxs z?;>yWLj~GlkAQ#jSWq|nUE}m()bBZ1`Rh^oO`d+Ar$33kry+En{&JjrML}&gUj3pU zFE58(t|p~g@k3p&-uvoFzpGktUMnQ6RxDA&ibYl_A!{@9au^_fB@6;1XHLORS}C(H zi&J8=@>Kw66&QJD@w>_I1XJuBW3_vn?f~bbTv3_J^W1+E?921QNo!MQiLHISD9?+d zP0BsAK+yB?l009uXXMOteoGX;?5I|RG_v#Bf~l?TPy3zGkT`N>WlZRa=k7Vdbz-66 zIQ979fX!i7Wen@lu-oEcweu$76ZXrc&JWRf!tLRg2JqNG{;`-H@L`KHfgY-Lve@vsPT7B0@716|Z$Z-Z{!W zV;qGHV!`h!S>b)rZpc`9J))^79ey;7@-=zZjys+j=U6maKhDddqZ}XQffIbFYn)R6 z57nRGEG#j`M-Gni4deWVXcr=HoNok4SKTPTIW&LDw*WrceS&Wj^l1|q_VHWu{Pt** ze2;MKxqf%Gt#e^JAKy{jQz4T)LUa6XN40EOCKLskF@9&B?+PnEe(xB+KN|M<@$&ZP{jM;DemSl!tAG2{Iisg ze|}6`>*BENm!G2E!s_XsaUit2`a&pfn!ggt)wG<~NoFFD~p(1PRvhIRZaPhi})MXmEm6+(X? zAw+GxB}7gAxHKo)H7d=m&r6ljuG2KX{&D9ANUe9Q=^7yych#S!-Q!YKbbka8)p==A zm-8`N5_Qz~j7dxLQeaeCHYTma$)Fy}ORKS45sf%}(j`4U=~Aq(!-|ZRRXvQijeGJ^ z%cq3itmW;FI)JsU8k4pNmCazDyH9@=bqwS9q)y8?KhH}MpVTd^>?u+Cs!&l|6KH<* zpikOqr$wK%YZ7(>z%vWLb^+m&cCQ+h_MDo+aXmPW7CD|K$-d&cg$&GVPEi#)hPjGY zx|SBxatca)&Ig?*6~uiQKE)tF7l+ci4JvbZ>vQo}1mB z?m;{w?j6>1xBD9F+2p#YP3U>vfnMicQVHdhK1yDCfacJHG?$*G zdGs93XO$LkB~?nFAfNOoRY`xRs9JiG7CM&Dd5!=ra;zY~qn6HhG|^&58(rYoNlP4q zwA7KN3mvymz;PR0%5d!IoDF1vxVxN zS5wG&fEt`JYIGi>i=Fq;YUc>8aXv_wIKNAmI$xs8oUc$5M((w)<+NMQ6{7X7iz)2t zqz$eebh#@<&91|=(KSq0xZX>fTn|!v{~LlTjaOXR{3kx zDZfD5rHpl>gbmAU@|wOa$t%grx`7}nA|ePPsN0Y)k&2=M zc4?uE@gW0-f>S_2bO;VnKt&W3k$KKdvZh@&*WWKa@7#~`b#Kuyw9kqdj%CMuQ9ESPc-)MbM#7}YUL)ZP_L{+s ziDWcU?e8%n3A4VsFYJpNeLjn2bT>CI3NCJi7EH$DX3S}9p>0NY z#8jZt#!W_KUc?R>k@Ky-w6=+Da+_s0GJldlF|P?(31@{B7bweeajQGYky;y%9NZK$ zoyN7RTWNn&2`?k9Jytjwmk||M(3Z!M&NOYwT}t~sPOp`iw~(CAw<+U2uUl%xEN7WO zyk@N3`M9ikM-q9|HZC|6CJ8jAUAst!H<<<&6(6Zvbpj!BrzUo!>VHN3A3 zvo$EF5-6b1Q~ajXENB~lhUA@|>x6=N0u#cfv&w(qgG`^+5=HoNur`2lvR~b&PjumO|P8X;=d`c+z1YJlY7&H@Dz-Rts$X0IYE9kSIlqGZ7utSx^+2hOEC-eXviWZXQ9;$Va+WlHlU%y|f~ zw(|)o@(5J0o|3MQ2O@+B<@r*H4*65)(r^JTq+<*b06XMGclsEElst5dEfFJ;AQfYh zRt}O0CVKdGh4Tk3-(^-{kukZb*3oM$ZffpGMs;jtk2ZjAsn%mND4R~OS73JDbj^Q4 z40{oS&4<@VUYMInc0xxy?FE@$J_^n)b|gY+Oj;8Pk^)6$w9nbnMms3RSr6q(9wP_) zv01|=P}UbkXoS_1#FCl?>&9cjCHOS!yEJqiGd`83Nj00{X6dHFN84%)I z^*MZ=*Ihw5FxD0YSJHV{j!9v(DT#k7##q~$87Dig!k3EiMO;k|9XhYz8cGVPukGe$ zN5@yNtQgngIs(U-9QZ2c^1uxg$A}#co1|!ZzB|+=CrR6lxT%N&|8??u1*Z?CRaGbp z6;&#}$uQEzu(M6Tdss;dZl=hPN*%ZG@^9f*ig-F9Wi2cjmjWEC+i?dU`nP`xymRwO z$9K3IY`|SvRL^9Jg6|TlJNEL9me$rRD1MJ|>27?VB1%1i)w5-V-5-nCMyMszfCx0@ zxjILKpFhA4*}fl9HYZ~jTYYU@{12DS2OXo0_u+ot_~UfZNaN>@w4Es$Ye>i&qhgqt zxJf9xi6El-@UNPeQ>aXcYVxOUA--x3v13e=7+%#m@}QuMTjN3n--=-{@rNtyYd zYS@LJ(G?*np*HILbUeo)+l8N#+F-;^(8w>i8Q6til8Y^NG7_qa*-n2|4}(k<-HF~R z0v*cP7bxlTWNJ1s6#Rz!NCYesAbm(}4qp%-;B%AF-LyS5Q6@Q|V z&Y2ar$uWn(?UstqXy;5$ZOCC_?L$F@o#dk--?Co{)CGEP^73Kb_^>`G8sAN)M@iNKQLBj>QAcHjIw0!1l6{UYd;|bA+CcC#3IGYysWLa4!KA}C zsEV#c)JpJcF~NX9mrX2WwItXv+s%I2>x#v)y%5xDSB`&bU!9COR@6LwbI|OQ&5mf& zL^GGZnOXEOLshxOs;Y;ikp^M(l-^>J(o0NIdbt5`(fTq>p%?cG;%aHXhv=-@!20#xf*q)++kt8IJ5cG{ zff?Sy9hfzQIroA8N>Git>3xOUNhe8nUspSV`GL0DK}<_w!3gRCwOvD~m+Zn6jxTMd ze<_?egr$S1OySh6XsS!0Wh)wJPX+xd11YQ=Mq7X2tU;U;Xx|ObfO}%y{pchi>ryaM z2zAy50_$ltt(ew6h#CF@+U74D#H@hdQ=dX_=OChf#oerWnu~l=x>~Mog;wwL7Nl^I zw=e}~8;XZ%co+bp)3O{Mryc`*3ryyIC*S%Zu;8Y_D3bFAn%8 zNTYv?y_%Q4zR-DvE(Q*~>ec+JSA76q7D#_wFR&HI@z>V`9-)x zr*ME%7~<$Ykd?U8uZ~EqUe&AlGDqP{uUvnavy#q%0y2VKf%UxO(ZC2ECkuzLyY#6c zJTru6Q`qZQQ+VF1`jr8+bHIwcJg}=iko8FEDt(bW8pbOr>?{5KLASE=YFFv&(&IM| zP6@wK(5#jhxh@Pe7u_QKd{x@L_-H zM=1`rX8`BDds3pf+|$)DBqpXr zDP>JcOxubC$Dy60;8(mfG^6yXE(+N*UWMW?A~?H-#B7S@URtmlHC|7dnB!Lqc0vjG zi`-tNgQ8uO67%USUuhq}WcpRIpksgNqrx{V>QkbTfi6_2l0TUk5SXdbPt}D^kwXm^fm04^i66Xn0`pLmnhX(P0|TezLiFcQ{E0~v*cmmAR2|PET zl7Ls>OakCexUmie^yDw3ccuqd5(wV_6?YM+egsV{M=^n{F2a}~qL}DfhDok9nC!X$ zC9WV!U15~DF2xl0YLvS#K!rPqsqS7(b8m##ZA(3F3H0v&0Z>Z^2u=x*A;aYh0093L zlc6LWl7U5kwXW8By76umJat{FC`H8^K@=20LGUu&PPftQfn-}R#6E~`;e`lZ_y9hX zI9nAF8OY51`Q}eZ-alU70BmAj;IZGoXxzI^8QfCba(CUJ?bh5NiBhFyrjpo;k`}RU zNRzb0n;mJrphLl}?MBw!ZA)#b=BA++$<$N1M{{R?rygu>Giw?@^X;zIEZC0p>fBNs zs+h>AIApa)#`0OLH#W958eWTf?n4PepnREhO+ZIVlfZIfLO(RJrOCfDGEK?&C$Y_> z)=S^{Fuzz4!va$`vL}5lXkrYW%bH|gUK?As5mHLYz!l)Iw)g2uVw^> z5BZf)=cdR%GlXhRaaGM3&Vs|i1g~@4Eug>wRMxJqUof@)jOp4lW}kooS{PUqJ^@fm z2M9!-I|6Hyt%6X033waFb$&wt1h|3@lA>hju-BAmfjCGV5h+8q93HYw5uy}QM_|d8 zm%xHt3D{+J7m{e#O4`V2j<#tMr-_uta^2Q+TPKZL38bS$>J__n)1+zBq-Wa3ZrY|- zn%;+_{BHn|APLH8qfZ}ZXXee!oA>_rzc+m4JDRw#Hi1R(`_BX|7?J@w}DMF>dQQU2}9yj%!XlJ+7xuIfcB_n#gK7M~}5mjK%ZX zMBLy#M!UMUrMK^dti7wUK3mA;FyM@9@onhp=9ppXx^0+a7(K1q4$i{(u8tiYyW$!B zbn6oV5`vU}5vyRQ_4|#SE@+))k9CgOS|+D=p0Txw3El1-FdbLR<^1FowCbdGTInq0Mc>(;G;#%f-$?9kmw=}g1wDm#OQM0@K7K=BR+dhUV z`*uu!cl&ah;|OXFw^!{Y2X_bQcDjSDpb83BAM2-9I7B~dIIbfN_E3;EQ=3AY=q^Dm zQncV2xz0W-mjm8_VaHElK@EC-!ktWFouH=5iBgisaA1U@3bj)VqB)H4VK|{N+2-(JHfiJCYX>+!y8B2Fm z({k0cWxASSs+u_ov64=P?sTYo&rYDDXH?fxvxb>b^|M;q%}uJ?X5}V30@O1vluQ19 z_ER5Rk+tl+2Akd;UJQt1HEy_ADoA_jeuet!0YO{7M+Et4K+vY}8zNGM)1X58C@IM6 z7?0@^Gy_2zq62KcgNW)S%~!UX1LIg~{{L&cVH^pxv&RS87h5Dqhv+b?!UT{rMg#O# z#tHOouVIW{%W|QnHnAUyjkuZ(R@l6M%}>V^I?kADpKlXW%QH2&OfWTY{0N_PLeRc9 zMi3vb*?iSmEU7hC;l7%nHAo*ucCtc$edXLFXlD(Sys;Aj`;iBG;@fw21qcpYFGU6DtNH*Xmdk{4fK0AKi6FGJC#f0@j_)KD&L`tcGuKP_k_u+uZ@Sh<3$bA}GmGrYql`YBOYe}rLwZKP!xrdrur z0ib3zAR%*So7rZjP$|`v$!nA9xOQ4sM|Is)T`iB$29KOE-0_Y!v(GZKhMia4am~e# zu5PJbJTk5!5Jn35E$W1AVWB&zA{r<8tP)wo%Vg0}o(EZ}Ts5eMgW$E9nUDxFyhPP( zs8$YB7)%~lUan?sD~~9DckP11Ea%9&uY)hvUwxUwb}pf|IT$VPqb9AAiAuw>G+8N8 z6Ovlm%$~Fhhg1!#<%uJPW4P+L>rOa{&N2gbFd3Fh-nnA8lL@IrHd6K33HFYag|7^p zP;EZ&_CU5|tx*P)T5w<-hNeoB7VAth{E$^zh&!tb9x@TA^<6WYl=|`BSI?aM#~0G0T^KK!+74^cJ#Nj`srvw<<6E zzM$Kx-86sp4;1hc2-blI9c0tmCMY}Qn=5b(4Vqv z{|sKKb)cXA9B?~>#9fzsZ29S1Tr62*LHahw(?8R{AQudS8<=zg^lz2q zD}8im+_uhWqYUr=fMT#sIo${8zZfe2N&j7)tPfNL^8Z2}6)v8;x|<$fDzHr5?L0g@ zAOmYTwm%3~HQmw+c~!W5LEVM>2|z;BF)jd7U&jQ0%D8~=0et;cR2&d~)H=6#Rr*B( zV9$6xY#V}Z4=>PWem5wViJ&4Bv3xeU=0-BSSJ zgLq4Ssb;S7t=xC1%@8T#c5w$=0*}ik;4@vwq3Am7=yuN-b_|MEpaRpI;Cvp9%i(}% zs}RtlP5ojEwsLfL7&QhevV-Nsj0eq<1@D5yAlgMl5n&O9X|Vqp%RY4oNyRFF7sWtO z#6?E~bm~N|z&YikXC=I0E*8Z$v7PtWfjy*uGFqlA5fnR1Q=q1`;U!~U>|&X_;mk34 zhKqYAO9h_TjRFso_sn|qdUDA33j5IN=@U7M#9uTvV5J{l0zdjRWGKB8J3Uz+|(f(HYHAjk#NQ1jL9! zuha9;i4YYO5J$mewtTo9vVtPTxqXvBInY?m4YD)~h~q$Ax!_EwZpqbZI3OP3;=4xa zULDboazx{;=E*zl0g)CIxiwU0S+taYYlIHHMHZAe8xkWHvSjw;0&`NOTN%Xcr-ivm9Bz1h6 zny%66)ZjF=M6S}>=v4~EuG0F;50<8uJ7@5d0V_2pQVkF7Vq{{!dIm33#3Ft_}G2)yjM)! zd^I{4d6C{M=mM$U&yqhi=!uOq^+sms!NF^^FO?LLY1%(UAAuAQ;Js8WHnK=;BI0?G zj@F^p*@W>;sZ=u3l$xf8pzH;I3P)vOmA?n#aMPBi8 z^%0|sj#w@`5rIzhQ!tSbr|=trz3XA)gH(s7qlZqzSnr3GpT_7Etp6(f@@<&&Cgd6@ zO_{P$>oL!s`$Ftx@?LJr&QNaX8kwntH#$vkYg|R22_$?WFI((Ps;mBgX=;jxe4dv2 zB0W9@Ytx5X>gz7C*}oPKd5d(eNI!)2=dpg8p7eD2T72>A&r(Oc#kZr8Zl0T=_oWh8 z{A0N9vXFPx)*^lID7MGYhmW53!69FY@je$)Lq+<@3s5PVD$*r5``M(QjgmT^@OmO6 z-sp%gHc}rSY5JLvw`8Gz=TflG&)tw(+<*mIXdUgu%{CxCbK8#JowN2@0SO=M^#R!H z6?`{v`CUe5FJ?SwyCTwGaWuckZrbd*cS97n*}$HSL^o`QV`u2{Me=!GI9~_dUxVbO z7s|jzu~fEkS2;SKy+&74sr^v1Sfo!g?rt#d&g0|P1t9ae)DZ7~4AaMp^qVvE1qqxl zUZ9nHsoy&~b@Pi;bSxIXMqg&hucX*B)AZGlZ<_wNNMB2M8@&ts^)Xsm@z<+UH@_KA zm7Vk&{!iU}$6y2}y>=s3q`$h%KQ|De3gWd_T4=Rw*ODsRR%(-Nn7U+pH|>$_UfL(y zBps0LFddieaXJBi>k?^{mF+lLvMtd2WXr!S_d)uoY)gJo;16IEvvuH(Z&YlEF~4Mt zgVERw{mtdnP$YGQLX5QNiKcH()87Fhz);ga;3ro8{wMqZN=5qDvS|E7)4xm6|Cyb+ zfwKtysRw&ATYU!+B2TOXK$*G3l~^PtLwPV-6rR$Fz;;o8z>*(s7WJjAq^m9+Eguv+ z(JTTuX-2FlipGi#>xbCfU@qZdcZ!5pBz#h2ErNo*n((t*0g$hCrXHnm|i`@X6!d0j(RK8a`Hw2l5S1eVl@8los!kPhF(7@ijcCcL%PBB!<=~MKK)m z$2=`T0Eu_#R=NXIH=h{{`4iqLa>{Mu8oi!s7Kf(A;TzGAKje#F5l5QETXFpg?7)M8 zD4Qw*a~?Z-8SK4tke9LDVAp2xFf0l}5RJ{^1U}<`@`|I)B2%(-WLk{fsNVS{3NYNy zg}nR)ue=tyK_MEWlVVgDvV8=;&C^-g=a&0t>2a|ceQr0P|8{y#_POQ$^YjVX=a&1Q zq|36;E%!Nkxz8>4U!u>;KDXTeI(~qWgw0KJD zS&EAzCZPW_^!Tj4^T{T!k9N#2;RO z7iBy{i;&QUo$Tz+nfE#GOwP=ozrTJ1Sc55We021t`blp}YoGj;%5y1uf!uNG{2Uc(N@c!)lX%wI3y3q;Kp>H=-52V;i3A7>>%(TwkwPYfo4kR?qm| z#C16kwWU$vA^EoB6NQd%bM%nHh`l&oU46V-HClA2e;$PpNH>BcwCIK7lE8cr+NK@K zmP_V`PLn)Sf8Dbz3|Fu5lWrRhrFHeWUO$ciK|;QNMYU4B-{xxq=2gh0MJ_>CzIO%I2C`dQ0}U%zLwzhCD9eXj z_~Pck%ya+e`Xnf;1j}62O+JMJ**YJ(mx~=JE+{p9z;saHl6M^@O>uaJ(zL z_pbbfg95AEkMI{PQrP_-wu~WeK)#DjC~RTz1jWl>>J%&u_A8uVq z=X}6rk(Ww~N);x^iv)>V)F>R%WhPu8Gn7lW${nB1g?2dLWg6t73{<@%o=iq^d`ek= z8~x4CS6UNrnFvN?(WJ^CT4hqAYqXBuA|4G-hEb5QoM5x6GZPijL*Z>uQZW67A|R9w^IzUkPhic=6Im%(-`|RxlHTyT__; zTIpHtPB288^%``Bpy}I=`(B1HzbS#S^Q*EAx4u+7Zxc(*~GMtIG z28o~(XLX!G7eiM=)yPxBISPB#v`zndJ?z~G&ZAdH4=ynDG-o(tf4fzG(U*c(G`yvv zwG>!)eOpH#E;0lxhZh*mH;kJ6>$aB=Q(^iUP8ycui3r|Rf%`B(*o|DLxmTuAG{kib zs-%KzVslaWt>u!4${j*dfuna=Gjl-rPoCZgwb{OKc%p z!#g#+w~fKv?Jbb;@C$svFq?dVj~E_foIb8G|l?27Kf`O2bZM(f5T<@B@DC9-<3~{+ae-(qxiFGMiqxGcB za}=}LbSblhT0Q6Rm4>3=gi)o*G!B_6$tq*ItV%e0&U6FU!uj0%!h9}SX6NEZ9}oim zg4WPW?76Hk0#QwuQj$)~3QJw+v|eX=>YZgbHMJs34ZXEzFL($9Pw6>LDO8nGd&N^$ zGQH4GKq$+GsmsL%f7cNR?6y=YGgJHdofV|o;~RKj0^!|%nF=P~ai{JLHLCol`|FQ7a$D7+;JWrBjTd0T_>aUBJK||PoA}xwjpy>>3&$74 zTY?_p_n~D4+YZ_`VA~9v3?#|5p?&G^NcjljeZ~g^f18y^%J9)Cd^>|=NijQzL5oimxJIZx~e9?Ss^Ty`ZaDtBpPPoAsJW(yH z$N4T<;S2#yPeoF?lu&qNOqVhlu1EGea_2aYXH89ap^{o07Yne+0~cxtd5_*)sP&)@HC}ize=e%9#0xj( zimzo}crZ_VtzhsLf5+j%DhiU1%Z6##t_Qui5BGbp8h+wH(WFEnJTC%R=pic)GR)Vx zl-NNqUE8ZG40R2ST?P81rl{~1FV|}^b%`m4HAwH{ZlxUX8MfWq z`a@huNpbS?_U{vkW?z`BxS?f6-*EN3sQFIU*&Zs>w{W~F`=`NPBs+to_fDoY%J&mT za^I?KQr#C`SXEQy8+4CjK)^94+%U%c;)ha|&Us}Dr~V$FH5cA?_7^NiWdTjcKHGad z`(I^x!&*O@E(D3_I~YMN_e-TkVO-5I{WP&CB=atLdm8yB{z!y)5Z#}y(N%%2ER_xg z%>LL&rt(sAn)YO0P%RHU_uED75S80CWtD+%&xu7-w}pRI5;ZHbK<9KYH7;`S)ek+G zpDa%oN5mO>3}rcuKV)=g>f?*8y+^~ny)tY#>h#^uo@~ZRbpFILBs$)PO1o{c;%l~I zD?NNElg^LuV{Zx-9EL76vUNwjEx-8^=V(QVq}1|CC-_?mHD&W!5ytLBA2u@@RT9Q> z5xk@ED^7TkWyJ|qISZjmlqVaglnH+04zh=RgkQyZ`o00KZ%;wtkt+1Zfbr_+Bl+UQ z0k+>A>rBUsjYV?}r=%Kr181gVKe?JMLa*1$T0$f4*>!PStFJpzf09sNh0^E@q; zxxBLnmYcr*boFYk|E>f~ixql>m#wNYJy~kwCc7>jYe_nUIyf?5S*Ly>@l+U_dq?VP z9RTLWnacWtvIs>7Y57AP&h{Dcbq#_pqu_*s8@Sw2vPDmh`9nW)Pd4FdLBXGeCH0r* zFcfC+Xusg2SFr7wr{+65-Pp{>OBG?9PM)97r{om9;MAMK?!5~I9v*`C;{AswcJ~!b zGT&%x5>A3vjHSp+G2O>AlDq$n^NP`!N%izEgie}0Udr!{OnOP@N8JywJ)3jckiV=r zAIy?fcNrQr^K~^JY0~WKh|kScq5ZOLA7*+POIaHo0#m2ExXhm0?<#VZ=$Ug#G@i2T z)Sf$}NrGLS=Y-yWqM|SvSP9zLSvaRuaxJ6M&$r&#-Tk{?9%kpEhxL4t#N{sonv4!M=G<)_BS$lkk!b3IDCH4K>9w(_}s{ z8ddu``20Oov^etWaqOGP*3C+sL}i0^-$R1}G540BRvPNA#!TB~s=UQ$8_)3M}ih#tlN-sc}LQC%Kx^SHHG z|8oV-?{)JR67dyM@{98&y>+wY#<#*y8n4xgB#h;ECrY%I_r-W#892Q7vGc~G{wtig zjXw0drcNP>4zj4bxY4C_vy>^`Y-Rr+Pr?*V^|$zY>>6t8$b4oRv>_=^_+Vk8d`!g5 zY-$%J#Z7eEH0yWLg(Gf;{gb5Gb4L^Z7#|7o&1!BYXOnDC=f&37(Dy_;YU*DaSI4$} zdnSXw0$cR}*#^&pz!P<9Ix+j3>9NqNw+#|^GzK6om9@_?wN)~y&PeA# zqf%KAM72YYcGd{WbE}+khGVVUlmoyn1f_9yf9snxn?~zmZbu&W%K*sAe0FTwP@avv=0APWMrl3v(3BTr=2K50s zLV_s_LTqi823pIlPHNz4?6;r@o;Wlt*%TD~7 zZYWe1GU3lu7)li?gFKog9Px9NMH@s!5^W9E7Fyxu!au9JKafKe0!;GyA83M?YsLHq z)z=Euw;cgF_(706e*KjPNI?hz9AQC#Huz5w3Gcmj3JL)95zq$?oT^+z#KT8+pj0oQ zFEW+mQ5e!lGk{09zHtcvUm>Dll3|e5YK&joh=O{ii+}=hV5p_l2*0-J0;NR$Zm08L zXhndBQ?4$_ 7.7) - nanopb (< 2.30911.0, >= 2.30908.0) @@ -47,9 +42,9 @@ PODS: - GoogleUtilitiesComponents (1.1.0): - GoogleUtilities/Logger - GTMSessionFetcher/Core (3.5.0) - - hermes-engine (0.81.1): - - hermes-engine/Pre-built (= 0.81.1) - - hermes-engine/Pre-built (0.81.1) + - hermes-engine (250829098.0.14): + - hermes-engine/Pre-built (= 250829098.0.14) + - hermes-engine/Pre-built (250829098.0.14) - MLImage (1.0.0-beta5) - MLKitBarcodeScanning (5.0.0): - MLKitCommon (~> 11.0) @@ -95,20 +90,14 @@ PODS: - nanopb/encode (= 2.30910.0) - nanopb/decode (2.30910.0) - nanopb/encode (2.30910.0) - - NitroImage (0.15.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - NitroImage (0.15.1): - hermes-engine - NitroModules - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-callinvoker - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -123,21 +112,15 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - NitroModules (0.36.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-callinvoker - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -152,59 +135,37 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - PromisesObjC (2.4.0) - - RCT-Folly (2024.11.18.00): - - boost - - DoubleConversion - - fast_float (= 8.0.0) - - fmt (= 11.0.2) - - glog - - RCT-Folly/Default (= 2024.11.18.00) - - RCT-Folly/Default (2024.11.18.00): - - boost - - DoubleConversion - - fast_float (= 8.0.0) - - fmt (= 11.0.2) - - glog - - RCT-Folly/Fabric (2024.11.18.00): - - boost - - DoubleConversion - - fast_float (= 8.0.0) - - fmt (= 11.0.2) - - glog - - RCTDeprecation (0.81.1) - - RCTRequired (0.81.1) - - RCTTypeSafety (0.81.1): - - FBLazyVector (= 0.81.1) - - RCTRequired (= 0.81.1) - - React-Core (= 0.81.1) - - React (0.81.1): - - React-Core (= 0.81.1) - - React-Core/DevSupport (= 0.81.1) - - React-Core/RCTWebSocket (= 0.81.1) - - React-RCTActionSheet (= 0.81.1) - - React-RCTAnimation (= 0.81.1) - - React-RCTBlob (= 0.81.1) - - React-RCTImage (= 0.81.1) - - React-RCTLinking (= 0.81.1) - - React-RCTNetwork (= 0.81.1) - - React-RCTSettings (= 0.81.1) - - React-RCTText (= 0.81.1) - - React-RCTVibration (= 0.81.1) - - React-callinvoker (0.81.1) - - React-Core (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - RCTDeprecation (0.86.0) + - RCTRequired (0.86.0) + - RCTSwiftUI (0.86.0) + - RCTSwiftUIWrapper (0.86.0): + - RCTSwiftUI + - RCTTypeSafety (0.86.0): + - FBLazyVector (= 0.86.0) + - RCTRequired (= 0.86.0) + - React-Core (= 0.86.0) + - React (0.86.0): + - React-Core (= 0.86.0) + - React-Core/DevSupport (= 0.86.0) + - React-Core/RCTWebSocket (= 0.86.0) + - React-RCTActionSheet (= 0.86.0) + - React-RCTAnimation (= 0.86.0) + - React-RCTBlob (= 0.86.0) + - React-RCTImage (= 0.86.0) + - React-RCTLinking (= 0.86.0) + - React-RCTNetwork (= 0.86.0) + - React-RCTSettings (= 0.86.0) + - React-RCTText (= 0.86.0) + - React-RCTVibration (= 0.86.0) + - React-callinvoker (0.86.0) + - React-Core (0.86.0): + - hermes-engine - RCTDeprecation - - React-Core/Default (= 0.81.1) + - React-Core-prebuilt + - React-Core/Default (= 0.86.0) - React-cxxreact - React-featureflags - React-hermes @@ -217,18 +178,14 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/CoreModulesHeaders (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core-prebuilt (0.86.0): + - ReactNativeDependencies + - React-Core/CoreModulesHeaders (0.86.0): + - hermes-engine - RCTDeprecation + - React-Core-prebuilt - React-Core/Default - React-cxxreact - React-featureflags @@ -242,18 +199,12 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/Default (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core/Default (0.86.0): + - hermes-engine - RCTDeprecation + - React-Core-prebuilt - React-cxxreact - React-featureflags - React-hermes @@ -266,20 +217,14 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/DevSupport (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core/DevSupport (0.86.0): + - hermes-engine - RCTDeprecation - - React-Core/Default (= 0.81.1) - - React-Core/RCTWebSocket (= 0.81.1) + - React-Core-prebuilt + - React-Core/Default (= 0.86.0) + - React-Core/RCTWebSocket (= 0.86.0) - React-cxxreact - React-featureflags - React-hermes @@ -292,18 +237,12 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/RCTActionSheetHeaders (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core/RCTActionSheetHeaders (0.86.0): + - hermes-engine - RCTDeprecation + - React-Core-prebuilt - React-Core/Default - React-cxxreact - React-featureflags @@ -317,18 +256,12 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/RCTAnimationHeaders (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core/RCTAnimationHeaders (0.86.0): + - hermes-engine - RCTDeprecation + - React-Core-prebuilt - React-Core/Default - React-cxxreact - React-featureflags @@ -342,18 +275,12 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/RCTBlobHeaders (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core/RCTBlobHeaders (0.86.0): + - hermes-engine - RCTDeprecation + - React-Core-prebuilt - React-Core/Default - React-cxxreact - React-featureflags @@ -367,18 +294,12 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/RCTImageHeaders (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core/RCTImageHeaders (0.86.0): + - hermes-engine - RCTDeprecation + - React-Core-prebuilt - React-Core/Default - React-cxxreact - React-featureflags @@ -392,18 +313,12 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/RCTLinkingHeaders (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core/RCTLinkingHeaders (0.86.0): + - hermes-engine - RCTDeprecation + - React-Core-prebuilt - React-Core/Default - React-cxxreact - React-featureflags @@ -417,18 +332,12 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/RCTNetworkHeaders (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core/RCTNetworkHeaders (0.86.0): + - hermes-engine - RCTDeprecation + - React-Core-prebuilt - React-Core/Default - React-cxxreact - React-featureflags @@ -442,18 +351,12 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/RCTSettingsHeaders (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core/RCTSettingsHeaders (0.86.0): + - hermes-engine - RCTDeprecation + - React-Core-prebuilt - React-Core/Default - React-cxxreact - React-featureflags @@ -467,18 +370,12 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/RCTTextHeaders (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core/RCTTextHeaders (0.86.0): + - hermes-engine - RCTDeprecation + - React-Core-prebuilt - React-Core/Default - React-cxxreact - React-featureflags @@ -492,18 +389,12 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/RCTVibrationHeaders (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core/RCTVibrationHeaders (0.86.0): + - hermes-engine - RCTDeprecation + - React-Core-prebuilt - React-Core/Default - React-cxxreact - React-featureflags @@ -517,19 +408,13 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Core/RCTWebSocket (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core/RCTWebSocket (0.86.0): + - hermes-engine - RCTDeprecation - - React-Core/Default (= 0.81.1) + - React-Core-prebuilt + - React-Core/Default (= 0.86.0) - React-cxxreact - React-featureflags - React-hermes @@ -542,76 +427,65 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-CoreModules (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - RCTTypeSafety (= 0.81.1) - - React-Core/CoreModulesHeaders (= 0.81.1) - - React-jsi (= 0.81.1) + - React-CoreModules (0.86.0): + - RCTTypeSafety (= 0.86.0) + - React-Core-prebuilt + - React-Core/CoreModulesHeaders (= 0.86.0) + - React-debug + - React-featureflags + - React-jsi (= 0.86.0) - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - React-NativeModulesApple - React-RCTBlob - React-RCTFBReactNativeSpec - - React-RCTImage (= 0.81.1) + - React-RCTImage (= 0.86.0) - React-runtimeexecutor + - React-utils - ReactCommon - - SocketRocket - - React-cxxreact (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-callinvoker (= 0.81.1) - - React-debug (= 0.81.1) - - React-jsi (= 0.81.1) + - ReactNativeDependencies + - React-cxxreact (0.86.0): + - hermes-engine + - React-callinvoker (= 0.86.0) + - React-Core-prebuilt + - React-debug (= 0.86.0) + - React-jsi (= 0.86.0) - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - - React-logger (= 0.81.1) - - React-perflogger (= 0.81.1) + - React-logger (= 0.86.0) + - React-perflogger (= 0.86.0) - React-runtimeexecutor - - React-timing (= 0.81.1) - - SocketRocket - - React-debug (0.81.1) - - React-defaultsnativemodule (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-timing (= 0.86.0) + - React-utils + - ReactNativeDependencies + - React-debug (0.86.0): + - React-debug/redbox (= 0.86.0) + - React-debug/redbox (0.86.0) + - React-defaultsnativemodule (0.86.0): + - hermes-engine + - React-Core-prebuilt - React-domnativemodule + - React-Fabric/animated + - React-featureflags - React-featureflagsnativemodule - React-idlecallbacksnativemodule + - React-intersectionobservernativemodule - React-jsi - React-jsiexecutor - React-microtasksnativemodule + - React-mutationobservernativemodule - React-RCTFBReactNativeSpec - - SocketRocket - - React-domnativemodule (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-viewtransitionnativemodule + - React-webperformancenativemodule + - ReactNativeDependencies + - Yoga + - React-domnativemodule (0.86.0): + - hermes-engine + - React-Core-prebuilt - React-Fabric - React-Fabric/bridging - React-FabricComponents @@ -621,39 +495,35 @@ PODS: - React-RCTFBReactNativeSpec - React-runtimeexecutor - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Fabric (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-Fabric (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - - React-Fabric/animations (= 0.81.1) - - React-Fabric/attributedstring (= 0.81.1) - - React-Fabric/bridging (= 0.81.1) - - React-Fabric/componentregistry (= 0.81.1) - - React-Fabric/componentregistrynative (= 0.81.1) - - React-Fabric/components (= 0.81.1) - - React-Fabric/consistency (= 0.81.1) - - React-Fabric/core (= 0.81.1) - - React-Fabric/dom (= 0.81.1) - - React-Fabric/imagemanager (= 0.81.1) - - React-Fabric/leakchecker (= 0.81.1) - - React-Fabric/mounting (= 0.81.1) - - React-Fabric/observers (= 0.81.1) - - React-Fabric/scheduler (= 0.81.1) - - React-Fabric/telemetry (= 0.81.1) - - React-Fabric/templateprocessor (= 0.81.1) - - React-Fabric/uimanager (= 0.81.1) + - React-Fabric/animated (= 0.86.0) + - React-Fabric/animationbackend (= 0.86.0) + - React-Fabric/animations (= 0.86.0) + - React-Fabric/attributedstring (= 0.86.0) + - React-Fabric/bridging (= 0.86.0) + - React-Fabric/componentregistry (= 0.86.0) + - React-Fabric/componentregistrynative (= 0.86.0) + - React-Fabric/components (= 0.86.0) + - React-Fabric/consistency (= 0.86.0) + - React-Fabric/core (= 0.86.0) + - React-Fabric/dom (= 0.86.0) + - React-Fabric/imagemanager (= 0.86.0) + - React-Fabric/leakchecker (= 0.86.0) + - React-Fabric/mounting (= 0.86.0) + - React-Fabric/observers (= 0.86.0) + - React-Fabric/scheduler (= 0.86.0) + - React-Fabric/telemetry (= 0.86.0) + - React-Fabric/uimanager (= 0.86.0) + - React-Fabric/viewtransition (= 0.86.0) - React-featureflags - React-graphics - React-jsi @@ -664,21 +534,16 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/animations (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/animated (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug + - React-Fabric/animationbackend - React-featureflags - React-graphics - React-jsi @@ -689,19 +554,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/attributedstring (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/animationbackend (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -714,19 +573,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/bridging (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/animations (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -739,19 +592,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/componentregistry (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/attributedstring (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -764,19 +611,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/componentregistrynative (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/bridging (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -789,25 +630,15 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/components (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/componentregistry (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - - React-Fabric/components/legacyviewmanagerinterop (= 0.81.1) - - React-Fabric/components/root (= 0.81.1) - - React-Fabric/components/scrollview (= 0.81.1) - - React-Fabric/components/view (= 0.81.1) - React-featureflags - React-graphics - React-jsi @@ -818,19 +649,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/components/legacyviewmanagerinterop (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/componentregistrynative (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -843,21 +668,19 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/components/root (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/components (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug + - React-Fabric/components/legacyviewmanagerinterop (= 0.86.0) + - React-Fabric/components/root (= 0.86.0) + - React-Fabric/components/scrollview (= 0.86.0) + - React-Fabric/components/view (= 0.86.0) - React-featureflags - React-graphics - React-jsi @@ -868,19 +691,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/components/scrollview (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/components/legacyviewmanagerinterop (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -893,19 +710,51 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/components/view (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/components/root (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimeexecutor + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - React-Fabric/components/scrollview (0.86.0): + - hermes-engine + - RCTRequired + - RCTTypeSafety + - React-Core + - React-Core-prebuilt + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimeexecutor + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - React-Fabric/components/view (0.86.0): + - hermes-engine + - RCTRequired + - RCTTypeSafety + - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -919,20 +768,52 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-Fabric/consistency (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-Fabric/consistency (0.86.0): + - hermes-engine + - RCTRequired + - RCTTypeSafety + - React-Core + - React-Core-prebuilt + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimeexecutor + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - React-Fabric/core (0.86.0): + - hermes-engine + - RCTRequired + - RCTTypeSafety + - React-Core + - React-Core-prebuilt + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimeexecutor + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - React-Fabric/dom (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -945,19 +826,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/core (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/imagemanager (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -970,19 +845,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/dom (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/leakchecker (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -995,46 +864,38 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/imagemanager (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/mounting (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags - React-graphics - React-jsi - React-jsiexecutor + - React-jsinspectortracing - React-logger - React-rendererdebug - React-runtimeexecutor - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/leakchecker (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/observers (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug + - React-Fabric/observers/events (= 0.86.0) + - React-Fabric/observers/intersection (= 0.86.0) + - React-Fabric/observers/mutation (= 0.86.0) - React-featureflags - React-graphics - React-jsi @@ -1045,19 +906,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/mounting (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/observers/events (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -1070,22 +925,15 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/observers (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/observers/intersection (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - - React-Fabric/observers/events (= 0.81.1) - React-featureflags - React-graphics - React-jsi @@ -1096,19 +944,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/observers/events (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/observers/mutation (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -1121,46 +963,37 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/scheduler (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/scheduler (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug + - React-Fabric/animationbackend - React-Fabric/observers/events + - React-Fabric/viewtransition - React-featureflags - React-graphics - React-jsi - React-jsiexecutor - React-logger + - React-performancecdpmetrics - React-performancetimeline - React-rendererdebug - React-runtimeexecutor - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/telemetry (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/telemetry (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -1173,47 +1006,36 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/templateprocessor (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/uimanager (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug + - React-Fabric/uimanager/consistency (= 0.86.0) - React-featureflags - React-graphics - React-jsi - React-jsiexecutor - React-logger + - React-rendererconsistency - React-rendererdebug - React-runtimeexecutor - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/uimanager (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/uimanager/consistency (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - - React-Fabric/uimanager/consistency (= 0.81.1) - React-featureflags - React-graphics - React-jsi @@ -1225,19 +1047,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/uimanager/consistency (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-Fabric/viewtransition (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -1245,30 +1061,23 @@ PODS: - React-jsi - React-jsiexecutor - React-logger - - React-rendererconsistency - React-rendererdebug - React-runtimeexecutor - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket - - React-FabricComponents (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-FabricComponents (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric - - React-FabricComponents/components (= 0.81.1) - - React-FabricComponents/textlayoutmanager (= 0.81.1) + - React-FabricComponents/components (= 0.86.0) + - React-FabricComponents/textlayoutmanager (= 0.86.0) - React-featureflags - React-graphics - React-jsi @@ -1279,34 +1088,28 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/components (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/components (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric - - React-FabricComponents/components/inputaccessory (= 0.81.1) - - React-FabricComponents/components/iostextinput (= 0.81.1) - - React-FabricComponents/components/modal (= 0.81.1) - - React-FabricComponents/components/rncore (= 0.81.1) - - React-FabricComponents/components/safeareaview (= 0.81.1) - - React-FabricComponents/components/scrollview (= 0.81.1) - - React-FabricComponents/components/switch (= 0.81.1) - - React-FabricComponents/components/text (= 0.81.1) - - React-FabricComponents/components/textinput (= 0.81.1) - - React-FabricComponents/components/unimplementedview (= 0.81.1) - - React-FabricComponents/components/virtualview (= 0.81.1) + - React-FabricComponents/components/inputaccessory (= 0.86.0) + - React-FabricComponents/components/iostextinput (= 0.86.0) + - React-FabricComponents/components/modal (= 0.86.0) + - React-FabricComponents/components/rncore (= 0.86.0) + - React-FabricComponents/components/safeareaview (= 0.86.0) + - React-FabricComponents/components/scrollview (= 0.86.0) + - React-FabricComponents/components/switch (= 0.86.0) + - React-FabricComponents/components/text (= 0.86.0) + - React-FabricComponents/components/textinput (= 0.86.0) + - React-FabricComponents/components/unimplementedview (= 0.86.0) + - React-FabricComponents/components/virtualview (= 0.86.0) - React-featureflags - React-graphics - React-jsi @@ -1317,20 +1120,14 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/components/inputaccessory (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/components/inputaccessory (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric @@ -1344,20 +1141,14 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/components/iostextinput (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/components/iostextinput (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric @@ -1371,20 +1162,14 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/components/modal (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/components/modal (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric @@ -1398,20 +1183,14 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/components/rncore (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/components/rncore (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric @@ -1425,20 +1204,14 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/components/safeareaview (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/components/safeareaview (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric @@ -1452,20 +1225,14 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/components/scrollview (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/components/scrollview (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric @@ -1479,20 +1246,14 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/components/switch (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/components/switch (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric @@ -1506,20 +1267,14 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/components/text (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/components/text (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric @@ -1533,20 +1288,14 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/components/textinput (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/components/textinput (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric @@ -1560,20 +1309,14 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/components/unimplementedview (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/components/unimplementedview (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric @@ -1587,20 +1330,14 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/components/virtualview (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/components/virtualview (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric @@ -1614,20 +1351,14 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricComponents/textlayoutmanager (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-FabricComponents/textlayoutmanager (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-cxxreact - React-debug - React-Fabric @@ -1641,272 +1372,201 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-FabricImage (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired (= 0.81.1) - - RCTTypeSafety (= 0.81.1) + - React-FabricImage (0.86.0): + - hermes-engine + - RCTRequired (= 0.86.0) + - RCTTypeSafety (= 0.86.0) + - React-Core-prebuilt - React-Fabric - React-featureflags - React-graphics - React-ImageManager - React-jsi - - React-jsiexecutor (= 0.81.1) + - React-jsiexecutor (= 0.86.0) - React-logger - React-rendererdebug - React-utils - ReactCommon - - SocketRocket + - ReactNativeDependencies - Yoga - - React-featureflags (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - SocketRocket - - React-featureflagsnativemodule (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-featureflags (0.86.0): + - React-Core-prebuilt + - ReactNativeDependencies + - React-featureflagsnativemodule (0.86.0): + - hermes-engine + - React-Core-prebuilt - React-featureflags - React-jsi - React-jsiexecutor - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - - SocketRocket - - React-graphics (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-graphics (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core-prebuilt + - React-featureflags - React-jsi - React-jsiexecutor + - React-rendererdebug - React-utils - - SocketRocket - - React-hermes (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-hermes (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-cxxreact (= 0.81.1) + - React-Core-prebuilt + - React-cxxreact (= 0.86.0) - React-jsi - - React-jsiexecutor (= 0.81.1) + - React-jsiexecutor (= 0.86.0) - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - - React-perflogger (= 0.81.1) + - React-jsitooling + - React-oscompat + - React-perflogger (= 0.86.0) - React-runtimeexecutor - - SocketRocket - - React-idlecallbacksnativemodule (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-idlecallbacksnativemodule (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core-prebuilt - React-jsi - React-jsiexecutor - React-RCTFBReactNativeSpec - React-runtimeexecutor - React-runtimescheduler - ReactCommon/turbomodule/core - - SocketRocket - - React-ImageManager (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric + - ReactNativeDependencies + - React-ImageManager (0.86.0): + - React-Core-prebuilt - React-Core/Default - React-debug - React-Fabric - React-graphics - React-rendererdebug - React-utils - - SocketRocket - - React-jserrorhandler (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-intersectionobservernativemodule (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core-prebuilt + - React-cxxreact + - React-Fabric + - React-Fabric/bridging + - React-graphics + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec + - React-runtimeexecutor + - React-runtimescheduler + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - Yoga + - React-jserrorhandler (0.86.0): + - hermes-engine + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags - React-jsi - ReactCommon/turbomodule/bridging - - SocketRocket - - React-jsi (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - SocketRocket - - React-jsiexecutor (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-cxxreact (= 0.81.1) - - React-jsi (= 0.81.1) + - ReactNativeDependencies + - React-jsi (0.86.0): + - hermes-engine + - React-Core-prebuilt + - ReactNativeDependencies + - React-jsiexecutor (0.86.0): + - hermes-engine + - React-Core-prebuilt + - React-cxxreact + - React-debug + - React-jserrorhandler + - React-jsi - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - - React-perflogger (= 0.81.1) + - React-jsitooling + - React-perflogger - React-runtimeexecutor - - SocketRocket - - React-jsinspector (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-utils + - ReactNativeDependencies + - React-jsinspector (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core-prebuilt - React-featureflags - React-jsi - React-jsinspectorcdp - React-jsinspectornetwork - React-jsinspectortracing - React-oscompat - - React-perflogger (= 0.81.1) + - React-perflogger (= 0.86.0) - React-runtimeexecutor - - SocketRocket - - React-jsinspectorcdp (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - SocketRocket - - React-jsinspectornetwork (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-featureflags + - React-utils + - ReactNativeDependencies + - React-jsinspectorcdp (0.86.0): + - React-Core-prebuilt + - ReactNativeDependencies + - React-jsinspectornetwork (0.86.0): + - React-Core-prebuilt - React-jsinspectorcdp - - React-performancetimeline - - React-timing - - SocketRocket - - React-jsinspectortracing (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric + - ReactNativeDependencies + - React-jsinspectortracing (0.86.0): + - hermes-engine + - React-Core-prebuilt + - React-jsi + - React-jsinspectornetwork - React-oscompat - React-timing - - SocketRocket - - React-jsitooling (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-cxxreact (= 0.81.1) - - React-jsi (= 0.81.1) + - React-utils + - ReactNativeDependencies + - React-jsitooling (0.86.0): + - hermes-engine + - React-Core-prebuilt + - React-cxxreact (= 0.86.0) + - React-debug + - React-jsi (= 0.86.0) - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - React-runtimeexecutor - - SocketRocket - - React-jsitracing (0.81.1): - - React-jsi - - React-logger (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - SocketRocket - - React-Mapbuffer (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-debug - - SocketRocket - - React-microtasksnativemodule (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-utils + - ReactNativeDependencies + - React-jsitracing (0.86.0): + - React-jsi + - React-logger (0.86.0): + - React-Core-prebuilt + - ReactNativeDependencies + - React-Mapbuffer (0.86.0): + - React-Core-prebuilt + - React-debug + - ReactNativeDependencies + - React-microtasksnativemodule (0.86.0): + - hermes-engine + - React-Core-prebuilt - React-jsi - React-jsiexecutor - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies + - React-mutationobservernativemodule (0.86.0): + - hermes-engine + - React-Core-prebuilt + - React-cxxreact + - React-Fabric + - React-Fabric/bridging + - React-Fabric/observers/mutation + - React-featureflags + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec + - React-runtimeexecutor + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - Yoga - react-native-blur (4.4.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -1921,20 +1581,14 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - react-native-image-picker (8.2.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -1949,20 +1603,14 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - react-native-safe-area-context (5.6.2): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -1979,20 +1627,14 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - react-native-safe-area-context/common (5.6.2): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2007,20 +1649,14 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - react-native-safe-area-context/fabric (5.6.2): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2036,20 +1672,14 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - react-native-slider (5.1.2): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2065,20 +1695,14 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - react-native-slider/common (5.1.2): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2093,20 +1717,15 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - React-NativeModulesApple (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-NativeModulesApple (0.86.0): + - hermes-engine - React-callinvoker - React-Core + - React-Core-prebuilt - React-cxxreact + - React-debug - React-featureflags - React-jsi - React-jsinspector @@ -2114,60 +1733,53 @@ PODS: - React-runtimeexecutor - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket - - React-oscompat (0.81.1) - - React-perflogger (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - SocketRocket - - React-performancetimeline (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric + - ReactNativeDependencies + - React-networking (0.86.0): + - React-Core-prebuilt + - React-jsinspectornetwork + - React-jsinspectortracing + - React-performancetimeline + - React-timing + - ReactNativeDependencies + - React-oscompat (0.86.0) + - React-perflogger (0.86.0): + - React-Core-prebuilt + - ReactNativeDependencies + - React-performancecdpmetrics (0.86.0): + - hermes-engine + - React-Core-prebuilt + - React-jsi + - React-performancetimeline + - React-runtimeexecutor + - React-timing + - ReactNativeDependencies + - React-performancetimeline (0.86.0): + - React-Core-prebuilt - React-featureflags + - React-jsinspector - React-jsinspectortracing - React-perflogger - React-timing - - SocketRocket - - React-RCTActionSheet (0.81.1): - - React-Core/RCTActionSheetHeaders (= 0.81.1) - - React-RCTAnimation (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric + - ReactNativeDependencies + - React-RCTActionSheet (0.86.0): + - React-Core/RCTActionSheetHeaders (= 0.86.0) + - React-RCTAnimation (0.86.0): - RCTTypeSafety + - React-Core-prebuilt - React-Core/RCTAnimationHeaders + - React-debug - React-featureflags - React-jsi - React-NativeModulesApple - React-RCTFBReactNativeSpec - ReactCommon - - SocketRocket - - React-RCTAppDelegate (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-RCTAppDelegate (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-CoreModules - React-debug - React-defaultsnativemodule @@ -2189,16 +1801,10 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon - - SocketRocket - - React-RCTBlob (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - ReactNativeDependencies + - React-RCTBlob (0.86.0): + - hermes-engine + - React-Core-prebuilt - React-Core/RCTBlobHeaders - React-Core/RCTWebSocket - React-jsi @@ -2208,17 +1814,12 @@ PODS: - React-RCTFBReactNativeSpec - React-RCTNetwork - ReactCommon - - SocketRocket - - React-RCTFabric (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-RCTFabric (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - RCTSwiftUIWrapper - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-FabricComponents @@ -2229,8 +1830,9 @@ PODS: - React-jsi - React-jsinspector - React-jsinspectorcdp - - React-jsinspectornetwork - React-jsinspectortracing + - React-networking + - React-performancecdpmetrics - React-performancetimeline - React-RCTAnimation - React-RCTFBReactNativeSpec @@ -2242,37 +1844,25 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket + - ReactNativeDependencies - Yoga - - React-RCTFBReactNativeSpec (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-RCTFBReactNativeSpec (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-jsi - React-NativeModulesApple - - React-RCTFBReactNativeSpec/components (= 0.81.1) + - React-RCTFBReactNativeSpec/components (= 0.86.0) - ReactCommon - - SocketRocket - - React-RCTFBReactNativeSpec/components (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-RCTFBReactNativeSpec/components (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2282,59 +1872,44 @@ PODS: - React-rendererdebug - React-utils - ReactCommon - - SocketRocket + - ReactNativeDependencies - Yoga - - React-RCTImage (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric + - React-RCTImage (0.86.0): - RCTTypeSafety + - React-Core-prebuilt - React-Core/RCTImageHeaders - React-jsi - React-NativeModulesApple - React-RCTFBReactNativeSpec - React-RCTNetwork - ReactCommon - - SocketRocket - - React-RCTLinking (0.81.1): - - React-Core/RCTLinkingHeaders (= 0.81.1) - - React-jsi (= 0.81.1) + - ReactNativeDependencies + - React-RCTLinking (0.86.0): + - React-Core/RCTLinkingHeaders (= 0.86.0) + - React-jsi (= 0.86.0) - React-NativeModulesApple - React-RCTFBReactNativeSpec - ReactCommon - - ReactCommon/turbomodule/core (= 0.81.1) - - React-RCTNetwork (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric + - ReactCommon/turbomodule/core (= 0.86.0) + - React-RCTNetwork (0.86.0): - RCTTypeSafety + - React-Core-prebuilt - React-Core/RCTNetworkHeaders + - React-debug - React-featureflags - React-jsi - React-jsinspectorcdp - React-jsinspectornetwork - React-NativeModulesApple + - React-networking - React-RCTFBReactNativeSpec - ReactCommon - - SocketRocket - - React-RCTRuntime (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-RCTRuntime (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - React-Core + - React-Core-prebuilt + - React-debug - React-jsi - React-jsinspector - React-jsinspectorcdp @@ -2344,63 +1919,40 @@ PODS: - React-RuntimeCore - React-runtimeexecutor - React-RuntimeHermes - - SocketRocket - - React-RCTSettings (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric + - React-utils + - ReactNativeDependencies + - React-RCTSettings (0.86.0): - RCTTypeSafety + - React-Core-prebuilt - React-Core/RCTSettingsHeaders - React-jsi - React-NativeModulesApple - React-RCTFBReactNativeSpec - ReactCommon - - SocketRocket - - React-RCTText (0.81.1): - - React-Core/RCTTextHeaders (= 0.81.1) + - ReactNativeDependencies + - React-RCTText (0.86.0): + - React-Core/RCTTextHeaders (= 0.86.0) - Yoga - - React-RCTVibration (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric + - React-RCTVibration (0.86.0): + - React-Core-prebuilt - React-Core/RCTVibrationHeaders - React-jsi - React-NativeModulesApple - React-RCTFBReactNativeSpec - ReactCommon - - SocketRocket - - React-rendererconsistency (0.81.1) - - React-renderercss (0.81.1): - - React-debug - - React-utils - - React-rendererdebug (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-debug - - SocketRocket - - React-RuntimeApple (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - ReactNativeDependencies + - React-rendererconsistency (0.86.0) + - React-renderercss (0.86.0): + - React-debug + - React-utils + - React-rendererdebug (0.86.0): + - React-Core-prebuilt + - React-debug + - ReactNativeDependencies + - React-RuntimeApple (0.86.0): + - hermes-engine - React-callinvoker + - React-Core-prebuilt - React-Core/Default - React-CoreModules - React-cxxreact @@ -2419,16 +1971,10 @@ PODS: - React-RuntimeHermes - React-runtimescheduler - React-utils - - SocketRocket - - React-RuntimeCore (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-RuntimeCore (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core-prebuilt - React-cxxreact - React-Fabric - React-featureflags @@ -2441,29 +1987,17 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - SocketRocket - - React-runtimeexecutor (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric + - ReactNativeDependencies + - React-runtimeexecutor (0.86.0): + - React-Core-prebuilt - React-debug - React-featureflags - - React-jsi (= 0.81.1) + - React-jsi (= 0.86.0) - React-utils - - SocketRocket - - React-RuntimeHermes (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-RuntimeHermes (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core-prebuilt - React-featureflags - React-hermes - React-jsi @@ -2475,17 +2009,11 @@ PODS: - React-RuntimeCore - React-runtimeexecutor - React-utils - - SocketRocket - - React-runtimescheduler (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactNativeDependencies + - React-runtimescheduler (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - React-callinvoker + - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags @@ -2497,35 +2025,46 @@ PODS: - React-runtimeexecutor - React-timing - React-utils - - SocketRocket - - React-timing (0.81.1): + - ReactNativeDependencies + - React-timing (0.86.0): - React-debug - - React-utils (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - React-utils (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - React-Core-prebuilt - React-debug - - React-jsi (= 0.81.1) - - SocketRocket - - ReactAppDependencyProvider (0.81.1): + - React-jsi (= 0.86.0) + - ReactNativeDependencies + - React-viewtransitionnativemodule (0.86.0): + - hermes-engine + - React-Core-prebuilt + - React-Fabric + - React-Fabric/bridging + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec + - React-runtimeexecutor + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - Yoga + - React-webperformancenativemodule (0.86.0): + - hermes-engine + - React-Core-prebuilt + - React-cxxreact + - React-jsi + - React-jsiexecutor + - React-performancetimeline + - React-RCTFBReactNativeSpec + - React-runtimeexecutor + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - ReactAppDependencyProvider (0.86.0): - ReactCodegen - - ReactCodegen (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - ReactCodegen (0.86.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-FabricImage @@ -2539,79 +2078,72 @@ PODS: - React-utils - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket - - ReactCommon (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - ReactCommon/turbomodule (= 0.81.1) - - SocketRocket - - ReactCommon/turbomodule (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-callinvoker (= 0.81.1) - - React-cxxreact (= 0.81.1) - - React-jsi (= 0.81.1) - - React-logger (= 0.81.1) - - React-perflogger (= 0.81.1) - - ReactCommon/turbomodule/bridging (= 0.81.1) - - ReactCommon/turbomodule/core (= 0.81.1) - - SocketRocket - - ReactCommon/turbomodule/bridging (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-callinvoker (= 0.81.1) - - React-cxxreact (= 0.81.1) - - React-jsi (= 0.81.1) - - React-logger (= 0.81.1) - - React-perflogger (= 0.81.1) - - SocketRocket - - ReactCommon/turbomodule/core (0.81.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-callinvoker (= 0.81.1) - - React-cxxreact (= 0.81.1) - - React-debug (= 0.81.1) - - React-featureflags (= 0.81.1) - - React-jsi (= 0.81.1) - - React-logger (= 0.81.1) - - React-perflogger (= 0.81.1) - - React-utils (= 0.81.1) - - SocketRocket - - RNGestureHandler (2.30.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric + - ReactNativeDependencies + - ReactCommon (0.86.0): + - React-Core-prebuilt + - ReactCommon/turbomodule (= 0.86.0) + - ReactNativeDependencies + - ReactCommon/turbomodule (0.86.0): + - hermes-engine + - React-callinvoker (= 0.86.0) + - React-Core-prebuilt + - React-cxxreact (= 0.86.0) + - React-jsi (= 0.86.0) + - React-logger (= 0.86.0) + - React-perflogger (= 0.86.0) + - ReactCommon/turbomodule/bridging (= 0.86.0) + - ReactCommon/turbomodule/core (= 0.86.0) + - ReactNativeDependencies + - ReactCommon/turbomodule/bridging (0.86.0): + - hermes-engine + - React-callinvoker (= 0.86.0) + - React-Core-prebuilt + - React-cxxreact (= 0.86.0) + - React-jsi (= 0.86.0) + - React-logger (= 0.86.0) + - React-perflogger (= 0.86.0) + - ReactNativeDependencies + - ReactCommon/turbomodule/core (0.86.0): + - hermes-engine + - React-callinvoker (= 0.86.0) + - React-Core-prebuilt + - React-cxxreact (= 0.86.0) + - React-debug (= 0.86.0) + - React-featureflags (= 0.86.0) + - React-jsi (= 0.86.0) + - React-logger (= 0.86.0) + - React-perflogger (= 0.86.0) + - React-utils (= 0.86.0) + - ReactNativeDependencies + - ReactNativeDependencies (0.86.0) + - RNGestureHandler (3.0.2): + - hermes-engine + - RCTRequired + - RCTTypeSafety + - React-Core + - React-Core-prebuilt + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - Yoga + - RNPermissions (5.4.4): + - hermes-engine - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2626,24 +2158,19 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - RNPermissions (5.4.4): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - RNReanimated (4.5.1): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager - React-jsi - React-NativeModulesApple @@ -2654,20 +2181,18 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies + - RNReanimated/apple (= 4.5.1) + - RNReanimated/common (= 4.5.1) + - RNReanimated/view (= 4.5.1) + - RNWorklets - Yoga - - RNReanimated (4.2.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - RNReanimated/apple (4.5.1): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2683,22 +2208,15 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated (= 4.2.1) + - ReactNativeDependencies - RNWorklets - - SocketRocket - Yoga - - RNReanimated/reanimated (4.2.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - RNReanimated/common (4.5.1): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2714,22 +2232,15 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated/apple (= 4.2.1) + - ReactNativeDependencies - RNWorklets - - SocketRocket - Yoga - - RNReanimated/reanimated/apple (4.2.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - RNReanimated/view (4.5.1): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2745,21 +2256,15 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core + - ReactNativeDependencies - RNWorklets - - SocketRocket - Yoga - - RNScreens (4.20.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - RNScreens (4.26.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2775,21 +2280,15 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNScreens/common (= 4.20.0) - - SocketRocket + - ReactNativeDependencies + - RNScreens/common (= 4.26.0) - Yoga - - RNScreens/common (4.20.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - RNScreens/common (4.26.0): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2805,20 +2304,14 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - RNSVG (15.15.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - RNSVG (15.15.5): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2833,21 +2326,15 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNSVG/common (= 15.15.1) - - SocketRocket + - ReactNativeDependencies + - RNSVG/common (= 15.15.5) - Yoga - - RNSVG/common (15.15.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - RNSVG/common (15.15.5): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2862,20 +2349,14 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - RNWorklets (0.7.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - RNWorklets (0.10.2): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2891,21 +2372,16 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNWorklets/worklets (= 0.7.1) - - SocketRocket + - ReactNativeDependencies + - RNWorklets/apple (= 0.10.2) + - RNWorklets/common (= 0.10.2) - Yoga - - RNWorklets/worklets (0.7.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - RNWorklets/apple (0.10.2): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2921,21 +2397,14 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNWorklets/worklets/apple (= 0.7.1) - - SocketRocket + - ReactNativeDependencies - Yoga - - RNWorklets/worklets/apple (0.7.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog + - RNWorklets/common (0.10.2): - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2951,24 +2420,17 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - - SocketRocket (0.7.1) - VisionCamera (5.1.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - hermes-engine - NitroImage - NitroModules - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-callinvoker - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -2983,14 +2445,9 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - Yoga - VisionCameraMLKit (1.0.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - GoogleMLKit/BarcodeScanning - GoogleMLKit/TextRecognition - GoogleMLKit/TextRecognitionChinese @@ -2999,11 +2456,10 @@ PODS: - GoogleMLKit/TextRecognitionKorean - hermes-engine - NitroModules - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -3018,23 +2474,17 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - SocketRocket + - ReactNativeDependencies - VisionCamera - Yoga - VisionCameraWorklets (5.1.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - hermes-engine - NitroModules - - RCT-Folly - - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - React-callinvoker - React-Core + - React-Core-prebuilt - React-debug - React-Fabric - React-featureflags @@ -3049,29 +2499,26 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core + - ReactNativeDependencies - RNWorklets - - SocketRocket - VisionCamera - Yoga - Yoga (0.0.0) DEPENDENCIES: - - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - - fast_float (from `../node_modules/react-native/third-party-podspecs/fast_float.podspec`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) - - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - NitroImage (from `../node_modules/react-native-nitro-image`) - NitroModules (from `../node_modules/react-native-nitro-modules`) - - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) - RCTRequired (from `../node_modules/react-native/Libraries/Required`) + - RCTSwiftUI (from `../node_modules/react-native/ReactApple/RCTSwiftUI`) + - RCTSwiftUIWrapper (from `../node_modules/react-native/ReactApple/RCTSwiftUIWrapper`) - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) - React (from `../node_modules/react-native/`) - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) - React-Core (from `../node_modules/react-native/`) + - React-Core-prebuilt (from `../node_modules/react-native/React-Core-prebuilt.podspec`) - React-Core/RCTWebSocket (from `../node_modules/react-native/`) - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) @@ -3087,6 +2534,7 @@ DEPENDENCIES: - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`) - React-idlecallbacksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`) - React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) + - React-intersectionobservernativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/intersectionobserver`) - React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`) - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) @@ -3099,13 +2547,16 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) + - React-mutationobservernativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/mutationobserver`) - "react-native-blur (from `../node_modules/@react-native-community/blur`)" - react-native-image-picker (from `../node_modules/react-native-image-picker`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - "react-native-slider (from `../node_modules/@react-native-community/slider`)" - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) + - React-networking (from `../node_modules/react-native/ReactCommon/react/networking`) - React-oscompat (from `../node_modules/react-native/ReactCommon/oscompat`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) + - React-performancecdpmetrics (from `../node_modules/react-native/ReactCommon/react/performance/cdpmetrics`) - React-performancetimeline (from `../node_modules/react-native/ReactCommon/react/performance/timeline`) - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) @@ -3130,16 +2581,18 @@ DEPENDENCIES: - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) - React-timing (from `../node_modules/react-native/ReactCommon/react/timing`) - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - - ReactAppDependencyProvider (from `build/generated/ios`) - - ReactCodegen (from `build/generated/ios`) + - React-viewtransitionnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/viewtransition`) + - React-webperformancenativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/webperformance`) + - ReactAppDependencyProvider (from `build/generated/ios/ReactAppDependencyProvider`) + - ReactCodegen (from `build/generated/ios/ReactCodegen`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - ReactNativeDependencies (from `../node_modules/react-native/third-party-podspecs/ReactNativeDependencies.podspec`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNPermissions (from `../node_modules/react-native-permissions`) - RNReanimated (from `../node_modules/react-native-reanimated`) - RNScreens (from `../node_modules/react-native-screens`) - RNSVG (from `../node_modules/react-native-svg`) - RNWorklets (from `../node_modules/react-native-worklets`) - - SocketRocket (~> 0.7.1) - VisionCamera (from `../node_modules/react-native-vision-camera`) - VisionCameraMLKit (from `../..`) - VisionCameraWorklets (from `../node_modules/react-native-vision-camera-worklets`) @@ -3165,34 +2618,25 @@ SPEC REPOS: - MLKitVision - nanopb - PromisesObjC - - SocketRocket EXTERNAL SOURCES: - boost: - :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" - DoubleConversion: - :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" - fast_float: - :podspec: "../node_modules/react-native/third-party-podspecs/fast_float.podspec" FBLazyVector: :path: "../node_modules/react-native/Libraries/FBLazyVector" - fmt: - :podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec" - glog: - :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" hermes-engine: :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" - :tag: hermes-2025-07-07-RNv0.81.0-e0fc67142ec0763c6b6153ca2bf96df815539782 + :tag: hermes-v250829098.0.14 NitroImage: :path: "../node_modules/react-native-nitro-image" NitroModules: :path: "../node_modules/react-native-nitro-modules" - RCT-Folly: - :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTDeprecation: :path: "../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation" RCTRequired: :path: "../node_modules/react-native/Libraries/Required" + RCTSwiftUI: + :path: "../node_modules/react-native/ReactApple/RCTSwiftUI" + RCTSwiftUIWrapper: + :path: "../node_modules/react-native/ReactApple/RCTSwiftUIWrapper" RCTTypeSafety: :path: "../node_modules/react-native/Libraries/TypeSafety" React: @@ -3201,6 +2645,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/callinvoker" React-Core: :path: "../node_modules/react-native/" + React-Core-prebuilt: + :podspec: "../node_modules/react-native/React-Core-prebuilt.podspec" React-CoreModules: :path: "../node_modules/react-native/React/CoreModules" React-cxxreact: @@ -3229,6 +2675,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks" React-ImageManager: :path: "../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios" + React-intersectionobservernativemodule: + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/intersectionobserver" React-jserrorhandler: :path: "../node_modules/react-native/ReactCommon/jserrorhandler" React-jsi: @@ -3253,6 +2701,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" React-microtasksnativemodule: :path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" + React-mutationobservernativemodule: + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/mutationobserver" react-native-blur: :path: "../node_modules/@react-native-community/blur" react-native-image-picker: @@ -3263,10 +2713,14 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-community/slider" React-NativeModulesApple: :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" + React-networking: + :path: "../node_modules/react-native/ReactCommon/react/networking" React-oscompat: :path: "../node_modules/react-native/ReactCommon/oscompat" React-perflogger: :path: "../node_modules/react-native/ReactCommon/reactperflogger" + React-performancecdpmetrics: + :path: "../node_modules/react-native/ReactCommon/react/performance/cdpmetrics" React-performancetimeline: :path: "../node_modules/react-native/ReactCommon/react/performance/timeline" React-RCTActionSheet: @@ -3315,12 +2769,18 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/react/timing" React-utils: :path: "../node_modules/react-native/ReactCommon/react/utils" + React-viewtransitionnativemodule: + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/viewtransition" + React-webperformancenativemodule: + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/webperformance" ReactAppDependencyProvider: - :path: build/generated/ios + :path: build/generated/ios/ReactAppDependencyProvider ReactCodegen: - :path: build/generated/ios + :path: build/generated/ios/ReactCodegen ReactCommon: :path: "../node_modules/react-native/ReactCommon" + ReactNativeDependencies: + :podspec: "../node_modules/react-native/third-party-podspecs/ReactNativeDependencies.podspec" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNPermissions: @@ -3343,19 +2803,14 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 - DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb - fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6 - FBLazyVector: b8f1312d48447cca7b4abc21ed155db14742bd03 - fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd - glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 + FBLazyVector: b3e7ad108f0d882e30445c5527d774e3fd432f3d GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleMLKit: 97ac7af399057e99182ee8edfa8249e3226a4065 GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8 GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 - hermes-engine: 4f8246b1f6d79f625e0d99472d1f3a71da4d28ca + hermes-engine: 81dcdbf6d754602a93d3f56df72372aff2585329 MLImage: 1824212150da33ef225fbd3dc49f184cf611046c MLKitBarcodeScanning: 10ca0845a6d15f2f6e911f682a1998b68b973e8b MLKitCommon: afec63980417d29ffbb4790529a1b0a2291699e1 @@ -3367,88 +2822,96 @@ SPEC CHECKSUMS: MLKitTextRecognitionKorean: 4877b6a8d95cd8846586e83f85b2f3e9ac93a737 MLKitVision: e858c5f125ecc288e4a31127928301eaba9ae0c1 nanopb: 438bc412db1928dac798aa6fd75726007be04262 - NitroImage: cef8d9c91d6d9247ec0569bb68148f7fba717026 - NitroModules: 8d376059cb66130ebac6a84982c068d69012b567 + NitroImage: 2f592086cbdecbbaa1c10dd97e0cdd43b1bb0817 + NitroModules: b4174dd303728e16ad1afb79f64c1a5c69a3b373 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 - RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f - RCTDeprecation: c4b9e2fd0ab200e3af72b013ed6113187c607077 - RCTRequired: e97dd5dafc1db8094e63bc5031e0371f092ae92a - RCTTypeSafety: 720403058b7c1380c6a3ae5706981d6362962c89 - React: f1486d005993b0af01943af1850d3d4f3b597545 - React-callinvoker: 133f69368c8559e744efa345223625d412f5dfbe - React-Core: 559823921b4f294c2840fa8238ca958a29ddc211 - React-CoreModules: c41e7bbfabbc420783bb926f45837a0d5e53341e - React-cxxreact: 9cb9fa738274a1b36b97ede09c8a6717dec1a20b - React-debug: e01581e1589f329e61c95b332bf7f4969b10564b - React-defaultsnativemodule: bbb39447caa6b6cf9405fa0099f828c083640faa - React-domnativemodule: 03744d12b6d56d098531a933730bf1d4cb79bdfb - React-Fabric: 530b3993a12a96e8a7cdb9f0ef48e605277b572e - React-FabricComponents: 271ec2a9b2c00ac66fd6d1fd24e9e964d907751d - React-FabricImage: d0af66e976dbab7f8b81e36dd369fc70727d2695 - React-featureflags: 269704c8eff86e0485c9d384e286350fcda6eb70 - React-featureflagsnativemodule: db1e5d88a912fb08a5ece33fcf64e1b732da8467 - React-graphics: b19d03a01b0722b4dc82f47acb56dc3ed41937e7 - React-hermes: 811606c0aca5a3f9c6fa8e4994e02ca8f677e68e - React-idlecallbacksnativemodule: 3a3df629cd50046c7e4354f9025aefe8f2c84601 - React-ImageManager: 0d53866c63132791e37bb2373f93044fdef14aa3 - React-jserrorhandler: d5700d6ab7162fd575287502a3c5d601d98e7f09 - React-jsi: ece95417fedbed0e7153a855cb8342b7c72ab75e - React-jsiexecutor: 2b0bb644b533df2f5c0cd6ade9a4560d0bf1dd84 - React-jsinspector: 0c160f8510a8852bdf2dac12f0b1949efc18200b - React-jsinspectorcdp: f4b84409f453f61ddd8614ad45139bc594ec6bb5 - React-jsinspectornetwork: 8f2f0ca8c871ca19b571f426002c0012e7fb2aee - React-jsinspectortracing: 33f6b977eb8a4bc1e3d1a4b948809aca083143f9 - React-jsitooling: 2c61529b589e17229a9f0a4a4fc35aa7ad495850 - React-jsitracing: 838a7b0c013c4aff7d382d7fdc78cf442013ba1d - React-logger: 7aef4d74123e5e3d267e5af1fbf5135b5a0d8381 - React-Mapbuffer: 91e0eab42a6ae7f3e34091a126d70fc53bd3823e - React-microtasksnativemodule: 1ead4fe154df3b1ba34b5a9e35ef3c4bdfa72ccb - react-native-blur: 6af83e7e3c4c1446a188d9b2c493600fc4beb173 - react-native-image-picker: 0314366753615115fa55c3cc937ac44cb7e75702 - react-native-safe-area-context: c00143b4823773bba23f2f19f85663ae89ceb460 - react-native-slider: 34064ca1a6864d7b263e44dd76a2d794e8d26744 - React-NativeModulesApple: eff2eba56030eb0d107b1642b8f853bc36a833ac - React-oscompat: b12c633e9c00f1f99467b1e0e0b8038895dae436 - React-perflogger: 58d12c4e5df1403030c97b9c621375c312cca454 - React-performancetimeline: 0ee0a3236c77a4ee6d8a6189089e41e4003d292e - React-RCTActionSheet: 3f741a3712653611a6bfc5abceb8260af9d0b218 - React-RCTAnimation: 408ad69ea136e99a463dd33eadecc29e586b3d72 - React-RCTAppDelegate: f03b46e80b8a3dbfa84b35abfe123e02f3ceef83 - React-RCTBlob: bd42e92a00ad22eaab92ffe5c137e7a2f725887a - React-RCTFabric: b99ab638c73cf2d57b886eafdbfb2e4909b0eb9a - React-RCTFBReactNativeSpec: 7ad9aba0e0655e3f29be0a1c3fd4a888fab04dcf - React-RCTImage: 0f1c74f7cd20027f8c34976a211b35d4263a0add - React-RCTLinking: 6d7dfc3a74110df56c3a73cc7626bf4415656542 - React-RCTNetwork: 6a25d8645a80d5b86098675ca39bf8fcf1afa08b - React-RCTRuntime: 38bfe9766565ae3293ca230bc51c9c020a8bc98a - React-RCTSettings: 651d9ae2cdd32f547ad0d225a2c13886d6ad2358 - React-RCTText: 9bc66cd288478e23195e01f5cb45eba79986b2b4 - React-RCTVibration: 371226f5667a00c76d792dcdb5c2e0fcbcde0c3b - React-rendererconsistency: a05f6c37f9389c53213d1e28798e441fa6fbdbcd - React-renderercss: 6e4febfa014b0f53bc171a62b0f713ddbdbb9860 - React-rendererdebug: e94bf27b9d55ef2795caa8e43aa92abc4a373b8b - React-RuntimeApple: 723be5159519eba1cd92449acb29436d21571b82 - React-RuntimeCore: f58eb0f01065c9d27d91de10b2e4ab4c76d83b0e - React-runtimeexecutor: f615ec8742d0b5820170f7c8b4d2c7cb75d93ac9 - React-RuntimeHermes: fddb258e03d330d1132bb19e78fe51ac2f3f41ac - React-runtimescheduler: e92a31460e654ced8587debeec37553315e1b6a5 - React-timing: 97ada2c47b4c5932e7f773c7d239c52b90d6ca68 - React-utils: f0949d247a46b4c09f03e5a3cb1167602d0b729a - ReactAppDependencyProvider: 3eb9096cb139eb433965693bbe541d96eb3d3ec9 - ReactCodegen: 4d203eddf6f977caa324640a20f92e70408d648b - ReactCommon: ce5d4226dfaf9d5dacbef57b4528819e39d3a120 - RNGestureHandler: e37bdb684df1ac17c7e1d8f71a3311b2793c186b - RNPermissions: 455d17943b9aa25ea450fe02802bb9ea99354427 - RNReanimated: fabac5a7aee92d454d7aef06335a7bef955d5603 - RNScreens: 714e10b6b554f7dc7ad9f78dcf36dc8e3fc73415 - RNSVG: d260beec1775108e1bd065a0ed666ff2c5565158 - RNWorklets: 416ef974c176d76634e34c0aeda83f6b67084a88 - SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - VisionCamera: 8407cd5a5a61543e8b35b1a9dab1ee39dc025130 - VisionCameraMLKit: c5215e7401725161b8a5b61f1b87f6aef4e25913 - VisionCameraWorklets: 425cff0639eaf1669efd11d02b89b969fe3b4fbc - Yoga: fa23995c18b65978347b096d0836f4f5093df545 + RCTDeprecation: 2a74a2c57675e64419bd89078efde81f7c1de90b + RCTRequired: 30451112e6fef4e6f31b4e7eee0845156e35e4b0 + RCTSwiftUI: 5aaf0b07e747ba749dc6acc94d8bd41eea4b570f + RCTSwiftUIWrapper: ab2ca548be15d63afa95103afc8685a7c3eab78b + RCTTypeSafety: 3eaed17dbddb0b989208b062ea14c44d412b9780 + React: 2574546f2d017abd14d0c9b48cf2b6a0547c2591 + React-callinvoker: 03cd4b931d1d583d87aae99b8f7b6fe26bf571ee + React-Core: 1c824d9c7dd8aa760b5f1b50d5a54c2a3f598f87 + React-Core-prebuilt: 7da85c26e5616d52f119b81ce5e3e6fc5c9bda49 + React-CoreModules: 2f9ed75bca7f6dea2b70e8a1f4a5ca9b6be52d76 + React-cxxreact: 7103d5ba69848c039e11079e74ceede05efe795e + React-debug: 8cc8d99ccc664ef9e873027f7fc3fb0a50496012 + React-defaultsnativemodule: 603c108411d39d7bb5ccb8c270f1f50cb6207e10 + React-domnativemodule: c49a502edc85f515a029c341fe5fba155fa6675c + React-Fabric: acc4915d719db793c5853e5c74b54ea5aa48e865 + React-FabricComponents: aebebc98914a4a8fc962fa7b5b98ebaf250acb68 + React-FabricImage: 42e99e48ff73c8b20cee229e622977d0b97b6247 + React-featureflags: c6d8d8d52acf3a956485726076b73eeff7935340 + React-featureflagsnativemodule: c992de92dcf30dcf09efdec17752abe4128ec55f + React-graphics: 239fd9b0512e539d3563f0825618f4e49795eefd + React-hermes: ae4685ca9fa5f47003bc594d3f146e29284136d1 + React-idlecallbacksnativemodule: 10a5be842ab181953c772a3f28cdf94572833eb0 + React-ImageManager: c36a56c3b13eba92e1d0d30da35d0a1ccf29cd6c + React-intersectionobservernativemodule: 71404bfa47d31e4143cc9db3e26178b5cfcd07e1 + React-jserrorhandler: ca2eeb03c1a77bbfab1b5425b943e3e8d92295f2 + React-jsi: c4b6daf8a31ac54f2db49cd6cce29720fec1f3a8 + React-jsiexecutor: acdc1a217b7ea29bcf6315b770d01e6b1c2fca14 + React-jsinspector: 95d6394efe9fb4a64ed33afc076b32a6384c8514 + React-jsinspectorcdp: 6ce51378a552feaaaac1a7b0d995fa0c49fa4f8c + React-jsinspectornetwork: 0db435c9264f200635fdf294a3b643dd3946d4cf + React-jsinspectortracing: 2e4470e1f301ff597bd65299aa95da4bae6e5c4f + React-jsitooling: 796bc991cdce68e2cc059d0271a28e0d4f8b0891 + React-jsitracing: f3008e7b5e1d9de8d9f2ca1b85824ed86b0cea00 + React-logger: fff73f4ceecad968c97baafdc77dcf84befc38b6 + React-Mapbuffer: ce449ccdf3b80384415b925606be8a4bdcfc65d3 + React-microtasksnativemodule: 2eb3f49d0d8e77b5343455eccd057010b8d38b6b + React-mutationobservernativemodule: f0a0d5ae9b51caf7becbeabf836d716cfedb6bf2 + react-native-blur: ea6c78ddeaa7aa0fce36ea29d1f81dd23063032a + react-native-image-picker: 23540feacc79c63c60857f318fdfa8477c26e70a + react-native-safe-area-context: 91a90d98c310adcc90a511e5aeb6046d7c19d885 + react-native-slider: f9910f69950b9953c46e091377c1265b71eb01f0 + React-NativeModulesApple: a092d89b58f635ebfab88048b0eda9fb516819fd + React-networking: 968bbbe73590149feb1e72b2af4f6a68e4796ece + React-oscompat: 0b72a7e926954a0415ccd83e0748b6561fe45367 + React-perflogger: 865984e492514aa6e5279fc3e663132cfa4d5022 + React-performancecdpmetrics: 2efbf9bdb48c8d8446f4dd10e8bf0dc5d711772f + React-performancetimeline: 9d256484bff1513481be9f234baba694dc3b52e2 + React-RCTActionSheet: 902c79deec52f99cc48b1051b59bcbe86787d339 + React-RCTAnimation: 09cf722039ae30ff5d64e2b011ff054ae651c3b6 + React-RCTAppDelegate: a47de6fddb7eb0c028abba83138a5ce283bd7e77 + React-RCTBlob: 38c418d067e0c61818223503063310122e44c588 + React-RCTFabric: 480ca2a105730e1790cfd13291d086cb53725825 + React-RCTFBReactNativeSpec: 63131378510a5191515a4adfc308e65b465106f4 + React-RCTImage: 3ce36f82441b76b715818ee7ee95f6f5b34f9ee1 + React-RCTLinking: 2f7b5ed4983122e5115732d25a4360960eab583c + React-RCTNetwork: dcd3b180f33da86f5dc5e928a816eb5464fa7f16 + React-RCTRuntime: 6ef8e778fbab426b12eb5a9b5f7a0e86313c5a10 + React-RCTSettings: b97727ec8c55c35bd284457a647c938c040b942b + React-RCTText: 4d1b88f6d3e1a43afe46706d956ec6664c87b984 + React-RCTVibration: 415d14d6a1a64bf947fcef6b193915a494431168 + React-rendererconsistency: ef8519bdd9931261c6561bfad6506356b8108387 + React-renderercss: 1aa1bf99fa2ace143eb87d5190fd43609fc1e832 + React-rendererdebug: 40a4fb3dea21a7ac1759ca0eb6c88a924aecb075 + React-RuntimeApple: 84fadbb4fe8ca531e15e29a22af05911f17569c6 + React-RuntimeCore: f4d9af5f16d37e63308cb03b36c89d01e7f68a06 + React-runtimeexecutor: 4d59410f66af529d04c84c8b152ced07dabc471e + React-RuntimeHermes: fd719d8f4d9ce79636fe2e09e94b0ac31b7b263b + React-runtimescheduler: 784033620aa5515e2f45a60369eed4d32f9400b8 + React-timing: a16df9ae98f950396d9ce3abf43cb0cb2f21194c + React-utils: b68ee619aef28d8f9b85532d98db0327f7bdf743 + React-viewtransitionnativemodule: 11fe091101d381451b1542c37b2745900254f096 + React-webperformancenativemodule: b5d249419f1546663845c82f24de4e18a3180997 + ReactAppDependencyProvider: dcdd0e1b9559a6d8d8aea05286f4ed085091978e + ReactCodegen: afe0d436ee089d7bea6418429d8312b1d23a6801 + ReactCommon: d5c1bb4427bf51c443de5926aac332c89ddd9363 + ReactNativeDependencies: fa0a54b3f5319ae0e3b9aff32bfee7a424b88e66 + RNGestureHandler: 953af68599a35f425ea962dda2ee15ee4e3ca719 + RNPermissions: 82a6127b299cfb3f97c8d36919804a916e58ce26 + RNReanimated: fcf6a8cdd698fb09a811a8d6c5bcfe36575d965d + RNScreens: 62693e770c4b73093c018aa7845b691f5ba9988d + RNSVG: 6cae8d4082913be2eb43eaec2a456222c42195ed + RNWorklets: 53439575fed8525fb8d1cabcc05a3d4d91e6ffe4 + VisionCamera: 2d364657571d6b6404830269d05b1de244586a40 + VisionCameraMLKit: fa9794aa8b135e32be2d6aeacc22bc07b0b34d82 + VisionCameraWorklets: 8393549fac54f37a4cf6a7b1088688cf9b309b7f + Yoga: 94172d87dd5d83fc929bf1ee53121b5d4490b5f2 -PODFILE CHECKSUM: a0675b0805e9ef23607b6c81c757f2485c98537e +PODFILE CHECKSUM: bfa9c28a161369f7b66f55890deec95fe59f1504 COCOAPODS: 1.15.2 diff --git a/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj b/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj index ea317d0..4135522 100644 --- a/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj +++ b/example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj @@ -9,21 +9,21 @@ /* Begin PBXBuildFile section */ 057CEFDD928055D235F3DB7A /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 4CBF21469BE79F9BE7F21815 /* libPods-VisionCameraMLKitExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A63FD178BEE6246E4EC6416 /* libPods-VisionCameraMLKitExample.a */; }; 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + A215E534AD4A8AF0FC2576BB /* libPods-VisionCameraMLKitExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5356D6FA4FBA3F96B78CB5C2 /* libPods-VisionCameraMLKitExample.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 0A63FD178BEE6246E4EC6416 /* libPods-VisionCameraMLKitExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VisionCameraMLKitExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07F961A680F5B00A75B9A /* VisionCameraMLKitExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VisionCameraMLKitExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = VisionCameraMLKitExample/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = VisionCameraMLKitExample/Info.plist; sourceTree = ""; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = VisionCameraMLKitExample/PrivacyInfo.xcprivacy; sourceTree = ""; }; - 2CC660772738D5556E105E3F /* Pods-VisionCameraMLKitExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.release.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.release.xcconfig"; sourceTree = ""; }; - 65B025F5B9C706CD9747F045 /* Pods-VisionCameraMLKitExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.debug.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.debug.xcconfig"; sourceTree = ""; }; + 5356D6FA4FBA3F96B78CB5C2 /* libPods-VisionCameraMLKitExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VisionCameraMLKitExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 625C59F936557B13E231DCEB /* Pods-VisionCameraMLKitExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.debug.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.debug.xcconfig"; sourceTree = ""; }; 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = VisionCameraMLKitExample/AppDelegate.swift; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = VisionCameraMLKitExample/LaunchScreen.storyboard; sourceTree = ""; }; + E68B4C59D121CD78D0153B88 /* Pods-VisionCameraMLKitExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraMLKitExample.release.xcconfig"; path = "Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -32,7 +32,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4CBF21469BE79F9BE7F21815 /* libPods-VisionCameraMLKitExample.a in Frameworks */, + A215E534AD4A8AF0FC2576BB /* libPods-VisionCameraMLKitExample.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -55,7 +55,7 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 0A63FD178BEE6246E4EC6416 /* libPods-VisionCameraMLKitExample.a */, + 5356D6FA4FBA3F96B78CB5C2 /* libPods-VisionCameraMLKitExample.a */, ); name = Frameworks; sourceTree = ""; @@ -92,8 +92,8 @@ BBD78D7AC51CEA395F1C20DB /* Pods */ = { isa = PBXGroup; children = ( - 65B025F5B9C706CD9747F045 /* Pods-VisionCameraMLKitExample.debug.xcconfig */, - 2CC660772738D5556E105E3F /* Pods-VisionCameraMLKitExample.release.xcconfig */, + 625C59F936557B13E231DCEB /* Pods-VisionCameraMLKitExample.debug.xcconfig */, + E68B4C59D121CD78D0153B88 /* Pods-VisionCameraMLKitExample.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -105,13 +105,13 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "VisionCameraMLKitExample" */; buildPhases = ( - 8581DA70A42FA364899A11ED /* [CP] Check Pods Manifest.lock */, + 00F024070541868F49A03B54 /* [CP] Check Pods Manifest.lock */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - D0DE7A8E7FF532555CD54397 /* [CP] Embed Pods Frameworks */, - 04BD6B3760B3994DF3D2A7AE /* [CP] Copy Pods Resources */, + 6C1BBF82F44D50DC71B59ABF /* [CP] Embed Pods Frameworks */, + B937774704109FDAC544271B /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -182,26 +182,9 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; + shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"\\\"$WITH_ENVIRONMENT\\\" \\\"$REACT_NATIVE_XCODE\\\"\"\n"; }; - 04BD6B3760B3994DF3D2A7AE /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 8581DA70A42FA364899A11ED /* [CP] Check Pods Manifest.lock */ = { + 00F024070541868F49A03B54 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -223,7 +206,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - D0DE7A8E7FF532555CD54397 /* [CP] Embed Pods Frameworks */ = { + 6C1BBF82F44D50DC71B59ABF /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -240,6 +223,23 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + B937774704109FDAC544271B /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VisionCameraMLKitExample/Pods-VisionCameraMLKitExample-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -256,7 +256,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 65B025F5B9C706CD9747F045 /* Pods-VisionCameraMLKitExample.debug.xcconfig */; + baseConfigurationReference = 625C59F936557B13E231DCEB /* Pods-VisionCameraMLKitExample.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -289,7 +289,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2CC660772738D5556E105E3F /* Pods-VisionCameraMLKitExample.release.xcconfig */; + baseConfigurationReference = E68B4C59D121CD78D0153B88 /* Pods-VisionCameraMLKitExample.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -381,6 +381,10 @@ ); MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ( + "$(inherited)", + "-DRCT_REMOVE_LEGACY_ARCH=1", + ); OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "-DFOLLY_NO_CONFIG", @@ -388,11 +392,14 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", + "-DRCT_REMOVE_LEGACY_ARCH=1", ); + PODFILE_DIR = "$(SRCROOT)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + SWIFT_ENABLE_EXPLICIT_MODULES = NO; USE_HERMES = true; }; name = Debug; @@ -452,6 +459,10 @@ "\"$(inherited)\"", ); MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CFLAGS = ( + "$(inherited)", + "-DRCT_REMOVE_LEGACY_ARCH=1", + ); OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "-DFOLLY_NO_CONFIG", @@ -459,11 +470,14 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", + "-DRCT_REMOVE_LEGACY_ARCH=1", ); + PODFILE_DIR = "$(SRCROOT)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_ENABLE_EXPLICIT_MODULES = NO; USE_HERMES = true; VALIDATE_PRODUCT = YES; }; diff --git a/example/ios/VisionCameraMLKitExample/Info.plist b/example/ios/VisionCameraMLKitExample/Info.plist index b86ed8d..bab19d4 100644 --- a/example/ios/VisionCameraMLKitExample/Info.plist +++ b/example/ios/VisionCameraMLKitExample/Info.plist @@ -2,6 +2,8 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion en CFBundleDisplayName diff --git a/example/jest.config.js b/example/jest.config.js index 8eb675e..294be30 100644 --- a/example/jest.config.js +++ b/example/jest.config.js @@ -1,3 +1,3 @@ module.exports = { - preset: 'react-native', + preset: '@react-native/jest-preset', }; diff --git a/example/package.json b/example/package.json index 86ac7f0..3bd51a8 100644 --- a/example/package.json +++ b/example/package.json @@ -17,37 +17,37 @@ "@react-navigation/native": "^7.1.26", "@react-navigation/native-stack": "^7.9.0", "lucide-react-native": "^0.562.0", - "react": "19.1.0", - "react-native": "0.81.1", - "react-native-gesture-handler": "^2.30.0", + "react": "19.2.3", + "react-native": "0.86.0", + "react-native-gesture-handler": "^3.0.2", "react-native-image-picker": "^8.2.1", - "react-native-nitro-image": "0.15.0", + "react-native-nitro-image": "0.15.1", "react-native-nitro-modules": "0.36.1", "react-native-permissions": "^5.4.4", - "react-native-reanimated": "^4.2.1", + "react-native-reanimated": "^4.5.1", "react-native-safe-area-context": "^5.6.2", - "react-native-screens": "^4.20.0", - "react-native-svg": "^15.15.1", + "react-native-screens": "^4.26.0", + "react-native-svg": "^15.15.5", "react-native-vision-camera": "^5.1.0", - "react-native-vision-camera-worklets": "^5.0.9", - "react-native-worklets": "0.7.1", + "react-native-vision-camera-worklets": "^5.1.0", + "react-native-worklets": "0.10.2", "zustand": "^5.0.9" }, "devDependencies": { "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.3", "@babel/runtime": "^7.25.0", - "@react-native-community/cli": "20.0.0", - "@react-native-community/cli-platform-android": "20.0.0", - "@react-native-community/cli-platform-ios": "20.0.0", - "@react-native/babel-preset": "0.81.1", - "@react-native/metro-config": "0.81.1", - "@react-native/typescript-config": "0.81.1", - "@types/react": "^19.1.0", + "@react-native-community/cli": "20.1.0", + "@react-native-community/cli-platform-android": "20.1.0", + "@react-native-community/cli-platform-ios": "20.1.0", + "@react-native/babel-preset": "0.86.0", + "@react-native/metro-config": "0.86.0", + "@react-native/typescript-config": "0.86.0", + "@types/react": "^19.2.0", "react-native-builder-bob": "^0.40.13", "react-native-monorepo-config": "^0.1.9" }, "engines": { - "node": ">=22" + "node": ">=22.11.0" } } diff --git a/package.json b/package.json index b13e4aa..c8eb06d 100644 --- a/package.json +++ b/package.json @@ -84,12 +84,13 @@ "@eslint/compat": "^1.3.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.35.0", - "@react-native-community/cli": "20.0.1", - "@react-native/babel-preset": "0.81.1", - "@react-native/eslint-config": "^0.81.1", + "@react-native-community/cli": "20.1.0", + "@react-native/babel-preset": "0.86.0", + "@react-native/eslint-config": "^0.86.0", + "@react-native/jest-preset": "0.86.0", "@release-it/conventional-changelog": "^10.0.1", "@types/jest": "^29.5.14", - "@types/react": "^19.1.0", + "@types/react": "^19.2.0", "commitlint": "^19.8.1", "del-cli": "^6.0.0", "eslint": "^9.35.0", @@ -99,14 +100,14 @@ "lefthook": "^2.0.3", "nitrogen": "0.36.1", "prettier": "^2.8.8", - "react": "19.1.0", - "react-native": "0.81.1", + "react": "19.2.3", + "react-native": "0.86.0", "react-native-builder-bob": "^0.40.13", - "react-native-nitro-image": "0.15.0", + "react-native-nitro-image": "0.15.1", "react-native-nitro-modules": "0.36.1", "react-native-vision-camera": "^5.1.0", - "react-native-vision-camera-worklets": "^5.0.9", - "react-native-worklets": "0.7.1", + "react-native-vision-camera-worklets": "^5.1.0", + "react-native-worklets": "0.10.2", "release-it": "^19.0.4", "turbo": "^2.5.6", "typescript": "^5.9.2" @@ -182,7 +183,7 @@ } }, "jest": { - "preset": "react-native", + "preset": "@react-native/jest-preset", "modulePathIgnorePatterns": [ "/example/node_modules", "/lib/" diff --git a/yarn.lock b/yarn.lock index 93b974a..8f35795 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21,7 +21,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.24.7, @babel/code-frame@npm:^7.27.1": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.27.1": version: 7.27.1 resolution: "@babel/code-frame@npm:7.27.1" dependencies: @@ -32,7 +32,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.29.7": +"@babel/code-frame@npm:^7.29.0, @babel/code-frame@npm:^7.29.7": version: 7.29.7 resolution: "@babel/code-frame@npm:7.29.7" dependencies: @@ -50,6 +50,13 @@ __metadata: languageName: node linkType: hard +"@babel/compat-data@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/compat-data@npm:7.29.7" + checksum: 10c0/47913f05e08a45a1c9df38c02b4b49e391005085b489432647a1abe112e5d9c75e3be8ea5972b7f6da4ec5d1339922ceb9ea02b8a25d4ed1cb8636e5261f344e + languageName: node + linkType: hard + "@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.2": version: 7.28.5 resolution: "@babel/core@npm:7.28.5" @@ -73,6 +80,29 @@ __metadata: languageName: node linkType: hard +"@babel/core@npm:^7.24.4": + version: 7.29.7 + resolution: "@babel/core@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-compilation-targets": "npm:^7.29.7" + "@babel/helper-module-transforms": "npm:^7.29.7" + "@babel/helpers": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + "@jridgewell/remapping": "npm:^2.3.5" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10c0/112fb09c24de7a1de64d1de2c31fe65c4e6af4cb2fb6e6d99ea5373e6fc51e75b88581c0efae4c4c68f119a02a988c7106e95011a41530a2fb8ed793c7eaa07b + languageName: node + linkType: hard + "@babel/eslint-parser@npm:^7.25.1": version: 7.28.5 resolution: "@babel/eslint-parser@npm:7.28.5" @@ -87,7 +117,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.25.0, @babel/generator@npm:^7.28.5, @babel/generator@npm:^7.7.2": +"@babel/generator@npm:^7.28.5, @babel/generator@npm:^7.7.2": version: 7.28.5 resolution: "@babel/generator@npm:7.28.5" dependencies: @@ -100,7 +130,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.29.7": +"@babel/generator@npm:^7.29.1, @babel/generator@npm:^7.29.7": version: 7.29.7 resolution: "@babel/generator@npm:7.29.7" dependencies: @@ -144,6 +174,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-compilation-targets@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-compilation-targets@npm:7.29.7" + dependencies: + "@babel/compat-data": "npm:^7.29.7" + "@babel/helper-validator-option": "npm:^7.29.7" + browserslist: "npm:^4.24.0" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 10c0/4c15fd4c69a0a7047799a28a88460c19cede0a0ee8af994ea169114986f4af48b92c7393a4a3fee0456c11a656eece3448a6ed06354453d6c27cccf17195453b + languageName: node + linkType: hard + "@babel/helper-create-class-features-plugin@npm:^7.27.1, @babel/helper-create-class-features-plugin@npm:^7.28.3, @babel/helper-create-class-features-plugin@npm:^7.28.5": version: 7.28.5 resolution: "@babel/helper-create-class-features-plugin@npm:7.28.5" @@ -250,6 +293,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-imports@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-module-imports@npm:7.29.7" + dependencies: + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/6adf60d97356027413342a092f818d9678c4f5caff716a33e3284b5ae14e47a9e88059d421dde4ee4894691260039a12602c0e7becadc175602194b40dfa345d + languageName: node + linkType: hard + "@babel/helper-module-transforms@npm:^7.27.1, @babel/helper-module-transforms@npm:^7.28.3": version: 7.28.3 resolution: "@babel/helper-module-transforms@npm:7.28.3" @@ -263,6 +316,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-transforms@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-module-transforms@npm:7.29.7" + dependencies: + "@babel/helper-module-imports": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/ee5a2172c24a42be696836f4b0d947489c9729d8adf5821885cf77d1ad5333e3c447368e9a71f67df1099570490553dccf9f888ef0a92a48aa63cb086bd8c7e1 + languageName: node + linkType: hard + "@babel/helper-optimise-call-expression@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-optimise-call-expression@npm:7.27.1" @@ -389,6 +455,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-option@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-option@npm:7.29.7" + checksum: 10c0/d2a06c6d0ac40ba4a2f219fc2cab249c7a94bacdb2686273b7f9598571c908809b48468ff588915a346e6cc7296f60b581023d1d498b747fed06f779d335c2cc + languageName: node + linkType: hard + "@babel/helper-wrap-function@npm:^7.27.1": version: 7.28.3 resolution: "@babel/helper-wrap-function@npm:7.28.3" @@ -410,7 +483,17 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.25.3, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.5": +"@babel/helpers@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helpers@npm:7.29.7" + dependencies: + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/218e8d10953647c9f44775f5a022b227a182674853b5ea8631889deb7e1a3e4bc870388aaecf59bb8bd92a87f9a96220ed3f70a35bffec6bcf9169ecb67891ac + languageName: node + linkType: hard + +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.5": version: 7.28.5 resolution: "@babel/parser@npm:7.28.5" dependencies: @@ -421,7 +504,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.29.7": +"@babel/parser@npm:^7.24.4, @babel/parser@npm:^7.29.0, @babel/parser@npm:^7.29.7": version: 7.29.7 resolution: "@babel/parser@npm:7.29.7" dependencies: @@ -643,6 +726,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-jsx@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/plugin-syntax-jsx@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/1736000de183538ba8eef34520105508860e48b0c763254ba9158af5e814ed8bbceeedbb4281fbda33de787ae5b3870e92f60c6ae7131e7d322e451d57387896 + languageName: node + linkType: hard + "@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4": version: 7.10.4 resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" @@ -765,7 +859,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:7.27.1, @babel/plugin-transform-arrow-functions@npm:^7.24.7, @babel/plugin-transform-arrow-functions@npm:^7.27.1": +"@babel/plugin-transform-arrow-functions@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-arrow-functions@npm:7.27.1" dependencies: @@ -824,7 +918,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-class-properties@npm:7.27.1, @babel/plugin-transform-class-properties@npm:^7.25.4, @babel/plugin-transform-class-properties@npm:^7.27.1": +"@babel/plugin-transform-class-properties@npm:^7.25.4, @babel/plugin-transform-class-properties@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-class-properties@npm:7.27.1" dependencies: @@ -836,6 +930,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-class-properties@npm:^7.28.6": + version: 7.29.7 + resolution: "@babel/plugin-transform-class-properties@npm:7.29.7" + dependencies: + "@babel/helper-create-class-features-plugin": "npm:^7.29.7" + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/c370700423439aa9f0c1f8c4b97f2ef7c2dc46a1b04ec3b10e83e6bae5e4e2159f56d8e4376c9d669b3cf827650cc3740170a36e3924e3e9970d27fd85f4e48a + languageName: node + linkType: hard + "@babel/plugin-transform-class-static-block@npm:^7.28.3": version: 7.28.3 resolution: "@babel/plugin-transform-class-static-block@npm:7.28.3" @@ -848,7 +954,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:7.28.4, @babel/plugin-transform-classes@npm:^7.25.4, @babel/plugin-transform-classes@npm:^7.28.4": +"@babel/plugin-transform-classes@npm:^7.25.4, @babel/plugin-transform-classes@npm:^7.28.4": version: 7.28.4 resolution: "@babel/plugin-transform-classes@npm:7.28.4" dependencies: @@ -864,7 +970,23 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-computed-properties@npm:^7.24.7, @babel/plugin-transform-computed-properties@npm:^7.27.1": +"@babel/plugin-transform-classes@npm:^7.28.6": + version: 7.29.7 + resolution: "@babel/plugin-transform-classes@npm:7.29.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.29.7" + "@babel/helper-compilation-targets": "npm:^7.29.7" + "@babel/helper-globals": "npm:^7.29.7" + "@babel/helper-plugin-utils": "npm:^7.29.7" + "@babel/helper-replace-supers": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/53a55bc5348d82ca744dbfcfedf33ab79877e609a5308f43976de8c240bd09cee195a535bf54a01b28d7080eebe759735b1c6cf39f252eef469eefc1d838d2a2 + languageName: node + linkType: hard + +"@babel/plugin-transform-computed-properties@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-computed-properties@npm:7.27.1" dependencies: @@ -992,7 +1114,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-function-name@npm:^7.25.1, @babel/plugin-transform-function-name@npm:^7.27.1": +"@babel/plugin-transform-function-name@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-function-name@npm:7.27.1" dependencies: @@ -1016,7 +1138,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-literals@npm:^7.25.2, @babel/plugin-transform-literals@npm:^7.27.1": +"@babel/plugin-transform-literals@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-literals@npm:7.27.1" dependencies: @@ -1027,7 +1149,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-logical-assignment-operators@npm:^7.24.7, @babel/plugin-transform-logical-assignment-operators@npm:^7.28.5": +"@babel/plugin-transform-logical-assignment-operators@npm:^7.28.5": version: 7.28.5 resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.28.5" dependencies: @@ -1073,6 +1195,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-commonjs@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.29.7" + dependencies: + "@babel/helper-module-transforms": "npm:^7.29.7" + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/9791cb524438b2a8ba6cb8715788fa1e202fbecd4e76b3ccab0af0819fd69212b40ae30d72ac377012f7149889f7792ed8bf91e97bbe9113ca9641f8ad3bf332 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-systemjs@npm:^7.28.5": version: 7.28.5 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.28.5" @@ -1122,7 +1256,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-nullish-coalescing-operator@npm:7.27.1, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.24.7, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.27.1": +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.24.7, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.27.1" dependencies: @@ -1133,7 +1267,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-numeric-separator@npm:^7.24.7, @babel/plugin-transform-numeric-separator@npm:^7.27.1": +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.28.6": + version: 7.29.7 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/b0c186fe38bc66830e1be76f06fabbae8a655d3896a841ba5ffa12d6c40bb9c8a6ecd38a7e2196034b1d7470653109b1ceac1ef5e46a5fdc9291d14afa56e0d0 + languageName: node + linkType: hard + +"@babel/plugin-transform-numeric-separator@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-numeric-separator@npm:7.27.1" dependencies: @@ -1144,7 +1289,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-object-rest-spread@npm:^7.24.7, @babel/plugin-transform-object-rest-spread@npm:^7.28.4": +"@babel/plugin-transform-object-rest-spread@npm:^7.28.4": version: 7.28.4 resolution: "@babel/plugin-transform-object-rest-spread@npm:7.28.4" dependencies: @@ -1182,31 +1327,31 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:7.27.1": - version: 7.27.1 - resolution: "@babel/plugin-transform-optional-chaining@npm:7.27.1" +"@babel/plugin-transform-optional-chaining@npm:^7.24.8, @babel/plugin-transform-optional-chaining@npm:^7.27.1, @babel/plugin-transform-optional-chaining@npm:^7.28.5": + version: 7.28.5 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.28.5" dependencies: "@babel/helper-plugin-utils": "npm:^7.27.1" "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.27.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/5b18ff5124e503f0a25d6b195be7351a028b3992d6f2a91fb4037e2a2c386400d66bc1df8f6df0a94c708524f318729e81a95c41906e5a7919a06a43e573a525 + checksum: 10c0/adf5f70b1f9eb0dd6ff3d159a714683af3c910775653e667bd9f864c3dc2dc9872aba95f6c1e5f2a9675067241942f4fd0d641147ef4bf2bd8bc15f1fa0f2ed5 languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.24.8, @babel/plugin-transform-optional-chaining@npm:^7.27.1, @babel/plugin-transform-optional-chaining@npm:^7.28.5": - version: 7.28.5 - resolution: "@babel/plugin-transform-optional-chaining@npm:7.28.5" +"@babel/plugin-transform-optional-chaining@npm:^7.28.6": + version: 7.29.7 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.29.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.27.1" - "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.27.1" + "@babel/helper-plugin-utils": "npm:^7.29.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.29.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/adf5f70b1f9eb0dd6ff3d159a714683af3c910775653e667bd9f864c3dc2dc9872aba95f6c1e5f2a9675067241942f4fd0d641147ef4bf2bd8bc15f1fa0f2ed5 + checksum: 10c0/71feacf9a7083030f4c69bf4e91db75f2fceae28e58c58b63db040006d908e15bc1e85a464f24ad659f17702e91a64eabbff9f1dd555ba33d78bb1af8a1d697a languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.24.7, @babel/plugin-transform-parameters@npm:^7.27.7": +"@babel/plugin-transform-parameters@npm:^7.27.7": version: 7.27.7 resolution: "@babel/plugin-transform-parameters@npm:7.27.7" dependencies: @@ -1374,7 +1519,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-shorthand-properties@npm:7.27.1, @babel/plugin-transform-shorthand-properties@npm:^7.24.7, @babel/plugin-transform-shorthand-properties@npm:^7.27.1": +"@babel/plugin-transform-shorthand-properties@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-shorthand-properties@npm:7.27.1" dependencies: @@ -1385,7 +1530,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.24.7, @babel/plugin-transform-spread@npm:^7.27.1": +"@babel/plugin-transform-spread@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-spread@npm:7.27.1" dependencies: @@ -1397,7 +1542,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-sticky-regex@npm:^7.24.7, @babel/plugin-transform-sticky-regex@npm:^7.27.1": +"@babel/plugin-transform-sticky-regex@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-sticky-regex@npm:7.27.1" dependencies: @@ -1419,7 +1564,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:7.27.1, @babel/plugin-transform-template-literals@npm:^7.27.1": +"@babel/plugin-transform-template-literals@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-template-literals@npm:7.27.1" dependencies: @@ -1456,7 +1601,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typescript@npm:^7.27.1": +"@babel/plugin-transform-typescript@npm:^7.29.7": version: 7.29.7 resolution: "@babel/plugin-transform-typescript@npm:7.29.7" dependencies: @@ -1494,7 +1639,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:7.27.1, @babel/plugin-transform-unicode-regex@npm:^7.24.7, @babel/plugin-transform-unicode-regex@npm:^7.27.1": +"@babel/plugin-transform-unicode-regex@npm:^7.24.7, @babel/plugin-transform-unicode-regex@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-unicode-regex@npm:7.27.1" dependencies: @@ -1627,33 +1772,33 @@ __metadata: languageName: node linkType: hard -"@babel/preset-typescript@npm:7.27.1": - version: 7.27.1 - resolution: "@babel/preset-typescript@npm:7.27.1" +"@babel/preset-typescript@npm:^7.24.7": + version: 7.28.5 + resolution: "@babel/preset-typescript@npm:7.28.5" dependencies: "@babel/helper-plugin-utils": "npm:^7.27.1" "@babel/helper-validator-option": "npm:^7.27.1" "@babel/plugin-syntax-jsx": "npm:^7.27.1" "@babel/plugin-transform-modules-commonjs": "npm:^7.27.1" - "@babel/plugin-transform-typescript": "npm:^7.27.1" + "@babel/plugin-transform-typescript": "npm:^7.28.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/cba6ca793d915f8aff9fe2f13b0dfbf5fd3f2e9a17f17478ec9878e9af0d206dcfe93154b9fd353727f16c1dca7c7a3ceb4943f8d28b216235f106bc0fbbcaa3 + checksum: 10c0/b3d55548854c105085dd80f638147aa8295bc186d70492289242d6c857cb03a6c61ec15186440ea10ed4a71cdde7d495f5eb3feda46273f36b0ac926e8409629 languageName: node linkType: hard -"@babel/preset-typescript@npm:^7.24.7": - version: 7.28.5 - resolution: "@babel/preset-typescript@npm:7.28.5" +"@babel/preset-typescript@npm:^7.28.5": + version: 7.29.7 + resolution: "@babel/preset-typescript@npm:7.29.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.27.1" - "@babel/helper-validator-option": "npm:^7.27.1" - "@babel/plugin-syntax-jsx": "npm:^7.27.1" - "@babel/plugin-transform-modules-commonjs": "npm:^7.27.1" - "@babel/plugin-transform-typescript": "npm:^7.28.5" + "@babel/helper-plugin-utils": "npm:^7.29.7" + "@babel/helper-validator-option": "npm:^7.29.7" + "@babel/plugin-syntax-jsx": "npm:^7.29.7" + "@babel/plugin-transform-modules-commonjs": "npm:^7.29.7" + "@babel/plugin-transform-typescript": "npm:^7.29.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/b3d55548854c105085dd80f638147aa8295bc186d70492289242d6c857cb03a6c61ec15186440ea10ed4a71cdde7d495f5eb3feda46273f36b0ac926e8409629 + checksum: 10c0/40746a23a7ab46c0beb1c02d69883d9ffbe3043685cb3ae5363644391376b9261189fdad317191349e322c2c9bc550b031daa42470a1f8987362e4c56e492194 languageName: node linkType: hard @@ -1664,7 +1809,7 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.25.0, @babel/template@npm:^7.27.1, @babel/template@npm:^7.27.2, @babel/template@npm:^7.3.3": +"@babel/template@npm:^7.27.1, @babel/template@npm:^7.27.2, @babel/template@npm:^7.3.3": version: 7.27.2 resolution: "@babel/template@npm:7.27.2" dependencies: @@ -1675,7 +1820,7 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.29.7": +"@babel/template@npm:^7.28.6, @babel/template@npm:^7.29.7": version: 7.29.7 resolution: "@babel/template@npm:7.29.7" dependencies: @@ -1686,7 +1831,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3, @babel/traverse@npm:^7.25.3, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.28.3, @babel/traverse@npm:^7.28.4, @babel/traverse@npm:^7.28.5": +"@babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.28.3, @babel/traverse@npm:^7.28.4, @babel/traverse@npm:^7.28.5": version: 7.28.5 resolution: "@babel/traverse@npm:7.28.5" dependencies: @@ -1701,7 +1846,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.29.7": +"@babel/traverse@npm:^7.29.0, @babel/traverse@npm:^7.29.7": version: 7.29.7 resolution: "@babel/traverse@npm:7.29.7" dependencies: @@ -1716,7 +1861,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.25.2, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": version: 7.28.5 resolution: "@babel/types@npm:7.28.5" dependencies: @@ -1726,7 +1871,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.29.7": +"@babel/types@npm:^7.29.0, @babel/types@npm:^7.29.7": version: 7.29.7 resolution: "@babel/types@npm:7.29.7" dependencies: @@ -1953,16 +2098,7 @@ __metadata: languageName: node linkType: hard -"@egjs/hammerjs@npm:^2.0.17": - version: 2.0.17 - resolution: "@egjs/hammerjs@npm:2.0.17" - dependencies: - "@types/hammerjs": "npm:^2.0.36" - checksum: 10c0/dbedc15a0e633f887c08394bd636faf6a3abd05726dc0909a0e01209d5860a752d9eca5e512da623aecfabe665f49f1d035de3103eb2f9022c5cea692f9cc9be - languageName: node - linkType: hard - -"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0, @eslint-community/eslint-utils@npm:^4.8.0": +"@eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1": version: 4.9.1 resolution: "@eslint-community/eslint-utils@npm:4.9.1" dependencies: @@ -1973,7 +2109,7 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.12.1": +"@eslint-community/regexpp@npm:^4.12.1, @eslint-community/regexpp@npm:^4.12.2": version: 4.12.2 resolution: "@eslint-community/regexpp@npm:4.12.2" checksum: 10c0/fddcbc66851b308478d04e302a4d771d6917a0b3740dc351513c0da9ca2eab8a1adf99f5e0aa7ab8b13fa0df005c81adeee7e63a92f3effd7d367a163b721c2d @@ -2973,245 +3109,119 @@ __metadata: languageName: node linkType: hard -"@react-native-community/cli-clean@npm:20.0.0": - version: 20.0.0 - resolution: "@react-native-community/cli-clean@npm:20.0.0" +"@react-native-community/cli-clean@npm:20.1.0": + version: 20.1.0 + resolution: "@react-native-community/cli-clean@npm:20.1.0" dependencies: - "@react-native-community/cli-tools": "npm:20.0.0" - chalk: "npm:^4.1.2" + "@react-native-community/cli-tools": "npm:20.1.0" execa: "npm:^5.0.0" fast-glob: "npm:^3.3.2" - checksum: 10c0/cd65907bf2bff82abe8a6616802cf1f756340983e9154a93e771710059ccbf863e45046d2568a6bcb85ef1b4e51b883866ce6371950ec27309a6d1e3fc10cbf4 - languageName: node - linkType: hard - -"@react-native-community/cli-clean@npm:20.0.1": - version: 20.0.1 - resolution: "@react-native-community/cli-clean@npm:20.0.1" - dependencies: - "@react-native-community/cli-tools": "npm:20.0.1" - chalk: "npm:^4.1.2" - execa: "npm:^5.0.0" - fast-glob: "npm:^3.3.2" - checksum: 10c0/7057ca335f8b01ce49a15b2937e84590a6747f8ad267c4dd0df9eb21c3bf3c8c0f8756f06ef8d57cec9d938c6108cd7f289241ff75619828e83c409db3bceb74 - languageName: node - linkType: hard - -"@react-native-community/cli-config-android@npm:20.0.0": - version: 20.0.0 - resolution: "@react-native-community/cli-config-android@npm:20.0.0" - dependencies: - "@react-native-community/cli-tools": "npm:20.0.0" - chalk: "npm:^4.1.2" - fast-glob: "npm:^3.3.2" - fast-xml-parser: "npm:^4.4.1" - checksum: 10c0/79298ecde495e0587585e8d67431e9543ac83392a06e5c8fb736853d199f0aae014858b1d3db81ce3decf58c2172c95c78eeb27e0f2be2b8a5ad43b96331d0ce + picocolors: "npm:^1.1.1" + checksum: 10c0/5d7fce73545154035e55801e9596b634c85bbe7bb04861f3d0787f2a9588b7b6f927709bfce4e45a8d62da03e5a92789281e07dafd29e2db86ca52ca3e5ad49c languageName: node linkType: hard -"@react-native-community/cli-config-android@npm:20.0.1": - version: 20.0.1 - resolution: "@react-native-community/cli-config-android@npm:20.0.1" +"@react-native-community/cli-config-android@npm:20.1.0": + version: 20.1.0 + resolution: "@react-native-community/cli-config-android@npm:20.1.0" dependencies: - "@react-native-community/cli-tools": "npm:20.0.1" - chalk: "npm:^4.1.2" + "@react-native-community/cli-tools": "npm:20.1.0" fast-glob: "npm:^3.3.2" fast-xml-parser: "npm:^4.4.1" - checksum: 10c0/6d7607af8a88339764fdc551d8293ddaf648b8ece3cd309a3ebfac25038e2761c9dad2e10705b3481defbb9e9388dba7073d6e7b76d2297e708934c593ffb96a - languageName: node - linkType: hard - -"@react-native-community/cli-config-apple@npm:20.0.0": - version: 20.0.0 - resolution: "@react-native-community/cli-config-apple@npm:20.0.0" - dependencies: - "@react-native-community/cli-tools": "npm:20.0.0" - chalk: "npm:^4.1.2" - execa: "npm:^5.0.0" - fast-glob: "npm:^3.3.2" - checksum: 10c0/1b11e1dde776ccc3244eb3029eb49239120a89a12ed84bd4957e4e7b81bba4b332ed04a2fb081fd9a9711be65d1a85cf429247fa2daa4010ba4b42b5bf273c2f + picocolors: "npm:^1.1.1" + checksum: 10c0/66e95adf2fb9750a12592a146b034ab4b8ea15aa41efb5d497d6be2f08029a59a0be2450e97de0bf59aa6b66778b3af0bbd693a42b7e7f9f60c9d9b5f966aadc languageName: node linkType: hard -"@react-native-community/cli-config-apple@npm:20.0.1": - version: 20.0.1 - resolution: "@react-native-community/cli-config-apple@npm:20.0.1" +"@react-native-community/cli-config-apple@npm:20.1.0": + version: 20.1.0 + resolution: "@react-native-community/cli-config-apple@npm:20.1.0" dependencies: - "@react-native-community/cli-tools": "npm:20.0.1" - chalk: "npm:^4.1.2" + "@react-native-community/cli-tools": "npm:20.1.0" execa: "npm:^5.0.0" fast-glob: "npm:^3.3.2" - checksum: 10c0/4924ecb3f405f5da2ff2acc968232d451c8862a11e0d877ee898347ecc467e9664aee2cbab2df488250776b171acafde33212a7880bb4e2e0c35b5e82e24c5ea - languageName: node - linkType: hard - -"@react-native-community/cli-config@npm:20.0.0": - version: 20.0.0 - resolution: "@react-native-community/cli-config@npm:20.0.0" - dependencies: - "@react-native-community/cli-tools": "npm:20.0.0" - chalk: "npm:^4.1.2" - cosmiconfig: "npm:^9.0.0" - deepmerge: "npm:^4.3.0" - fast-glob: "npm:^3.3.2" - joi: "npm:^17.2.1" - checksum: 10c0/766364c0c1d0f551a98b86317b74e7942521a05111f6c7e2898341c0413c6a55ae91d184bd549109edd61f02065662ada142788dba1090db200cbec00fdfeede + picocolors: "npm:^1.1.1" + checksum: 10c0/2c7b7116594c7b0035ef7e41e0219fcff664c3883471460ae48f19dd88ec9a26dd5b4bd60e33c054603b3a10efd78570490bc84599ad987e4ffb274b05512351 languageName: node linkType: hard -"@react-native-community/cli-config@npm:20.0.1": - version: 20.0.1 - resolution: "@react-native-community/cli-config@npm:20.0.1" +"@react-native-community/cli-config@npm:20.1.0": + version: 20.1.0 + resolution: "@react-native-community/cli-config@npm:20.1.0" dependencies: - "@react-native-community/cli-tools": "npm:20.0.1" - chalk: "npm:^4.1.2" + "@react-native-community/cli-tools": "npm:20.1.0" cosmiconfig: "npm:^9.0.0" deepmerge: "npm:^4.3.0" fast-glob: "npm:^3.3.2" joi: "npm:^17.2.1" - checksum: 10c0/5c1a78fc8f2e65fa1dcf99e63dbd7b72bd875c3acf5c649b8b917c41cdbeb355617a844db079b2723e835ed2ff9059dec8a4ced03b383005d09c737813ea4e07 - languageName: node - linkType: hard - -"@react-native-community/cli-doctor@npm:20.0.0": - version: 20.0.0 - resolution: "@react-native-community/cli-doctor@npm:20.0.0" - dependencies: - "@react-native-community/cli-config": "npm:20.0.0" - "@react-native-community/cli-platform-android": "npm:20.0.0" - "@react-native-community/cli-platform-apple": "npm:20.0.0" - "@react-native-community/cli-platform-ios": "npm:20.0.0" - "@react-native-community/cli-tools": "npm:20.0.0" - chalk: "npm:^4.1.2" - command-exists: "npm:^1.2.8" - deepmerge: "npm:^4.3.0" - envinfo: "npm:^7.13.0" - execa: "npm:^5.0.0" - node-stream-zip: "npm:^1.9.1" - ora: "npm:^5.4.1" - semver: "npm:^7.5.2" - wcwidth: "npm:^1.0.1" - yaml: "npm:^2.2.1" - checksum: 10c0/843a7e8e5969154ed171616c3c556389d7debc30035bb6a9d6392876d22ef84d390d02673032bb8c8ac164380a97fe9f62971476aacb06ba3a3b849358e4cb99 + picocolors: "npm:^1.1.1" + checksum: 10c0/e3d863ffc5d18aa7b7406908c1360de7ea7a1bab32897e34d0afe184f4682bb5d110d4db0c6ba2c35c4f1330ca4a281b8fc3548e53a2978b8b07ca3beca5765c languageName: node linkType: hard -"@react-native-community/cli-doctor@npm:20.0.1": - version: 20.0.1 - resolution: "@react-native-community/cli-doctor@npm:20.0.1" +"@react-native-community/cli-doctor@npm:20.1.0": + version: 20.1.0 + resolution: "@react-native-community/cli-doctor@npm:20.1.0" dependencies: - "@react-native-community/cli-config": "npm:20.0.1" - "@react-native-community/cli-platform-android": "npm:20.0.1" - "@react-native-community/cli-platform-apple": "npm:20.0.1" - "@react-native-community/cli-platform-ios": "npm:20.0.1" - "@react-native-community/cli-tools": "npm:20.0.1" - chalk: "npm:^4.1.2" + "@react-native-community/cli-config": "npm:20.1.0" + "@react-native-community/cli-platform-android": "npm:20.1.0" + "@react-native-community/cli-platform-apple": "npm:20.1.0" + "@react-native-community/cli-platform-ios": "npm:20.1.0" + "@react-native-community/cli-tools": "npm:20.1.0" command-exists: "npm:^1.2.8" deepmerge: "npm:^4.3.0" envinfo: "npm:^7.13.0" execa: "npm:^5.0.0" node-stream-zip: "npm:^1.9.1" ora: "npm:^5.4.1" + picocolors: "npm:^1.1.1" semver: "npm:^7.5.2" wcwidth: "npm:^1.0.1" yaml: "npm:^2.2.1" - checksum: 10c0/c316fd13a8ed8fa862608c7c9e28a42845dcd50a757954993f036206306beed288fff6e9f29d38e53b3a35277c1e2a9dbec30f7900199517c93c54eac7275206 - languageName: node - linkType: hard - -"@react-native-community/cli-platform-android@npm:20.0.0": - version: 20.0.0 - resolution: "@react-native-community/cli-platform-android@npm:20.0.0" - dependencies: - "@react-native-community/cli-config-android": "npm:20.0.0" - "@react-native-community/cli-tools": "npm:20.0.0" - chalk: "npm:^4.1.2" - execa: "npm:^5.0.0" - logkitty: "npm:^0.7.1" - checksum: 10c0/9c17cbc0661698dd8154286ce893205b91a9f71cb8af8505641fb82ab99217116300fe0dfb8b5e6e9270cf3917c0fb227d45f90322ad7e624d9c65aacf086012 + checksum: 10c0/79762f3e789fe98f21c6f500f6c65df399508979f05174272652a9422295f4dace099e87575776bf209b533a6667070fc355c5b510e14a8a61b31a9a8af7b3be languageName: node linkType: hard -"@react-native-community/cli-platform-android@npm:20.0.1": - version: 20.0.1 - resolution: "@react-native-community/cli-platform-android@npm:20.0.1" +"@react-native-community/cli-platform-android@npm:20.1.0": + version: 20.1.0 + resolution: "@react-native-community/cli-platform-android@npm:20.1.0" dependencies: - "@react-native-community/cli-config-android": "npm:20.0.1" - "@react-native-community/cli-tools": "npm:20.0.1" - chalk: "npm:^4.1.2" + "@react-native-community/cli-config-android": "npm:20.1.0" + "@react-native-community/cli-tools": "npm:20.1.0" execa: "npm:^5.0.0" logkitty: "npm:^0.7.1" - checksum: 10c0/5178f85aa1c7ee321776055f84c4e8413f7daf745da703a9f35f36800d745e6c3aff85262fffce7102cd49b9aa54d47ce25613804682983a8247e5e8a52d93f5 - languageName: node - linkType: hard - -"@react-native-community/cli-platform-apple@npm:20.0.0": - version: 20.0.0 - resolution: "@react-native-community/cli-platform-apple@npm:20.0.0" - dependencies: - "@react-native-community/cli-config-apple": "npm:20.0.0" - "@react-native-community/cli-tools": "npm:20.0.0" - chalk: "npm:^4.1.2" - execa: "npm:^5.0.0" - fast-xml-parser: "npm:^4.4.1" - checksum: 10c0/a6b2296d6291853f59d3c68302676fea54d68fa6626c14727b7db63280761714785fd70a75d7ae036644a7ea5ebd8211235c64d4dec5297565d734a1c5a3b6c9 + picocolors: "npm:^1.1.1" + checksum: 10c0/8b44178a78245085824c3fda9d6753d03cd817772fdde82b1d4f1a89f177f10339822401c0b8ef17e8353f3168c3971ae4cfb0a158cef03c22e55f5dff66179b languageName: node linkType: hard -"@react-native-community/cli-platform-apple@npm:20.0.1": - version: 20.0.1 - resolution: "@react-native-community/cli-platform-apple@npm:20.0.1" +"@react-native-community/cli-platform-apple@npm:20.1.0": + version: 20.1.0 + resolution: "@react-native-community/cli-platform-apple@npm:20.1.0" dependencies: - "@react-native-community/cli-config-apple": "npm:20.0.1" - "@react-native-community/cli-tools": "npm:20.0.1" - chalk: "npm:^4.1.2" + "@react-native-community/cli-config-apple": "npm:20.1.0" + "@react-native-community/cli-tools": "npm:20.1.0" execa: "npm:^5.0.0" fast-xml-parser: "npm:^4.4.1" - checksum: 10c0/2ca9669339f616ba6a6d0df299cd0828fed3497b1d16414be0f92fa46b2395e7ab12c7c0a7bd7c66ce026a100225dde9ba133796813365d0f4dc31681a8a82e7 - languageName: node - linkType: hard - -"@react-native-community/cli-platform-ios@npm:20.0.0": - version: 20.0.0 - resolution: "@react-native-community/cli-platform-ios@npm:20.0.0" - dependencies: - "@react-native-community/cli-platform-apple": "npm:20.0.0" - checksum: 10c0/6eb77946cb3392cc548007723b790796360651e62422095da2bbc8a20b7c8e79d9fc32c0155ba669a10c556e6e7327964d59e5755c64f6099708701e4acf8fd3 - languageName: node - linkType: hard - -"@react-native-community/cli-platform-ios@npm:20.0.1": - version: 20.0.1 - resolution: "@react-native-community/cli-platform-ios@npm:20.0.1" - dependencies: - "@react-native-community/cli-platform-apple": "npm:20.0.1" - checksum: 10c0/9989444c4490413104f5ca7de666f90a97a7b1901d9a301e36242ee80e5f5bdffd2899858c3a0476c6e46bf3b0bd17da87e76970b688bcea90ff6b02bf4a612c + picocolors: "npm:^1.1.1" + checksum: 10c0/2218e172bf13c0bb027f7f2a036a0515f8fe023d98fc65d22d0dab601036c5b461b1af7a5d826df2dd3e86a3894f9531d43e8861746a071ee22c0b33b8e47c29 languageName: node linkType: hard -"@react-native-community/cli-server-api@npm:20.0.0": - version: 20.0.0 - resolution: "@react-native-community/cli-server-api@npm:20.0.0" +"@react-native-community/cli-platform-ios@npm:20.1.0": + version: 20.1.0 + resolution: "@react-native-community/cli-platform-ios@npm:20.1.0" dependencies: - "@react-native-community/cli-tools": "npm:20.0.0" - body-parser: "npm:^1.20.3" - compression: "npm:^1.7.1" - connect: "npm:^3.6.5" - errorhandler: "npm:^1.5.1" - nocache: "npm:^3.0.1" - open: "npm:^6.2.0" - pretty-format: "npm:^29.7.0" - serve-static: "npm:^1.13.1" - ws: "npm:^6.2.3" - checksum: 10c0/105099a3b667978880144eb4e1ca1725c57f1fe6bb6f8d09b189f021ecc1ad70f8e865667ef1d921fb85f1bea8d1da766d713672dfde80d6d2cdde47e800d5a8 + "@react-native-community/cli-platform-apple": "npm:20.1.0" + checksum: 10c0/f5eef9dfbe3dad7dc69f4b3a37ebee9d124f3a649071e0275624f88468a7f506b622646dcbd61ba0435cc35e705badfc287690d6b7c3867c0a86c9a45d4f463e languageName: node linkType: hard -"@react-native-community/cli-server-api@npm:20.0.1": - version: 20.0.1 - resolution: "@react-native-community/cli-server-api@npm:20.0.1" +"@react-native-community/cli-server-api@npm:20.1.0": + version: 20.1.0 + resolution: "@react-native-community/cli-server-api@npm:20.1.0" dependencies: - "@react-native-community/cli-tools": "npm:20.0.1" + "@react-native-community/cli-tools": "npm:20.1.0" body-parser: "npm:^1.20.3" compression: "npm:^1.7.1" connect: "npm:^3.6.5" @@ -3221,111 +3231,59 @@ __metadata: pretty-format: "npm:^29.7.0" serve-static: "npm:^1.13.1" ws: "npm:^6.2.3" - checksum: 10c0/29dd846b8836059333e4dc83e8867b3d65916af3a5c3287933ff04c73a86aeb078e0f1ccdf42aaf2660b654b9094ed4c6b0724d229067fb1966ae12715e3d7b2 - languageName: node - linkType: hard - -"@react-native-community/cli-tools@npm:20.0.0": - version: 20.0.0 - resolution: "@react-native-community/cli-tools@npm:20.0.0" - dependencies: - "@vscode/sudo-prompt": "npm:^9.0.0" - appdirsjs: "npm:^1.2.4" - chalk: "npm:^4.1.2" - execa: "npm:^5.0.0" - find-up: "npm:^5.0.0" - launch-editor: "npm:^2.9.1" - mime: "npm:^2.4.1" - ora: "npm:^5.4.1" - prompts: "npm:^2.4.2" - semver: "npm:^7.5.2" - checksum: 10c0/3a379558f2673de68d872d740754e242d9dd84dd0a837f94910936faad632f35fc9b61dd9d256e635391815da7a4cbc42c3ca918371d60fb4d5a3b4acea6bffd + checksum: 10c0/d83c9bbff36fb84201478ed8efc17c80f1474cb0346a2577eee7299407e9e34892c213e2316062a2c59d47ebfe9f0c8215b82cd1abb05c85fe7702a94a52cdf5 languageName: node linkType: hard -"@react-native-community/cli-tools@npm:20.0.1": - version: 20.0.1 - resolution: "@react-native-community/cli-tools@npm:20.0.1" +"@react-native-community/cli-tools@npm:20.1.0": + version: 20.1.0 + resolution: "@react-native-community/cli-tools@npm:20.1.0" dependencies: "@vscode/sudo-prompt": "npm:^9.0.0" appdirsjs: "npm:^1.2.4" - chalk: "npm:^4.1.2" execa: "npm:^5.0.0" find-up: "npm:^5.0.0" launch-editor: "npm:^2.9.1" mime: "npm:^2.4.1" ora: "npm:^5.4.1" + picocolors: "npm:^1.1.1" prompts: "npm:^2.4.2" semver: "npm:^7.5.2" - checksum: 10c0/d6f083b2592250b11c114e3abe6ef38c70653aac1316750b8ba9dcbc81b99c25879999164498511128b8ebcaa083aa0427181225b178d70ca76b4c5f466f952f - languageName: node - linkType: hard - -"@react-native-community/cli-types@npm:20.0.0": - version: 20.0.0 - resolution: "@react-native-community/cli-types@npm:20.0.0" - dependencies: - joi: "npm:^17.2.1" - checksum: 10c0/328f623579b3b8e797d833aeb76fad86a75f9da84b45da1d9ffe4c6429a57cd3440d401d3448d15c2b44b69137459033522e3dff87767f887772d8f6055dccc9 + checksum: 10c0/993f5dfbc1ae6301e616269896b2e5ecc29888cc8a12325881148b546b07d45572bd065e392ceaad32f86067e31b0eabedbe71fac3b1b820cd489ab86e56094d languageName: node linkType: hard -"@react-native-community/cli-types@npm:20.0.1": - version: 20.0.1 - resolution: "@react-native-community/cli-types@npm:20.0.1" +"@react-native-community/cli-types@npm:20.1.0": + version: 20.1.0 + resolution: "@react-native-community/cli-types@npm:20.1.0" dependencies: joi: "npm:^17.2.1" - checksum: 10c0/dd1e2013594fa8f30ff0f1494bb67ccb62fd9a5d4933294e8e193f8074362559f863eaddacb6e127505490929aef4f59c7019032606ca21964bdbf205fc4171d + checksum: 10c0/247deebf26b435c0cbabfb7dd7de09a4e79c544ee72a2ef2817268108f7c085dab4d54c7fbc8111ad89360e13227f2041c5eac208971c251da332e4d33311a7b languageName: node linkType: hard -"@react-native-community/cli@npm:20.0.0": - version: 20.0.0 - resolution: "@react-native-community/cli@npm:20.0.0" +"@react-native-community/cli@npm:20.1.0": + version: 20.1.0 + resolution: "@react-native-community/cli@npm:20.1.0" dependencies: - "@react-native-community/cli-clean": "npm:20.0.0" - "@react-native-community/cli-config": "npm:20.0.0" - "@react-native-community/cli-doctor": "npm:20.0.0" - "@react-native-community/cli-server-api": "npm:20.0.0" - "@react-native-community/cli-tools": "npm:20.0.0" - "@react-native-community/cli-types": "npm:20.0.0" - chalk: "npm:^4.1.2" - commander: "npm:^9.4.1" - deepmerge: "npm:^4.3.0" - execa: "npm:^5.0.0" - find-up: "npm:^5.0.0" - fs-extra: "npm:^8.1.0" - graceful-fs: "npm:^4.1.3" - prompts: "npm:^2.4.2" - semver: "npm:^7.5.2" - bin: - rnc-cli: build/bin.js - checksum: 10c0/ccdbc8ba1f678274772237941993e68143707240762194842fa756fc7d53bcee9114e0b7d3eefaa750ccba41189909fbd7ceaa3a99f5ae781ccf2e98c5ff10b6 - languageName: node - linkType: hard - -"@react-native-community/cli@npm:20.0.1": - version: 20.0.1 - resolution: "@react-native-community/cli@npm:20.0.1" - dependencies: - "@react-native-community/cli-clean": "npm:20.0.1" - "@react-native-community/cli-config": "npm:20.0.1" - "@react-native-community/cli-doctor": "npm:20.0.1" - "@react-native-community/cli-server-api": "npm:20.0.1" - "@react-native-community/cli-tools": "npm:20.0.1" - "@react-native-community/cli-types": "npm:20.0.1" - chalk: "npm:^4.1.2" + "@react-native-community/cli-clean": "npm:20.1.0" + "@react-native-community/cli-config": "npm:20.1.0" + "@react-native-community/cli-doctor": "npm:20.1.0" + "@react-native-community/cli-server-api": "npm:20.1.0" + "@react-native-community/cli-tools": "npm:20.1.0" + "@react-native-community/cli-types": "npm:20.1.0" commander: "npm:^9.4.1" deepmerge: "npm:^4.3.0" execa: "npm:^5.0.0" find-up: "npm:^5.0.0" fs-extra: "npm:^8.1.0" graceful-fs: "npm:^4.1.3" + picocolors: "npm:^1.1.1" prompts: "npm:^2.4.2" semver: "npm:^7.5.2" bin: rnc-cli: build/bin.js - checksum: 10c0/70fab833b60ba73d8afb95715ae390695b2fe4831bd70ba65cc3362bf9790f95ffe789ff019d14faf951e1d153dea3e31527efc73fc1f4ffe6f03c5b6583835a + checksum: 10c0/f3fa43fc8c170ba291ae81e796f4ac35956e143bf1094f15f91061b93835ed0665749ca11da91df7fbfcdd96ee32fc16da6cab60adef1a21022195aa1947bd5d languageName: node linkType: hard @@ -3336,26 +3294,26 @@ __metadata: languageName: node linkType: hard -"@react-native/assets-registry@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/assets-registry@npm:0.81.1" - checksum: 10c0/44ee4e31bd08770eb59116097c38e8146eb6821a4bf932e7733c04d56a1e8b0263befd8642477824fc836b61c31d99405cbf36b15b4e131dcd82ca21f171db66 +"@react-native/assets-registry@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/assets-registry@npm:0.86.0" + checksum: 10c0/9cd7e96d99bb5b1da0b83ad4362966749c367081bd4272ca65c8ce4933cea9caad6bbf1d6e404832447c9b200495d707809bbb1db2fd5d9a6f7ae25a50a96bf8 languageName: node linkType: hard -"@react-native/babel-plugin-codegen@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/babel-plugin-codegen@npm:0.81.1" +"@react-native/babel-plugin-codegen@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/babel-plugin-codegen@npm:0.86.0" dependencies: - "@babel/traverse": "npm:^7.25.3" - "@react-native/codegen": "npm:0.81.1" - checksum: 10c0/e039e517454cf261c34f3300a1cb7475a8f11f2c2c997cad9688c9878200199fc716ba8d26b3f6c0e614b72aa89ae91dba5e81ffdaf2789e3cd7dafd69e9fa6d + "@babel/traverse": "npm:^7.29.0" + "@react-native/codegen": "npm:0.86.0" + checksum: 10c0/b9263b7fd6a40411639e2343e17454fa9e988e651f44ec9149bd0c3a7945166863713b01d21249b81e62bd9ec67e356fbbee860e7b7f8479df26310b90d2bd4e languageName: node linkType: hard -"@react-native/babel-preset@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/babel-preset@npm:0.81.1" +"@react-native/babel-preset@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/babel-preset@npm:0.86.0" dependencies: "@babel/core": "npm:^7.25.2" "@babel/plugin-proposal-export-default-from": "npm:^7.24.7" @@ -3363,27 +3321,19 @@ __metadata: "@babel/plugin-syntax-export-default-from": "npm:^7.24.7" "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" - "@babel/plugin-transform-arrow-functions": "npm:^7.24.7" "@babel/plugin-transform-async-generator-functions": "npm:^7.25.4" "@babel/plugin-transform-async-to-generator": "npm:^7.24.7" "@babel/plugin-transform-block-scoping": "npm:^7.25.0" "@babel/plugin-transform-class-properties": "npm:^7.25.4" "@babel/plugin-transform-classes": "npm:^7.25.4" - "@babel/plugin-transform-computed-properties": "npm:^7.24.7" "@babel/plugin-transform-destructuring": "npm:^7.24.8" "@babel/plugin-transform-flow-strip-types": "npm:^7.25.2" "@babel/plugin-transform-for-of": "npm:^7.24.7" - "@babel/plugin-transform-function-name": "npm:^7.25.1" - "@babel/plugin-transform-literals": "npm:^7.25.2" - "@babel/plugin-transform-logical-assignment-operators": "npm:^7.24.7" "@babel/plugin-transform-modules-commonjs": "npm:^7.24.8" "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.24.7" "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.24.7" - "@babel/plugin-transform-numeric-separator": "npm:^7.24.7" - "@babel/plugin-transform-object-rest-spread": "npm:^7.24.7" "@babel/plugin-transform-optional-catch-binding": "npm:^7.24.7" "@babel/plugin-transform-optional-chaining": "npm:^7.24.8" - "@babel/plugin-transform-parameters": "npm:^7.24.7" "@babel/plugin-transform-private-methods": "npm:^7.24.7" "@babel/plugin-transform-private-property-in-object": "npm:^7.24.7" "@babel/plugin-transform-react-display-name": "npm:^7.24.7" @@ -3392,186 +3342,209 @@ __metadata: "@babel/plugin-transform-react-jsx-source": "npm:^7.24.7" "@babel/plugin-transform-regenerator": "npm:^7.24.7" "@babel/plugin-transform-runtime": "npm:^7.24.7" - "@babel/plugin-transform-shorthand-properties": "npm:^7.24.7" - "@babel/plugin-transform-spread": "npm:^7.24.7" - "@babel/plugin-transform-sticky-regex": "npm:^7.24.7" "@babel/plugin-transform-typescript": "npm:^7.25.2" "@babel/plugin-transform-unicode-regex": "npm:^7.24.7" - "@babel/template": "npm:^7.25.0" - "@react-native/babel-plugin-codegen": "npm:0.81.1" - babel-plugin-syntax-hermes-parser: "npm:0.29.1" + "@react-native/babel-plugin-codegen": "npm:0.86.0" + babel-plugin-syntax-hermes-parser: "npm:0.36.0" babel-plugin-transform-flow-enums: "npm:^0.0.2" react-refresh: "npm:^0.14.0" peerDependencies: "@babel/core": "*" - checksum: 10c0/d04b9c1d445d84a6c4793605f411a9cfc296f6e4e09d82268db25e715e7ba89136340267f125258d742e732d2dfe86608f3b011d05dc9117f9ee1870d244ba30 + checksum: 10c0/cf86875fa72d9bdd0b6ac3819b7a683764f154ff8df5144cce9cc60dd734d9d100bde563acbb48104e0033650dcc28d968eb985ff59e47349dc6861169df02f6 languageName: node linkType: hard -"@react-native/codegen@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/codegen@npm:0.81.1" +"@react-native/codegen@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/codegen@npm:0.86.0" dependencies: "@babel/core": "npm:^7.25.2" - "@babel/parser": "npm:^7.25.3" - glob: "npm:^7.1.1" - hermes-parser: "npm:0.29.1" + "@babel/parser": "npm:^7.29.0" + hermes-parser: "npm:0.36.0" invariant: "npm:^2.2.4" nullthrows: "npm:^1.1.1" + tinyglobby: "npm:^0.2.15" yargs: "npm:^17.6.2" peerDependencies: "@babel/core": "*" - checksum: 10c0/d0910a99e4174f7aaec5328a92a56c70fa0acf84062282f56dcf1ac3389e9d5b21a16b049049a79648e1b632f11872c2dca6be9670593b92ccb656922485ca2b + checksum: 10c0/22ca5657595438f5e464223a65b25567b3e7106995c8ae59c5853d8cdb3df59065db54c4cb74e958bbe2930b0aded8cd518c2fc1ef13a4740a05fb1faa3ae160 languageName: node linkType: hard -"@react-native/community-cli-plugin@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/community-cli-plugin@npm:0.81.1" +"@react-native/community-cli-plugin@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/community-cli-plugin@npm:0.86.0" dependencies: - "@react-native/dev-middleware": "npm:0.81.1" + "@react-native/dev-middleware": "npm:0.86.0" debug: "npm:^4.4.0" invariant: "npm:^2.2.4" - metro: "npm:^0.83.1" - metro-config: "npm:^0.83.1" - metro-core: "npm:^0.83.1" + metro: "npm:^0.84.3" + metro-config: "npm:^0.84.3" + metro-core: "npm:^0.84.3" semver: "npm:^7.1.3" peerDependencies: "@react-native-community/cli": "*" - "@react-native/metro-config": "*" + "@react-native/metro-config": 0.86.0 peerDependenciesMeta: "@react-native-community/cli": optional: true "@react-native/metro-config": optional: true - checksum: 10c0/96811ba146d1a591aa2e96abb6cc2ce06a3076457b2bba3cd3bc2f792f81c6d540378dd22af09e632a7dfaecc3b45a179224ab453fe71a3ddc8d29d060b87451 + checksum: 10c0/62c70f1e99eb48dad8516fed9a15415e8de85ee76018f00231f4473e9fedbfc8c9b4932fc531b9307c3a6d681278b61fa7e63ba2fa250628451662f815a3366e languageName: node linkType: hard -"@react-native/debugger-frontend@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/debugger-frontend@npm:0.81.1" - checksum: 10c0/282c957fa6af485496b6e5fbc16266f8463bb2474cdaa6c3e41b4a061ffc6432bd3d18ceedfba35ff47648876c71021abfbb17f0f8e0a4d7f8edec4eb94bb811 +"@react-native/debugger-frontend@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/debugger-frontend@npm:0.86.0" + checksum: 10c0/bc86a765dfbcaae35f501244d5a52aa0723196fe34ba177c2e3398491df6af1aa084ace044fde668e05bfec3daf15f735c20c79a8b98bd578b38891913e337e2 languageName: node linkType: hard -"@react-native/dev-middleware@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/dev-middleware@npm:0.81.1" +"@react-native/debugger-shell@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/debugger-shell@npm:0.86.0" + dependencies: + cross-spawn: "npm:^7.0.6" + debug: "npm:^4.4.0" + fb-dotslash: "npm:0.5.8" + checksum: 10c0/4cc7626b826195403c9b9ac8ad1ed9938ad83858424aa59b980882349966aa0fa6b509066006bc7c7be2c0d20767fbc7d75c8df0007f853b57646c9157fcb3e7 + languageName: node + linkType: hard + +"@react-native/dev-middleware@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/dev-middleware@npm:0.86.0" dependencies: "@isaacs/ttlcache": "npm:^1.4.1" - "@react-native/debugger-frontend": "npm:0.81.1" + "@react-native/debugger-frontend": "npm:0.86.0" + "@react-native/debugger-shell": "npm:0.86.0" chrome-launcher: "npm:^0.15.2" - chromium-edge-launcher: "npm:^0.2.0" + chromium-edge-launcher: "npm:^0.3.0" connect: "npm:^3.6.5" debug: "npm:^4.4.0" invariant: "npm:^2.2.4" nullthrows: "npm:^1.1.1" open: "npm:^7.0.3" serve-static: "npm:^1.16.2" - ws: "npm:^6.2.3" - checksum: 10c0/ab5d09709efe4ede4f09e94923208e82636d7be5b6b9871c4daf58c728bda7be1dcc0dbee65897fc9429d381cc148bcd595116d0e822fba685d0b92ed8309dbc + ws: "npm:^7.5.10" + checksum: 10c0/14e704d723d7c3eaae94552251b50b31b3769dbfbc2b48f6cabe422b1bfc640dae20751997d8fd8bbe26a22c6865a46083e4d3621d7b200b19c79e515ef54bb9 languageName: node linkType: hard -"@react-native/eslint-config@npm:^0.81.1": - version: 0.81.5 - resolution: "@react-native/eslint-config@npm:0.81.5" +"@react-native/eslint-config@npm:^0.86.0": + version: 0.86.0 + resolution: "@react-native/eslint-config@npm:0.86.0" dependencies: "@babel/core": "npm:^7.25.2" "@babel/eslint-parser": "npm:^7.25.1" - "@react-native/eslint-plugin": "npm:0.81.5" - "@typescript-eslint/eslint-plugin": "npm:^7.1.1" - "@typescript-eslint/parser": "npm:^7.1.1" + "@react-native/eslint-plugin": "npm:0.86.0" + "@typescript-eslint/eslint-plugin": "npm:^8.36.0" + "@typescript-eslint/parser": "npm:^8.36.0" eslint-config-prettier: "npm:^8.5.0" eslint-plugin-eslint-comments: "npm:^3.2.0" eslint-plugin-ft-flow: "npm:^2.0.1" - eslint-plugin-jest: "npm:^27.9.0" - eslint-plugin-react: "npm:^7.30.1" - eslint-plugin-react-hooks: "npm:^5.2.0" - eslint-plugin-react-native: "npm:^4.0.0" + eslint-plugin-jest: "npm:^29.0.1" + eslint-plugin-react: "npm:^7.37.5" + eslint-plugin-react-hooks: "npm:^7.0.1" + eslint-plugin-react-native: "npm:^5.0.0" peerDependencies: - eslint: ">=8" + eslint: ^8.0.0 || ^9.0.0 prettier: ">=2" - checksum: 10c0/86847d8277136c3ff6d59a9e4b5408a56e6297790f460a2f90e130afe365f7cdcabd1a32c893cb59f3616a5a425b04d7008120771d3c67de71b36945d3da70c5 + checksum: 10c0/51b3ab96620c8b4ebe14887b9455074de5dbecf87c8860fa065be13a1070a2f69270ecd7eb3a44e215cb49c1eeaa89204e9511606e7c6b452dbb87a1d2bedf3d languageName: node linkType: hard -"@react-native/eslint-plugin@npm:0.81.5": - version: 0.81.5 - resolution: "@react-native/eslint-plugin@npm:0.81.5" - checksum: 10c0/fe95aee1b8b34c53be0b6f04d2e6a650790f220607ecfa2a5e48d18c90adfbd7117f605564d192a7dc6f675cc9745596902496b2541c5a31bef8d103412ad06e +"@react-native/eslint-plugin@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/eslint-plugin@npm:0.86.0" + checksum: 10c0/4d8bd7451d75dc572369ab2769647b5ec624bbb13e4a924a35cd526ee57507e3667b2eeedfd79568073dc2fa9db598788fc81b39e0b071397fc854ffd36a3ee5 languageName: node linkType: hard -"@react-native/gradle-plugin@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/gradle-plugin@npm:0.81.1" - checksum: 10c0/875317017d583ff17c2ef17bcf39399d13abc41921f9659c9f62e9308e344202430a3051ac06e62843d1be0194e2acef0df660ba35d7a9bfc3607c43e9162ebd +"@react-native/gradle-plugin@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/gradle-plugin@npm:0.86.0" + checksum: 10c0/02d8f7ae14c3163a6acf7098b1227a7ffd66cbfde96b6b151a02739bfbc27f123e023bd3411383e9a7a33f18e4d4d82100448615b8955a0ff95e8dee5d1ef4e8 languageName: node linkType: hard -"@react-native/js-polyfills@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/js-polyfills@npm:0.81.1" - checksum: 10c0/91b970ce9177e5dc8a5cd654036c2d912b322e9da09c618190fd8f5d9ef2ecac2ee43004b58c73bd93ec3035156c2707d3097755b8c943079ae85c5c38bdc1b8 +"@react-native/jest-preset@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/jest-preset@npm:0.86.0" + dependencies: + "@jest/create-cache-key-function": "npm:^29.7.0" + "@react-native/js-polyfills": "npm:0.86.0" + babel-jest: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + regenerator-runtime: "npm:^0.13.2" + peerDependencies: + react: ^19.2.3 + checksum: 10c0/34f6ffe9c6cedbb23eaeffa801a3dfea75034740dca2c7685aa1bbef42a5f12aa735c17e041142a4551176410b641c35656861c94827e73341a39d40b20b5fbb languageName: node linkType: hard -"@react-native/metro-babel-transformer@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/metro-babel-transformer@npm:0.81.1" +"@react-native/js-polyfills@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/js-polyfills@npm:0.86.0" + checksum: 10c0/63379d40d37b09fe371cc4f8a60304479ee59667a032072dded0b1e475519ff17f27bbaa39fcd463949ea9068c65eb045d5d4639ede8236a241f6e3cd295174c + languageName: node + linkType: hard + +"@react-native/metro-babel-transformer@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/metro-babel-transformer@npm:0.86.0" dependencies: "@babel/core": "npm:^7.25.2" - "@react-native/babel-preset": "npm:0.81.1" - hermes-parser: "npm:0.29.1" + "@react-native/babel-preset": "npm:0.86.0" + hermes-parser: "npm:0.36.0" nullthrows: "npm:^1.1.1" peerDependencies: "@babel/core": "*" - checksum: 10c0/8604e71343af7dd53fc414d837b27e976dcd919ce9a2d78bab2c9d40c0ac231ce8fb3254fdfbd1d486c0b105e6afb20b8a669f28facf1e23e79420b27c95d55f + checksum: 10c0/8b4ab63d32f28f3663204110d37c50a8dd82eef622d57fb236f3d05b69bdfc1fe277f7572e4cf9135790921922f35fe4aea5f7c183fe17cd2ead19ed8e1db5d1 languageName: node linkType: hard -"@react-native/metro-config@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/metro-config@npm:0.81.1" +"@react-native/metro-config@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/metro-config@npm:0.86.0" dependencies: - "@react-native/js-polyfills": "npm:0.81.1" - "@react-native/metro-babel-transformer": "npm:0.81.1" - metro-config: "npm:^0.83.1" - metro-runtime: "npm:^0.83.1" - checksum: 10c0/117d3a39a5cb7fa277aba05b8f353a66ea913220ba1ec513cf89b20419b15bba4191e195289f206b2d04d42fd63728988f2e02319caae69e20e271a1ffcffc05 + "@react-native/js-polyfills": "npm:0.86.0" + "@react-native/metro-babel-transformer": "npm:0.86.0" + metro-config: "npm:^0.84.3" + metro-runtime: "npm:^0.84.3" + checksum: 10c0/92c043b8e4d959a3d8c0aa56940a9ff455c9de21bb6fe2a799fb5a847e45587d9783c3b55e3254f2b17fa71dfc66fff30ce0a70505e0d699dbbf2c76e0ff539c languageName: node linkType: hard -"@react-native/normalize-colors@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/normalize-colors@npm:0.81.1" - checksum: 10c0/67abd4b6e1a3871bd2f3a548bc6b9643245c0706e2d86a35de3fddd59b6fd93d0db2f5dbca4cdb0aa0104f6a9364705d200cc6a784e67acda10674d5900ecc9f +"@react-native/normalize-colors@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/normalize-colors@npm:0.86.0" + checksum: 10c0/d836506a642e1c636015fca23c84ca372b10fb1457df98f95e8bc5716399292d5740b94efe13f9be08c21323ddc3c2c45205d316f0f5440f24e5919b5075f69a languageName: node linkType: hard -"@react-native/typescript-config@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/typescript-config@npm:0.81.1" - checksum: 10c0/7a7bcf658ba1728c12515013821692e5303f2e31d6589e0df9ba525cf877e8429ee6dbb6f79bb9a1a9d38d93f2bf3bd8e38369a57e760abe12d0e55cceb398e7 +"@react-native/typescript-config@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/typescript-config@npm:0.86.0" + checksum: 10c0/66020e6d945e863847639b4932ef22082dad8ba4e345fe785b85987ae18a6cb56b31539c3502731238580ad96a9b1de32fac55e2e245debf693f039ec36fcffc languageName: node linkType: hard -"@react-native/virtualized-lists@npm:0.81.1": - version: 0.81.1 - resolution: "@react-native/virtualized-lists@npm:0.81.1" +"@react-native/virtualized-lists@npm:0.86.0": + version: 0.86.0 + resolution: "@react-native/virtualized-lists@npm:0.86.0" dependencies: invariant: "npm:^2.2.4" nullthrows: "npm:^1.1.1" peerDependencies: - "@types/react": ^19.1.0 + "@types/react": ^19.2.0 react: "*" - react-native: "*" + react-native: 0.86.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/1cf1c39afdd60c8d72a53250aa0284fcba1680b9218cb287b5ca6a4f1776e0200c6769bbf3e3c75dfe28a05a8797b9f7b562b1699de379c7f438cf2216270fb4 + checksum: 10c0/df5006e01e2f527f4fd46b93db17f3dbf8fd18c832e4ad9b1b43c544a83b4b2fca34ba72114ad9679968043e7fa6f9fe352bcee250799b9b973d0998b446ca50 languageName: node linkType: hard @@ -3831,13 +3804,6 @@ __metadata: languageName: node linkType: hard -"@types/hammerjs@npm:^2.0.36": - version: 2.0.46 - resolution: "@types/hammerjs@npm:2.0.46" - checksum: 10c0/f3c1cb20dc2f0523f7b8c76065078544d50d8ae9b0edc1f62fed657210ed814266ff2dfa835d2c157a075991001eec3b64c88bf92e3e6e895c0db78d05711d06 - languageName: node - linkType: hard - "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": version: 2.0.6 resolution: "@types/istanbul-lib-coverage@npm:2.0.6" @@ -3873,7 +3839,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.9": +"@types/json-schema@npm:^7.0.15": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db @@ -3912,19 +3878,21 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:^19.1.0": - version: 19.2.7 - resolution: "@types/react@npm:19.2.7" +"@types/react-test-renderer@npm:^19.1.0": + version: 19.1.0 + resolution: "@types/react-test-renderer@npm:19.1.0" dependencies: - csstype: "npm:^3.2.2" - checksum: 10c0/a7b75f1f9fcb34badd6f84098be5e35a0aeca614bc91f93d2698664c0b2ba5ad128422bd470ada598238cebe4f9e604a752aead7dc6f5a92261d0c7f9b27cfd1 + "@types/react": "npm:*" + checksum: 10c0/799654e430df10aeaf267d71507fb64ec151359ead7e3774111bfd4abce7e0911dba461811195c06c22a6d17496ea92537d3185320ff4112fe29954cad1b9152 languageName: node linkType: hard -"@types/semver@npm:^7.3.12": - version: 7.7.1 - resolution: "@types/semver@npm:7.7.1" - checksum: 10c0/c938aef3bf79a73f0f3f6037c16e2e759ff40c54122ddf0b2583703393d8d3127130823facb880e694caa324eb6845628186aac1997ee8b31dc2d18fafe26268 +"@types/react@npm:*, @types/react@npm:^19.2.0": + version: 19.2.17 + resolution: "@types/react@npm:19.2.17" + dependencies: + csstype: "npm:^3.2.2" + checksum: 10c0/bc2c4af96b3e480604424de70d5ebda90c5f4b485df471858c0bc2d7d70364b606ec3c4d8579f94f01aa0c6c0591f56bcf14cba5689f5eea4b74250ccdc3a232 languageName: node linkType: hard @@ -3951,184 +3919,138 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^7.1.1": - version: 7.18.0 - resolution: "@typescript-eslint/eslint-plugin@npm:7.18.0" +"@typescript-eslint/eslint-plugin@npm:^8.36.0": + version: 8.63.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.63.0" dependencies: - "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:7.18.0" - "@typescript-eslint/type-utils": "npm:7.18.0" - "@typescript-eslint/utils": "npm:7.18.0" - "@typescript-eslint/visitor-keys": "npm:7.18.0" - graphemer: "npm:^1.4.0" - ignore: "npm:^5.3.1" + "@eslint-community/regexpp": "npm:^4.12.2" + "@typescript-eslint/scope-manager": "npm:8.63.0" + "@typescript-eslint/type-utils": "npm:8.63.0" + "@typescript-eslint/utils": "npm:8.63.0" + "@typescript-eslint/visitor-keys": "npm:8.63.0" + ignore: "npm:^7.0.5" natural-compare: "npm:^1.4.0" - ts-api-utils: "npm:^1.3.0" + ts-api-utils: "npm:^2.5.0" peerDependencies: - "@typescript-eslint/parser": ^7.0.0 - eslint: ^8.56.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/2b37948fa1b0dab77138909dabef242a4d49ab93e4019d4ef930626f0a7d96b03e696cd027fa0087881c20e73be7be77c942606b4a76fa599e6b37f6985304c3 + "@typescript-eslint/parser": ^8.63.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/906a406d85ad80cbe06cb688d4382c34dfb36ebfbd710814f7f9651265b88cf53684ee5275a402eb0eb1243628c504019714f00c1331e54d499cdc7d7c14b5ba languageName: node linkType: hard -"@typescript-eslint/parser@npm:^7.1.1": - version: 7.18.0 - resolution: "@typescript-eslint/parser@npm:7.18.0" +"@typescript-eslint/parser@npm:^8.36.0": + version: 8.63.0 + resolution: "@typescript-eslint/parser@npm:8.63.0" dependencies: - "@typescript-eslint/scope-manager": "npm:7.18.0" - "@typescript-eslint/types": "npm:7.18.0" - "@typescript-eslint/typescript-estree": "npm:7.18.0" - "@typescript-eslint/visitor-keys": "npm:7.18.0" - debug: "npm:^4.3.4" + "@typescript-eslint/scope-manager": "npm:8.63.0" + "@typescript-eslint/types": "npm:8.63.0" + "@typescript-eslint/typescript-estree": "npm:8.63.0" + "@typescript-eslint/visitor-keys": "npm:8.63.0" + debug: "npm:^4.4.3" peerDependencies: - eslint: ^8.56.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/370e73fca4278091bc1b657f85e7d74cd52b24257ea20c927a8e17546107ce04fbf313fec99aed0cc2a145ddbae1d3b12e9cc2c1320117636dc1281bcfd08059 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/28fd1db23ff16627936a0d630c6761df1d17046147b6e0ce6a144d60c0fbeddc756c3208e552f9e53c4d6a35f554205f7de3184b1ab45220a2fbc3bb8d1ac62b languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/scope-manager@npm:5.62.0" +"@typescript-eslint/project-service@npm:8.63.0": + version: 8.63.0 + resolution: "@typescript-eslint/project-service@npm:8.63.0" dependencies: - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/visitor-keys": "npm:5.62.0" - checksum: 10c0/861253235576c1c5c1772d23cdce1418c2da2618a479a7de4f6114a12a7ca853011a1e530525d0931c355a8fd237b9cd828fac560f85f9623e24054fd024726f + "@typescript-eslint/tsconfig-utils": "npm:^8.63.0" + "@typescript-eslint/types": "npm:^8.63.0" + debug: "npm:^4.4.3" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/d49ce51b128d83a0e87e01d7a30f2295d77713d913abddc817df7ac3aeb1333b5dbb0c634ad0d0941f51cdd84ad86a036178b90bb370aa819ff59082377ca0ee languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:7.18.0": - version: 7.18.0 - resolution: "@typescript-eslint/scope-manager@npm:7.18.0" +"@typescript-eslint/scope-manager@npm:8.63.0": + version: 8.63.0 + resolution: "@typescript-eslint/scope-manager@npm:8.63.0" dependencies: - "@typescript-eslint/types": "npm:7.18.0" - "@typescript-eslint/visitor-keys": "npm:7.18.0" - checksum: 10c0/038cd58c2271de146b3a594afe2c99290034033326d57ff1f902976022c8b0138ffd3cb893ae439ae41003b5e4bcc00cabf6b244ce40e8668f9412cc96d97b8e + "@typescript-eslint/types": "npm:8.63.0" + "@typescript-eslint/visitor-keys": "npm:8.63.0" + checksum: 10c0/394245a25f852170adbdaaca9364d5d36a0e97e69e691e5594d5a3ced892a7eb78cf6e7a17cbc095490a168b60bb3ad6f6d48cd167c4b9cbc568bf0f785ab926 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:7.18.0": - version: 7.18.0 - resolution: "@typescript-eslint/type-utils@npm:7.18.0" - dependencies: - "@typescript-eslint/typescript-estree": "npm:7.18.0" - "@typescript-eslint/utils": "npm:7.18.0" - debug: "npm:^4.3.4" - ts-api-utils: "npm:^1.3.0" +"@typescript-eslint/tsconfig-utils@npm:8.63.0, @typescript-eslint/tsconfig-utils@npm:^8.63.0": + version: 8.63.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.63.0" peerDependencies: - eslint: ^8.56.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/ad92a38007be620f3f7036f10e234abdc2fdc518787b5a7227e55fd12896dacf56e8b34578723fbf9bea8128df2510ba8eb6739439a3879eda9519476d5783fd - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/types@npm:5.62.0" - checksum: 10c0/7febd3a7f0701c0b927e094f02e82d8ee2cada2b186fcb938bc2b94ff6fbad88237afc304cbaf33e82797078bbbb1baf91475f6400912f8b64c89be79bfa4ddf - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:7.18.0": - version: 7.18.0 - resolution: "@typescript-eslint/types@npm:7.18.0" - checksum: 10c0/eb7371ac55ca77db8e59ba0310b41a74523f17e06f485a0ef819491bc3dd8909bb930120ff7d30aaf54e888167e0005aa1337011f3663dc90fb19203ce478054 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/378a220fa5d0aaaf38887d667d1ddf8addfb7601af491230e77e32773158f9aad7723b8e25cebf0f95debc2217ca7b9ddf4e9ebd506e319fe1f9fe4244274013 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" +"@typescript-eslint/type-utils@npm:8.63.0": + version: 8.63.0 + resolution: "@typescript-eslint/type-utils@npm:8.63.0" dependencies: - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/visitor-keys": "npm:5.62.0" - debug: "npm:^4.3.4" - globby: "npm:^11.1.0" - is-glob: "npm:^4.0.3" - semver: "npm:^7.3.7" - tsutils: "npm:^3.21.0" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/d7984a3e9d56897b2481940ec803cb8e7ead03df8d9cfd9797350be82ff765dfcf3cfec04e7355e1779e948da8f02bc5e11719d07a596eb1cb995c48a95e38cf + "@typescript-eslint/types": "npm:8.63.0" + "@typescript-eslint/typescript-estree": "npm:8.63.0" + "@typescript-eslint/utils": "npm:8.63.0" + debug: "npm:^4.4.3" + ts-api-utils: "npm:^2.5.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/36f6ba389d05e57f2f1de0f21406a46694e39da83248d7d5eefb1601d6a37a3d2382ee6f1b1b0e63b78465d8d830eeaec1503aaefbad70b1030e59fb15307aae languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:7.18.0": - version: 7.18.0 - resolution: "@typescript-eslint/typescript-estree@npm:7.18.0" - dependencies: - "@typescript-eslint/types": "npm:7.18.0" - "@typescript-eslint/visitor-keys": "npm:7.18.0" - debug: "npm:^4.3.4" - globby: "npm:^11.1.0" - is-glob: "npm:^4.0.3" - minimatch: "npm:^9.0.4" - semver: "npm:^7.6.0" - ts-api-utils: "npm:^1.3.0" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/0c7f109a2e460ec8a1524339479cf78ff17814d23c83aa5112c77fb345e87b3642616291908dcddea1e671da63686403dfb712e4a4435104f92abdfddf9aba81 +"@typescript-eslint/types@npm:8.63.0, @typescript-eslint/types@npm:^8.63.0": + version: 8.63.0 + resolution: "@typescript-eslint/types@npm:8.63.0" + checksum: 10c0/270bd2b2affdc173dd7e9e1bf1419a6f7a2fa8ff1fca5c613da88f6e44c433a1412887e513d24233a4a6112bdc3439d3e4cadc4f492eb1a6dc2a377495a38836 languageName: node linkType: hard -"@typescript-eslint/utils@npm:7.18.0": - version: 7.18.0 - resolution: "@typescript-eslint/utils@npm:7.18.0" +"@typescript-eslint/typescript-estree@npm:8.63.0": + version: 8.63.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.63.0" dependencies: - "@eslint-community/eslint-utils": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:7.18.0" - "@typescript-eslint/types": "npm:7.18.0" - "@typescript-eslint/typescript-estree": "npm:7.18.0" + "@typescript-eslint/project-service": "npm:8.63.0" + "@typescript-eslint/tsconfig-utils": "npm:8.63.0" + "@typescript-eslint/types": "npm:8.63.0" + "@typescript-eslint/visitor-keys": "npm:8.63.0" + debug: "npm:^4.4.3" + minimatch: "npm:^10.2.2" + semver: "npm:^7.7.3" + tinyglobby: "npm:^0.2.15" + ts-api-utils: "npm:^2.5.0" peerDependencies: - eslint: ^8.56.0 - checksum: 10c0/a25a6d50eb45c514469a01ff01f215115a4725fb18401055a847ddf20d1b681409c4027f349033a95c4ff7138d28c3b0a70253dfe8262eb732df4b87c547bd1e + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/84ca87141a0e7d59fb91e7baef36e20d4f00f1996aec6dfb1909c40496f13e54a13102c12b7cbe89285d04ca7975faa64d5683ff0d44ec9baa7be5207540142c languageName: node linkType: hard -"@typescript-eslint/utils@npm:^5.10.0": - version: 5.62.0 - resolution: "@typescript-eslint/utils@npm:5.62.0" +"@typescript-eslint/utils@npm:8.63.0, @typescript-eslint/utils@npm:^8.0.0": + version: 8.63.0 + resolution: "@typescript-eslint/utils@npm:8.63.0" dependencies: - "@eslint-community/eslint-utils": "npm:^4.2.0" - "@types/json-schema": "npm:^7.0.9" - "@types/semver": "npm:^7.3.12" - "@typescript-eslint/scope-manager": "npm:5.62.0" - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/typescript-estree": "npm:5.62.0" - eslint-scope: "npm:^5.1.1" - semver: "npm:^7.3.7" + "@eslint-community/eslint-utils": "npm:^4.9.1" + "@typescript-eslint/scope-manager": "npm:8.63.0" + "@typescript-eslint/types": "npm:8.63.0" + "@typescript-eslint/typescript-estree": "npm:8.63.0" peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 10c0/f09b7d9952e4a205eb1ced31d7684dd55cee40bf8c2d78e923aa8a255318d97279825733902742c09d8690f37a50243f4c4d383ab16bd7aefaf9c4b438f785e1 - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" - dependencies: - "@typescript-eslint/types": "npm:5.62.0" - eslint-visitor-keys: "npm:^3.3.0" - checksum: 10c0/7c3b8e4148e9b94d9b7162a596a1260d7a3efc4e65199693b8025c71c4652b8042501c0bc9f57654c1e2943c26da98c0f77884a746c6ae81389fcb0b513d995d + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/1b0bb66c2dc11d7c3ea4e840294e35062e5f85b6d1d7d94b37e94e52a2fa71e76adf277602a183832c23d3deeb1f129d7aeb3ce960c1fc9b6d1136a4d61bd54a languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:7.18.0": - version: 7.18.0 - resolution: "@typescript-eslint/visitor-keys@npm:7.18.0" +"@typescript-eslint/visitor-keys@npm:8.63.0": + version: 8.63.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.63.0" dependencies: - "@typescript-eslint/types": "npm:7.18.0" - eslint-visitor-keys: "npm:^3.4.3" - checksum: 10c0/538b645f8ff1d9debf264865c69a317074eaff0255e63d7407046176b0f6a6beba34a6c51d511f12444bae12a98c69891eb6f403c9f54c6c2e2849d1c1cb73c0 + "@typescript-eslint/types": "npm:8.63.0" + eslint-visitor-keys: "npm:^5.0.0" + checksum: 10c0/595b47b08efecc35d93a8ac072798f9dc2371a371f03a1a03ab134a8505fe422fc647014bd096eac75a3d487c5eecf24393048ca88ed06f759d00115c11dd8bc languageName: node linkType: hard @@ -4167,7 +4089,17 @@ __metadata: languageName: node linkType: hard -"accepts@npm:^1.3.7, accepts@npm:~1.3.8": +"accepts@npm:^2.0.0": + version: 2.0.0 + resolution: "accepts@npm:2.0.0" + dependencies: + mime-types: "npm:^3.0.0" + negotiator: "npm:^1.0.0" + checksum: 10c0/98374742097e140891546076215f90c32644feacf652db48412329de4c2a529178a81aa500fbb13dd3e6cbf6e68d829037b123ac037fc9a08bcec4b87b358eef + languageName: node + linkType: hard + +"accepts@npm:~1.3.8": version: 1.3.8 resolution: "accepts@npm:1.3.8" dependencies: @@ -4615,12 +4547,12 @@ __metadata: languageName: node linkType: hard -"babel-plugin-syntax-hermes-parser@npm:0.29.1": - version: 0.29.1 - resolution: "babel-plugin-syntax-hermes-parser@npm:0.29.1" +"babel-plugin-syntax-hermes-parser@npm:0.36.0": + version: 0.36.0 + resolution: "babel-plugin-syntax-hermes-parser@npm:0.36.0" dependencies: - hermes-parser: "npm:0.29.1" - checksum: 10c0/a6d95e4a7079976e477636d18509272a7a185930e143c61d0421a36096e85905563630ac4f0f317518b6db37f50daaefc1828d575b3d5fb090a55e9d39d2534c + hermes-parser: "npm:0.36.0" + checksum: 10c0/3c20d2b2595e890bf7bf14b48a1dc6eb2ad2625e04f942254f3676440e20489eb8266fba8f8077194d294dfb67552b6b969bb40b93c873b88fdc319528dc2606 languageName: node linkType: hard @@ -4959,7 +4891,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.2": +"chalk@npm:^4.0.0, chalk@npm:^4.1.0": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -5020,17 +4952,16 @@ __metadata: languageName: node linkType: hard -"chromium-edge-launcher@npm:^0.2.0": - version: 0.2.0 - resolution: "chromium-edge-launcher@npm:0.2.0" +"chromium-edge-launcher@npm:^0.3.0": + version: 0.3.0 + resolution: "chromium-edge-launcher@npm:0.3.0" dependencies: "@types/node": "npm:*" escape-string-regexp: "npm:^4.0.0" is-wsl: "npm:^2.2.0" lighthouse-logger: "npm:^1.0.0" mkdirp: "npm:^1.0.4" - rimraf: "npm:^3.0.2" - checksum: 10c0/880972816dd9b95c0eb77d1f707569667a8cce7cc29fe9c8d199c47fdfbe4971e9da3e5a29f61c4ecec29437ac7cebbbb5afc30bec96306579d1121e7340606a + checksum: 10c0/ad04a75bf53ebed0b7adc5bd133587369b0c2e55c92fe460eb6ccec5efe03c161a7466756173969867a2acbe02dd40449186bd74671dd892520492283d4ff43d languageName: node linkType: hard @@ -5485,7 +5416,7 @@ __metadata: languageName: node linkType: hard -"convert-source-map@npm:2.0.0, convert-source-map@npm:^2.0.0": +"convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0" checksum: 10c0/8f2f7a27a1a011cc6cc88cc4da2d7d0cfa5ee0369508baae3d98c260bb3ac520691464e5bbe4ae7cdf09860c1d69ecc6f70c63c6e7c7f7e3f18ec08484dc7d9b @@ -5659,7 +5590,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.4.0, debug@npm:^4.4.1": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.4.0, debug@npm:^4.4.1, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -6340,21 +6271,24 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-jest@npm:^27.9.0": - version: 27.9.0 - resolution: "eslint-plugin-jest@npm:27.9.0" +"eslint-plugin-jest@npm:^29.0.1": + version: 29.15.4 + resolution: "eslint-plugin-jest@npm:29.15.4" dependencies: - "@typescript-eslint/utils": "npm:^5.10.0" + "@typescript-eslint/utils": "npm:^8.0.0" peerDependencies: - "@typescript-eslint/eslint-plugin": ^5.0.0 || ^6.0.0 || ^7.0.0 - eslint: ^7.0.0 || ^8.0.0 + "@typescript-eslint/eslint-plugin": ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 jest: "*" + typescript: ">=4.8.4 <7.0.0" peerDependenciesMeta: "@typescript-eslint/eslint-plugin": optional: true jest: optional: true - checksum: 10c0/b8b09f7d8ba3d84a8779a6e95702a6e4dce45ab034e4edf5ddb631e77cd38dcdf791dfd9228e0a0d1d80d1eb2d278deb62ad2ec39f10fb8fd43cec07304e0c38 + typescript: + optional: true + checksum: 10c0/4a32cd29cb67b54e5fb5f4acbc749e79c024f50099825e91c93d1d9674caf5b4bfe4810c7931e01d6b30e77cbf72d7619db785a12f459f2b23f5885d39cb1033 languageName: node linkType: hard @@ -6378,12 +6312,18 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react-hooks@npm:^5.2.0": - version: 5.2.0 - resolution: "eslint-plugin-react-hooks@npm:5.2.0" +"eslint-plugin-react-hooks@npm:^7.0.1": + version: 7.1.1 + resolution: "eslint-plugin-react-hooks@npm:7.1.1" + dependencies: + "@babel/core": "npm:^7.24.4" + "@babel/parser": "npm:^7.24.4" + hermes-parser: "npm:^0.25.1" + zod: "npm:^3.25.0 || ^4.0.0" + zod-validation-error: "npm:^3.5.0 || ^4.0.0" peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - checksum: 10c0/1c8d50fa5984c6dea32470651807d2922cc3934cf3425e78f84a24c2dfd972e7f019bee84aefb27e0cf2c13fea0ac1d4473267727408feeb1c56333ca1489385 + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + checksum: 10c0/cee8454915d71ac5d70a0d8f4f260e76eaf45fcd4162747dd4282b792ee5616d187351dabe6cdcff9040c79d0cec625635c4fd0777276be119efa88ebe058525 languageName: node linkType: hard @@ -6394,18 +6334,18 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react-native@npm:^4.0.0": - version: 4.1.0 - resolution: "eslint-plugin-react-native@npm:4.1.0" +"eslint-plugin-react-native@npm:^5.0.0": + version: 5.0.0 + resolution: "eslint-plugin-react-native@npm:5.0.0" dependencies: eslint-plugin-react-native-globals: "npm:^0.1.1" peerDependencies: - eslint: ^3.17.0 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: 10c0/9aedccde6227b78bad7c243844aca0860fca2dccd635e91e745bcd617c1e7fb889fa212917cf7b56860335a147fc7c8dc339d1976330ec4f896fe9156b35b162 + eslint: ^3.17.0 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + checksum: 10c0/c7c927bc743abf0cb367cc64fea5b28b28ea0c58be2990cab858a050b4855e89d90513afa44d73012c9fd670810ad0da2ac72e3e4bdfedf0ce0cbb65e901af7f languageName: node linkType: hard -"eslint-plugin-react@npm:^7.30.1": +"eslint-plugin-react@npm:^7.37.5": version: 7.37.5 resolution: "eslint-plugin-react@npm:7.37.5" dependencies: @@ -6433,7 +6373,7 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:5.1.1, eslint-scope@npm:^5.1.1": +"eslint-scope@npm:5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" dependencies: @@ -6460,7 +6400,7 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.3": +"eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820 @@ -6474,6 +6414,13 @@ __metadata: languageName: node linkType: hard +"eslint-visitor-keys@npm:^5.0.0": + version: 5.0.1 + resolution: "eslint-visitor-keys@npm:5.0.1" + checksum: 10c0/16190bdf2cbae40a1109384c94450c526a79b0b9c3cb21e544256ed85ac48a4b84db66b74a6561d20fe6ab77447f150d711c2ad5ad74df4fcc133736bce99678 + languageName: node + linkType: hard + "eslint@npm:^9.35.0": version: 9.39.2 resolution: "eslint@npm:9.39.2" @@ -6764,6 +6711,15 @@ __metadata: languageName: node linkType: hard +"fb-dotslash@npm:0.5.8": + version: 0.5.8 + resolution: "fb-dotslash@npm:0.5.8" + bin: + dotslash: bin/dotslash + checksum: 10c0/6c693ecb8e61cd8571e0ad6a923e0582cf8e481695e906e17c8e31620402e06f8b80d95111a420d2f62349d9bebc2b820bae14c2c54a814e72abdc710dc1d3ed + languageName: node + linkType: hard + "fb-watchman@npm:^2.0.0": version: 2.0.2 resolution: "fb-watchman@npm:2.0.2" @@ -7211,7 +7167,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.1.1, glob@npm:^7.1.3, glob@npm:^7.1.4": +"glob@npm:^7.1.3, glob@npm:^7.1.4": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -7251,7 +7207,7 @@ __metadata: languageName: node linkType: hard -"globby@npm:^11.0.1, globby@npm:^11.1.0": +"globby@npm:^11.0.1": version: 11.1.0 resolution: "globby@npm:11.1.0" dependencies: @@ -7293,13 +7249,6 @@ __metadata: languageName: node linkType: hard -"graphemer@npm:^1.4.0": - version: 1.4.0 - resolution: "graphemer@npm:1.4.0" - checksum: 10c0/e951259d8cd2e0d196c72ec711add7115d42eb9a8146c8eeda5b8d3ac91e5dd816b9cd68920726d9fd4490368e7ed86e9c423f40db87e2d8dfafa00fa17c3a31 - languageName: node - linkType: hard - "handlebars@npm:^4.7.7": version: 4.7.8 resolution: "handlebars@npm:4.7.8" @@ -7375,6 +7324,20 @@ __metadata: languageName: node linkType: hard +"hermes-compiler@npm:250829098.0.14": + version: 250829098.0.14 + resolution: "hermes-compiler@npm:250829098.0.14" + checksum: 10c0/b9f4024ac7377bebec61b91e1cee8f3909a8ecad0ea786c6b7c3863f8b65bd94f928850b341c82865a9435b8390d33f03e149e9d479c6870b6f5a754d101fde7 + languageName: node + linkType: hard + +"hermes-estree@npm:0.25.1": + version: 0.25.1 + resolution: "hermes-estree@npm:0.25.1" + checksum: 10c0/48be3b2fa37a0cbc77a112a89096fa212f25d06de92781b163d67853d210a8a5c3784fac23d7d48335058f7ed283115c87b4332c2a2abaaccc76d0ead1a282ac + languageName: node + linkType: hard + "hermes-estree@npm:0.28.1": version: 0.28.1 resolution: "hermes-estree@npm:0.28.1" @@ -7382,17 +7345,17 @@ __metadata: languageName: node linkType: hard -"hermes-estree@npm:0.29.1": - version: 0.29.1 - resolution: "hermes-estree@npm:0.29.1" - checksum: 10c0/e6b01f79ba708697d61a74b871d5ebae5f863c6d782657d8e2d2256eb838f1eb86ff9c34773a81d9cc69e54be3a5059c686e0ab54a4afba903b40dde92dd0ccb +"hermes-estree@npm:0.35.0": + version: 0.35.0 + resolution: "hermes-estree@npm:0.35.0" + checksum: 10c0/a88c9dc63b8b3679b1aeb43e72e977597096c1bd7d59978c952f1d6df6d1a517c4a817c70b1b701854996b485adfa66c2fc7f80871029a7f0c04306f6717b59a languageName: node linkType: hard -"hermes-estree@npm:0.32.0": - version: 0.32.0 - resolution: "hermes-estree@npm:0.32.0" - checksum: 10c0/3b67d1fe44336240ef7f9c40ecbf363279ba263d51efe120570c3862cc109e652fc09aebddfe6b73d0f0246610bee130e4064c359f1f4cbf002bdb1d99717ef2 +"hermes-estree@npm:0.36.0": + version: 0.36.0 + resolution: "hermes-estree@npm:0.36.0" + checksum: 10c0/fe27f57b0f8c3921e9dc48c517e25f4399609ca386353f90e04d3f859bacbee93184af80a6b930db5e981e8953a5c4083147556b21b19519917e131e7533b27c languageName: node linkType: hard @@ -7405,30 +7368,30 @@ __metadata: languageName: node linkType: hard -"hermes-parser@npm:0.29.1": - version: 0.29.1 - resolution: "hermes-parser@npm:0.29.1" +"hermes-parser@npm:0.35.0": + version: 0.35.0 + resolution: "hermes-parser@npm:0.35.0" dependencies: - hermes-estree: "npm:0.29.1" - checksum: 10c0/7f40d9bdfb5acaa700f333a24c644b17f5f8d0e823b1e7a9fb6dcf253a54d54716ae63c74effa023688ee4f09013c80188c40d601570fee256a44954e04c2926 + hermes-estree: "npm:0.35.0" + checksum: 10c0/49d98093a2094758db5b536627c6cf5146b140f66e63143acf471c62f1d3fd8bd6ae10a33f2372f72e3653deda5d4615c6dae89d01248849440916209901fc4a languageName: node linkType: hard -"hermes-parser@npm:0.32.0": - version: 0.32.0 - resolution: "hermes-parser@npm:0.32.0" +"hermes-parser@npm:0.36.0": + version: 0.36.0 + resolution: "hermes-parser@npm:0.36.0" dependencies: - hermes-estree: "npm:0.32.0" - checksum: 10c0/5902d2c5d347c0629fba07a47eaad5569590ac69bc8bfb2e454e08d2dfbe1ebd989d88518dca2cba64061689b5eac5960ae6bd15a4a66600bbf377498a3234b7 + hermes-estree: "npm:0.36.0" + checksum: 10c0/76e726366ac2ea91e9464853f439d582ddee9c07ccb84cac35297ce1a5d197bf96b84ab030423a22940b55a0fa5bb191bc0ebeebd95f8e34116900decdccdb86 languageName: node linkType: hard -"hoist-non-react-statics@npm:^3.3.0": - version: 3.3.2 - resolution: "hoist-non-react-statics@npm:3.3.2" +"hermes-parser@npm:^0.25.1": + version: 0.25.1 + resolution: "hermes-parser@npm:0.25.1" dependencies: - react-is: "npm:^16.7.0" - checksum: 10c0/fe0889169e845d738b59b64badf5e55fa3cf20454f9203d1eb088df322d49d4318df774828e789898dcb280e8a5521bb59b3203385662ca5e9218a6ca5820e74 + hermes-estree: "npm:0.25.1" + checksum: 10c0/3abaa4c6f1bcc25273f267297a89a4904963ea29af19b8e4f6eabe04f1c2c7e9abd7bfc4730ddb1d58f2ea04b6fee74053d8bddb5656ec6ebf6c79cc8d14202c languageName: node linkType: hard @@ -7543,7 +7506,7 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.0.5, ignore@npm:^5.2.0, ignore@npm:^5.3.1": +"ignore@npm:^5.0.5, ignore@npm:^5.2.0": version: 5.3.2 resolution: "ignore@npm:5.3.2" checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 @@ -7557,6 +7520,13 @@ __metadata: languageName: node linkType: hard +"ignore@npm:^7.0.5": + version: 7.0.6 + resolution: "ignore@npm:7.0.6" + checksum: 10c0/fc01ef1d14efbe003439b60538726351e81483d1b6f55bdbb3a4465c6346d9481afad5b350dfbd604ddd7049618ef9093ff26dc147984aabc303c56ba53ea3b5 + languageName: node + linkType: hard + "image-size@npm:^1.0.2": version: 1.2.1 resolution: "image-size@npm:1.2.1" @@ -9384,69 +9354,70 @@ __metadata: languageName: node linkType: hard -"metro-babel-transformer@npm:0.83.3": - version: 0.83.3 - resolution: "metro-babel-transformer@npm:0.83.3" +"metro-babel-transformer@npm:0.84.4": + version: 0.84.4 + resolution: "metro-babel-transformer@npm:0.84.4" dependencies: "@babel/core": "npm:^7.25.2" flow-enums-runtime: "npm:^0.0.6" - hermes-parser: "npm:0.32.0" + hermes-parser: "npm:0.35.0" + metro-cache-key: "npm:0.84.4" nullthrows: "npm:^1.1.1" - checksum: 10c0/b0107f86cdc9ef9419d669b5b3dac22e35b02c67c480563a63d98f5fb50953587938769efc854bfc09c225557790cd6488dbe3fed6f05c2b3f322cfb2e5ff577 + checksum: 10c0/d1ac996666334bc1cfe9d399cbf4cd747b675f6f8f758c2317eebcc52bd76046ed864ddb7b270efeb8cf337940a61fb03912e5c859b7cbc54687c2f5c41a9d2a languageName: node linkType: hard -"metro-cache-key@npm:0.83.3": - version: 0.83.3 - resolution: "metro-cache-key@npm:0.83.3" +"metro-cache-key@npm:0.84.4": + version: 0.84.4 + resolution: "metro-cache-key@npm:0.84.4" dependencies: flow-enums-runtime: "npm:^0.0.6" - checksum: 10c0/403a2ca5b5bbb31a979effaa31fba0c47e2eb3830428c39c99db58aa0739a6fcc386f5a56c91495c53a4569065f0bda29e3038e9c41ca17af443971395f257dc + checksum: 10c0/a82ab6367f11886d960cc8fa1f3aa54f6529fe30c16059c141c3e789084c50838fdd7e1a5528534cd9c11a74c63aa5c6a7461dbfa50e8c449b6141eaf2fd05e0 languageName: node linkType: hard -"metro-cache@npm:0.83.3": - version: 0.83.3 - resolution: "metro-cache@npm:0.83.3" +"metro-cache@npm:0.84.4": + version: 0.84.4 + resolution: "metro-cache@npm:0.84.4" dependencies: exponential-backoff: "npm:^3.1.1" flow-enums-runtime: "npm:^0.0.6" https-proxy-agent: "npm:^7.0.5" - metro-core: "npm:0.83.3" - checksum: 10c0/608e85d819092c0b472c9adabb5de58e88355739de71833230626c1af7f3ce5dd1dca9f1ff3a836d995201f717315fd769c4c646a818c1f490ea2ec29417e32a + metro-core: "npm:0.84.4" + checksum: 10c0/3bf7f3a1f85b4f1af05f4b2c71c78e56fd3262d967ee43f02e9ff6820254063af33a70b6549e3dc5e993a6a0b9df92e9279632ad9a8b1cde2577342f93df45eb languageName: node linkType: hard -"metro-config@npm:0.83.3, metro-config@npm:^0.83.1": - version: 0.83.3 - resolution: "metro-config@npm:0.83.3" +"metro-config@npm:0.84.4, metro-config@npm:^0.84.3": + version: 0.84.4 + resolution: "metro-config@npm:0.84.4" dependencies: connect: "npm:^3.6.5" flow-enums-runtime: "npm:^0.0.6" jest-validate: "npm:^29.7.0" - metro: "npm:0.83.3" - metro-cache: "npm:0.83.3" - metro-core: "npm:0.83.3" - metro-runtime: "npm:0.83.3" + metro: "npm:0.84.4" + metro-cache: "npm:0.84.4" + metro-core: "npm:0.84.4" + metro-runtime: "npm:0.84.4" yaml: "npm:^2.6.1" - checksum: 10c0/c53e4a061cfc776a65cdb5055c0be840055f9741dae25e7d407835988618b15f1407270dbd957c7333d01e9c79eccbf8e6bcb76421b2145bd134b53df459a033 + checksum: 10c0/f8aaf7d8cff9b486353b62f4746b0a70f99749bd4061f5ae847524aaedcd9c5a34bf176cbbe12fb33e771e8ed3c1496654b2578fa5ba8b9e4f856f0589744d98 languageName: node linkType: hard -"metro-core@npm:0.83.3, metro-core@npm:^0.83.1": - version: 0.83.3 - resolution: "metro-core@npm:0.83.3" +"metro-core@npm:0.84.4, metro-core@npm:^0.84.3": + version: 0.84.4 + resolution: "metro-core@npm:0.84.4" dependencies: flow-enums-runtime: "npm:^0.0.6" lodash.throttle: "npm:^4.1.1" - metro-resolver: "npm:0.83.3" - checksum: 10c0/d44c1f117c4b27f18abd27110e9536abf3105733e8fccaa522bd0e008248cce0260130517840c4914d7ce5df498f39ecfd43b6046a0f0b1c0f8ada7de38e52c4 + metro-resolver: "npm:0.84.4" + checksum: 10c0/19d859de16b5e082c9c31bed981c579a4e6d31a626c7829b725df9ae0ffb755d0ef7809ba9f8adf22d3921f5ffdd931ed77b21b95ca2ea17895f0c99b3cab831 languageName: node linkType: hard -"metro-file-map@npm:0.83.3": - version: 0.83.3 - resolution: "metro-file-map@npm:0.83.3" +"metro-file-map@npm:0.84.4": + version: 0.84.4 + resolution: "metro-file-map@npm:0.84.4" dependencies: debug: "npm:^4.4.0" fb-watchman: "npm:^2.0.0" @@ -9457,146 +9428,144 @@ __metadata: micromatch: "npm:^4.0.4" nullthrows: "npm:^1.1.1" walker: "npm:^1.0.7" - checksum: 10c0/4bf9c0fcdb5a5c08851f7370d6427fb68a770f156c4eabbddf20bd3583fb25ae428507eaeb8dc525e792db41d048620209750f33735055863abc909cbb6ef71a + checksum: 10c0/09ca829570d1d6dc5beb0534da8a7f2bfcae5415b0974fd5f58b4a05da95dbafdd47f7dc8dedeb11b6562ee9a92c4d918466d02a05cda6e1eaf2c400cbbe6fb4 languageName: node linkType: hard -"metro-minify-terser@npm:0.83.3": - version: 0.83.3 - resolution: "metro-minify-terser@npm:0.83.3" +"metro-minify-terser@npm:0.84.4": + version: 0.84.4 + resolution: "metro-minify-terser@npm:0.84.4" dependencies: flow-enums-runtime: "npm:^0.0.6" terser: "npm:^5.15.0" - checksum: 10c0/9158e3199c0ea647776a7ed5c68ec1bb493f5347ac979f1ca75020cf1c39f907bd29983d60f8cb24dca17053d6b5c35f140c6d720fad0bd0fa9728e8c51e95c6 + checksum: 10c0/c9b36c2adb8254c38bdedad9da8bf2b7fae7f45cbd883e590430a5fc9cad808af24dd08a9420925e15733dab886528ad553e3eeb3faffc53d3ad80e7e03e5f6d languageName: node linkType: hard -"metro-resolver@npm:0.83.3": - version: 0.83.3 - resolution: "metro-resolver@npm:0.83.3" +"metro-resolver@npm:0.84.4": + version: 0.84.4 + resolution: "metro-resolver@npm:0.84.4" dependencies: flow-enums-runtime: "npm:^0.0.6" - checksum: 10c0/1d6c030a00b987fbee38e5c632219b2be602e38c9aa9628bb4b591f646e64130d08adb8dcb35076c5c8cc151135557b655f3dee514c0df9f26d3416629eb006b + checksum: 10c0/468334270598222e15cbee32af51a3b5e1f4fa6869794955b95d1134b28a58594e8e3879e841ccf00bbb5cd86c689a4481714d6c6a464931987d5333d2c55f80 languageName: node linkType: hard -"metro-runtime@npm:0.83.3, metro-runtime@npm:^0.83.1": - version: 0.83.3 - resolution: "metro-runtime@npm:0.83.3" +"metro-runtime@npm:0.84.4, metro-runtime@npm:^0.84.3": + version: 0.84.4 + resolution: "metro-runtime@npm:0.84.4" dependencies: "@babel/runtime": "npm:^7.25.0" flow-enums-runtime: "npm:^0.0.6" - checksum: 10c0/1d788483b6c2f13e0ea9ff4564996154754d3de84f683812ac848053eaea9243144adee3e8ffe90789e6c253f7402211d72b1b5ebf09e6c23841bc956a680253 + checksum: 10c0/e2b2e819027940c6bbd081e5650238d52b6c6d78561cd486b8c10cd1e7fce0213c66fa7f885e37ad5377fcd5726b1c9e473fba6de13938cdf2c966e82968c05f languageName: node linkType: hard -"metro-source-map@npm:0.83.3, metro-source-map@npm:^0.83.1": - version: 0.83.3 - resolution: "metro-source-map@npm:0.83.3" +"metro-source-map@npm:0.84.4, metro-source-map@npm:^0.84.3": + version: 0.84.4 + resolution: "metro-source-map@npm:0.84.4" dependencies: - "@babel/traverse": "npm:^7.25.3" - "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3" - "@babel/types": "npm:^7.25.2" + "@babel/traverse": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" flow-enums-runtime: "npm:^0.0.6" invariant: "npm:^2.2.4" - metro-symbolicate: "npm:0.83.3" + metro-symbolicate: "npm:0.84.4" nullthrows: "npm:^1.1.1" - ob1: "npm:0.83.3" + ob1: "npm:0.84.4" source-map: "npm:^0.5.6" vlq: "npm:^1.0.0" - checksum: 10c0/47e984bde1f8f06348298771f44b5803657c9cfa387df8ff36a359cc72ae3bc0e9c4ea6141345609b183ac8c63dcc997000d3626006e388c24779abb57c6f82c + checksum: 10c0/39df4524022e07aa4b4d09dd874a9509eb9e2e1e491e80a35099020347ab6be2407851b026452296aad314b0eb7ecf14f9b6bab96bd7c31d47d8b1eb30279aaf languageName: node linkType: hard -"metro-symbolicate@npm:0.83.3": - version: 0.83.3 - resolution: "metro-symbolicate@npm:0.83.3" +"metro-symbolicate@npm:0.84.4": + version: 0.84.4 + resolution: "metro-symbolicate@npm:0.84.4" dependencies: flow-enums-runtime: "npm:^0.0.6" invariant: "npm:^2.2.4" - metro-source-map: "npm:0.83.3" + metro-source-map: "npm:0.84.4" nullthrows: "npm:^1.1.1" source-map: "npm:^0.5.6" vlq: "npm:^1.0.0" bin: metro-symbolicate: src/index.js - checksum: 10c0/bd3d234c7581466a9a78f952caa25816666753f6b560fe41502727b3e59931ac65225c9909635dc7c25d4dfaf392631366ef3ec5fa8490413385d60f8d900112 + checksum: 10c0/416a9ef694150a8ec708187743b74ab67e0b4fec39c64610b3771b584830117670a62acb9aa824f84a44efbb1cfec07aaf943d1aaf349d977eecf7c72bd8c0bf languageName: node linkType: hard -"metro-transform-plugins@npm:0.83.3": - version: 0.83.3 - resolution: "metro-transform-plugins@npm:0.83.3" +"metro-transform-plugins@npm:0.84.4": + version: 0.84.4 + resolution: "metro-transform-plugins@npm:0.84.4" dependencies: "@babel/core": "npm:^7.25.2" - "@babel/generator": "npm:^7.25.0" - "@babel/template": "npm:^7.25.0" - "@babel/traverse": "npm:^7.25.3" + "@babel/generator": "npm:^7.29.1" + "@babel/template": "npm:^7.28.6" + "@babel/traverse": "npm:^7.29.0" flow-enums-runtime: "npm:^0.0.6" nullthrows: "npm:^1.1.1" - checksum: 10c0/df3c6db6a69d4888e1b6aad40d48ffec0c3c3faa38e89c07633432fc107ef12c47d55598904c91aadfe0751c5bcb7ec191f8a5ee70c18d253201150fc617ca37 + checksum: 10c0/7edb0c0d3655e9f5f5fb8bd8221ec297394b8730c959a3245ea81e50da8177ad7782f21696201a0dcb922281efd919e9548d5b819d8338e52d4b130f06333123 languageName: node linkType: hard -"metro-transform-worker@npm:0.83.3": - version: 0.83.3 - resolution: "metro-transform-worker@npm:0.83.3" +"metro-transform-worker@npm:0.84.4": + version: 0.84.4 + resolution: "metro-transform-worker@npm:0.84.4" dependencies: "@babel/core": "npm:^7.25.2" - "@babel/generator": "npm:^7.25.0" - "@babel/parser": "npm:^7.25.3" - "@babel/types": "npm:^7.25.2" + "@babel/generator": "npm:^7.29.1" + "@babel/parser": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" flow-enums-runtime: "npm:^0.0.6" - metro: "npm:0.83.3" - metro-babel-transformer: "npm:0.83.3" - metro-cache: "npm:0.83.3" - metro-cache-key: "npm:0.83.3" - metro-minify-terser: "npm:0.83.3" - metro-source-map: "npm:0.83.3" - metro-transform-plugins: "npm:0.83.3" + metro: "npm:0.84.4" + metro-babel-transformer: "npm:0.84.4" + metro-cache: "npm:0.84.4" + metro-cache-key: "npm:0.84.4" + metro-minify-terser: "npm:0.84.4" + metro-source-map: "npm:0.84.4" + metro-transform-plugins: "npm:0.84.4" nullthrows: "npm:^1.1.1" - checksum: 10c0/bea0cbcc7d13cd2b97a2159257b3a53b9ecfb15da18ace82ae05bf2d0ac7cc1806c0bd77ed3b8f4c82c9532773fb99f3938e4b1480e2673f5eda69575ee1d7ef + checksum: 10c0/95924f9bcaf6df931bba2783f440d8fab29909bdde8cecdcc3bc7603e7de71e51728a34288f045694b616c94216d1fc683493b8a470e074c9c8a7f220aa9f9b5 languageName: node linkType: hard -"metro@npm:0.83.3, metro@npm:^0.83.1": - version: 0.83.3 - resolution: "metro@npm:0.83.3" +"metro@npm:0.84.4, metro@npm:^0.84.3": + version: 0.84.4 + resolution: "metro@npm:0.84.4" dependencies: - "@babel/code-frame": "npm:^7.24.7" + "@babel/code-frame": "npm:^7.29.0" "@babel/core": "npm:^7.25.2" - "@babel/generator": "npm:^7.25.0" - "@babel/parser": "npm:^7.25.3" - "@babel/template": "npm:^7.25.0" - "@babel/traverse": "npm:^7.25.3" - "@babel/types": "npm:^7.25.2" - accepts: "npm:^1.3.7" - chalk: "npm:^4.0.0" + "@babel/generator": "npm:^7.29.1" + "@babel/parser": "npm:^7.29.0" + "@babel/template": "npm:^7.28.6" + "@babel/traverse": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" + accepts: "npm:^2.0.0" ci-info: "npm:^2.0.0" connect: "npm:^3.6.5" debug: "npm:^4.4.0" error-stack-parser: "npm:^2.0.6" flow-enums-runtime: "npm:^0.0.6" graceful-fs: "npm:^4.2.4" - hermes-parser: "npm:0.32.0" + hermes-parser: "npm:0.35.0" image-size: "npm:^1.0.2" invariant: "npm:^2.2.4" jest-worker: "npm:^29.7.0" jsc-safe-url: "npm:^0.2.2" lodash.throttle: "npm:^4.1.1" - metro-babel-transformer: "npm:0.83.3" - metro-cache: "npm:0.83.3" - metro-cache-key: "npm:0.83.3" - metro-config: "npm:0.83.3" - metro-core: "npm:0.83.3" - metro-file-map: "npm:0.83.3" - metro-resolver: "npm:0.83.3" - metro-runtime: "npm:0.83.3" - metro-source-map: "npm:0.83.3" - metro-symbolicate: "npm:0.83.3" - metro-transform-plugins: "npm:0.83.3" - metro-transform-worker: "npm:0.83.3" - mime-types: "npm:^2.1.27" + metro-babel-transformer: "npm:0.84.4" + metro-cache: "npm:0.84.4" + metro-cache-key: "npm:0.84.4" + metro-config: "npm:0.84.4" + metro-core: "npm:0.84.4" + metro-file-map: "npm:0.84.4" + metro-resolver: "npm:0.84.4" + metro-runtime: "npm:0.84.4" + metro-source-map: "npm:0.84.4" + metro-symbolicate: "npm:0.84.4" + metro-transform-plugins: "npm:0.84.4" + metro-transform-worker: "npm:0.84.4" + mime-types: "npm:^3.0.1" nullthrows: "npm:^1.1.1" serialize-error: "npm:^2.1.0" source-map: "npm:^0.5.6" @@ -9605,7 +9574,7 @@ __metadata: yargs: "npm:^17.6.2" bin: metro: src/cli.js - checksum: 10c0/9513c05725c3984ce3b72896c4f7d019ad4fd024a1231b8b84c5c655a0563fc7f26725f28c20c5d3511e3825d64fec3a1e68621f6a6af34d785c5e714ed7da89 + checksum: 10c0/ff92915119db29cd855274f3789d391cba83c50cb92e22d1e9b8c729e7f6d39495e32540a22ca4c6591eea6a847ade49fcfa5faab01b2300227e3f1fc7df359c languageName: node linkType: hard @@ -9633,7 +9602,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:3.0.2": +"mime-types@npm:3.0.2, mime-types@npm:^3.0.0, mime-types@npm:^3.0.1": version: 3.0.2 resolution: "mime-types@npm:3.0.2" dependencies: @@ -9642,7 +9611,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.27, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": +"mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -9690,7 +9659,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^10.0.1": +"minimatch@npm:^10.0.1, minimatch@npm:^10.2.2": version: 10.2.5 resolution: "minimatch@npm:10.2.5" dependencies: @@ -10047,12 +10016,12 @@ __metadata: languageName: node linkType: hard -"ob1@npm:0.83.3": - version: 0.83.3 - resolution: "ob1@npm:0.83.3" +"ob1@npm:0.84.4": + version: 0.84.4 + resolution: "ob1@npm:0.84.4" dependencies: flow-enums-runtime: "npm:^0.0.6" - checksum: 10c0/9231315de39cf0612a01e283c7d7ef31d16618e598de96e44ae1ab3007629296ce1a3d5d02ef60ff22d9fefe33050358c10e7fcba8278861157b89befe13cb3d + checksum: 10c0/8bf3a3bdc2b27f1b1b60569c31ff2d9d829025f9a1ce7388b5e810242e48672c8d6b24e5972d6e30aef4d84f6894d12b13d0c6c418460d031da1972b96920bba languageName: node linkType: hard @@ -10840,7 +10809,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^16.13.1, react-is@npm:^16.7.0": +"react-is@npm:^16.13.1": version: 16.13.1 resolution: "react-is@npm:16.13.1" checksum: 10c0/33977da7a5f1a287936a0c85639fec6ca74f4f15ef1e59a6bc20338fc73dc69555381e211f7a3529b8150a1f71e4225525b41b60b52965bda53ce7d47377ada1 @@ -10893,17 +10862,16 @@ __metadata: languageName: node linkType: hard -"react-native-gesture-handler@npm:^2.30.0": - version: 2.30.0 - resolution: "react-native-gesture-handler@npm:2.30.0" +"react-native-gesture-handler@npm:^3.0.2": + version: 3.0.2 + resolution: "react-native-gesture-handler@npm:3.0.2" dependencies: - "@egjs/hammerjs": "npm:^2.0.17" - hoist-non-react-statics: "npm:^3.3.0" + "@types/react-test-renderer": "npm:^19.1.0" invariant: "npm:^2.2.4" peerDependencies: react: "*" react-native: "*" - checksum: 10c0/26b94b0f97433fc6fb5b1c196ef29fce4f5a66c77fabb1bc33db91099357fbd44a5220783fcbb91d42700bcd578f2fd114314029d9e0d4674878767d9b7f5df6 + checksum: 10c0/06d2b1a621f73c148c4000692e55d5f2a44d44229cabc79829de0b0b3dbf3bcf4bc00e8635f82bd1062d7eac87f39ca5213eb1041e23a5543947e64773eaddfb languageName: node linkType: hard @@ -10917,13 +10885,13 @@ __metadata: languageName: node linkType: hard -"react-native-is-edge-to-edge@npm:1.2.1": - version: 1.2.1 - resolution: "react-native-is-edge-to-edge@npm:1.2.1" +"react-native-is-edge-to-edge@npm:^1.3.1": + version: 1.3.1 + resolution: "react-native-is-edge-to-edge@npm:1.3.1" peerDependencies: react: "*" react-native: "*" - checksum: 10c0/87d20b900aded7d44c90afb946a7aa03c23a94ca3dd547bdddc2303b85357e4aab22567a57b19f1558d6c8be7058e3dcf34faa1e15182d1604f90974266d9a1d + checksum: 10c0/28cebd5f1f3632864ff5e342278721d1e5e38627ae73859a8814012116ef15c629fee7137a6c9c97bb05d94bbe639b0b47e69b36fc2735ab53ed31570140663f languageName: node linkType: hard @@ -10947,14 +10915,14 @@ __metadata: languageName: node linkType: hard -"react-native-nitro-image@npm:0.15.0": - version: 0.15.0 - resolution: "react-native-nitro-image@npm:0.15.0" +"react-native-nitro-image@npm:0.15.1": + version: 0.15.1 + resolution: "react-native-nitro-image@npm:0.15.1" peerDependencies: react: "*" react-native: "*" react-native-nitro-modules: "*" - checksum: 10c0/7af289336f0340a387f723a38e2533d7b121db5c146a339f0c0c49a3d2d9cd40935c06dff5a6dd8ab5eb633515d959211f727dddafd648f957476ee36903ea5c + checksum: 10c0/c8599543167ce1f53c838c2ec4a38923b617945d6dbea9717383a21688fa30d409f91a03a4d2645efa2fcd3523a4f06bdc97484507e99ebf10ac7236da55d735 languageName: node linkType: hard @@ -10982,17 +10950,17 @@ __metadata: languageName: node linkType: hard -"react-native-reanimated@npm:^4.2.1": - version: 4.2.1 - resolution: "react-native-reanimated@npm:4.2.1" +"react-native-reanimated@npm:^4.5.1": + version: 4.5.1 + resolution: "react-native-reanimated@npm:4.5.1" dependencies: - react-native-is-edge-to-edge: "npm:1.2.1" - semver: "npm:7.7.3" + react-native-is-edge-to-edge: "npm:^1.3.1" + semver: "npm:^7.7.3" peerDependencies: react: "*" - react-native: "*" - react-native-worklets: ">=0.7.0" - checksum: 10c0/1458fb634d374125931c5815b2b0941e12b1a880ba12b4ff6bba12b6c101e0ce1f5c32a2226034ae762558e38077ae112d8498eb2c8af6bd967524c7e7f87c6b + react-native: 0.83 - 0.86 + react-native-worklets: 0.10.x + checksum: 10c0/86c64529fc5d59fb1a9083e0934c957c95c7319149efe02f92252237aa1fb208958da1c42705447c12b1d6bff3c23a354e10a05787995082eda1c284703ec0bc languageName: node linkType: hard @@ -11006,30 +10974,29 @@ __metadata: languageName: node linkType: hard -"react-native-screens@npm:^4.20.0": - version: 4.20.0 - resolution: "react-native-screens@npm:4.20.0" +"react-native-screens@npm:^4.26.0": + version: 4.26.0 + resolution: "react-native-screens@npm:4.26.0" dependencies: react-freeze: "npm:^1.0.0" warn-once: "npm:^0.1.0" peerDependencies: react: "*" - react-native: "*" - checksum: 10c0/f5d18bef4bee5b65470f76048dd3511d4ef18d1cf3566b6479a70a67e75c92aca3549342ee96c418e68997380dc562fbf4e63b5d411975c2265f106fecb242cd + react-native: ">=0.84.0" + checksum: 10c0/6abb462d71c06e91ee0237ae5281bb2e1a36411c69b1931a0d3aaac8adbf6662089eb3b5013878d69961493aac4b41b388c738e69624a5d6fe0d75f492d43a31 languageName: node linkType: hard -"react-native-svg@npm:^15.15.1": - version: 15.15.1 - resolution: "react-native-svg@npm:15.15.1" +"react-native-svg@npm:^15.15.5": + version: 15.15.5 + resolution: "react-native-svg@npm:15.15.5" dependencies: css-select: "npm:^5.1.0" css-tree: "npm:^1.1.3" - warn-once: "npm:0.1.1" peerDependencies: react: "*" react-native: "*" - checksum: 10c0/9e047e8afdd5121296a3402c5c37363b9b37fbddc41f7b0b49b923ac4d2898954c8b57a6784a625321236987e494ee54f594e22f9de815f807e03a3433d7fefd + checksum: 10c0/34b71b6c83d7235efbc4263abaffcbad780375f9e0fca95786e4f1a543a2b6d07e90c7df426f708e85be68331c412066cdc639f4a6ce82bed42bc728050d9402 languageName: node linkType: hard @@ -11042,34 +11009,34 @@ __metadata: "@babel/runtime": "npm:^7.25.0" "@gorhom/bottom-sheet": "npm:^5" "@react-native-community/blur": "npm:^4.4.1" - "@react-native-community/cli": "npm:20.0.0" - "@react-native-community/cli-platform-android": "npm:20.0.0" - "@react-native-community/cli-platform-ios": "npm:20.0.0" + "@react-native-community/cli": "npm:20.1.0" + "@react-native-community/cli-platform-android": "npm:20.1.0" + "@react-native-community/cli-platform-ios": "npm:20.1.0" "@react-native-community/slider": "npm:^5.1.2" - "@react-native/babel-preset": "npm:0.81.1" - "@react-native/metro-config": "npm:0.81.1" - "@react-native/typescript-config": "npm:0.81.1" + "@react-native/babel-preset": "npm:0.86.0" + "@react-native/metro-config": "npm:0.86.0" + "@react-native/typescript-config": "npm:0.86.0" "@react-navigation/elements": "npm:^2.9.3" "@react-navigation/native": "npm:^7.1.26" "@react-navigation/native-stack": "npm:^7.9.0" - "@types/react": "npm:^19.1.0" + "@types/react": "npm:^19.2.0" lucide-react-native: "npm:^0.562.0" - react: "npm:19.1.0" - react-native: "npm:0.81.1" + react: "npm:19.2.3" + react-native: "npm:0.86.0" react-native-builder-bob: "npm:^0.40.13" - react-native-gesture-handler: "npm:^2.30.0" + react-native-gesture-handler: "npm:^3.0.2" react-native-image-picker: "npm:^8.2.1" react-native-monorepo-config: "npm:^0.1.9" - react-native-nitro-image: "npm:0.15.0" + react-native-nitro-image: "npm:0.15.1" react-native-nitro-modules: "npm:0.36.1" react-native-permissions: "npm:^5.4.4" - react-native-reanimated: "npm:^4.2.1" + react-native-reanimated: "npm:^4.5.1" react-native-safe-area-context: "npm:^5.6.2" - react-native-screens: "npm:^4.20.0" - react-native-svg: "npm:^15.15.1" + react-native-screens: "npm:^4.26.0" + react-native-svg: "npm:^15.15.5" react-native-vision-camera: "npm:^5.1.0" - react-native-vision-camera-worklets: "npm:^5.0.9" - react-native-worklets: "npm:0.7.1" + react-native-vision-camera-worklets: "npm:^5.1.0" + react-native-worklets: "npm:0.10.2" zustand: "npm:^5.0.9" languageName: unknown linkType: soft @@ -11083,12 +11050,13 @@ __metadata: "@eslint/compat": "npm:^1.3.2" "@eslint/eslintrc": "npm:^3.3.1" "@eslint/js": "npm:^9.35.0" - "@react-native-community/cli": "npm:20.0.1" - "@react-native/babel-preset": "npm:0.81.1" - "@react-native/eslint-config": "npm:^0.81.1" + "@react-native-community/cli": "npm:20.1.0" + "@react-native/babel-preset": "npm:0.86.0" + "@react-native/eslint-config": "npm:^0.86.0" + "@react-native/jest-preset": "npm:0.86.0" "@release-it/conventional-changelog": "npm:^10.0.1" "@types/jest": "npm:^29.5.14" - "@types/react": "npm:^19.1.0" + "@types/react": "npm:^19.2.0" commitlint: "npm:^19.8.1" del-cli: "npm:^6.0.0" eslint: "npm:^9.35.0" @@ -11098,14 +11066,14 @@ __metadata: lefthook: "npm:^2.0.3" nitrogen: "npm:0.36.1" prettier: "npm:^2.8.8" - react: "npm:19.1.0" - react-native: "npm:0.81.1" + react: "npm:19.2.3" + react-native: "npm:0.86.0" react-native-builder-bob: "npm:^0.40.13" - react-native-nitro-image: "npm:0.15.0" + react-native-nitro-image: "npm:0.15.1" react-native-nitro-modules: "npm:0.36.1" react-native-vision-camera: "npm:^5.1.0" - react-native-vision-camera-worklets: "npm:^5.0.9" - react-native-worklets: "npm:0.7.1" + react-native-vision-camera-worklets: "npm:^5.1.0" + react-native-worklets: "npm:0.10.2" release-it: "npm:^19.0.4" turbo: "npm:^2.5.6" typescript: "npm:^5.9.2" @@ -11120,7 +11088,7 @@ __metadata: languageName: unknown linkType: soft -"react-native-vision-camera-worklets@npm:^5.0.9": +"react-native-vision-camera-worklets@npm:^5.1.0": version: 5.1.0 resolution: "react-native-vision-camera-worklets@npm:5.1.0" peerDependencies: @@ -11145,76 +11113,79 @@ __metadata: languageName: node linkType: hard -"react-native-worklets@npm:0.7.1": - version: 0.7.1 - resolution: "react-native-worklets@npm:0.7.1" - dependencies: - "@babel/plugin-transform-arrow-functions": "npm:7.27.1" - "@babel/plugin-transform-class-properties": "npm:7.27.1" - "@babel/plugin-transform-classes": "npm:7.28.4" - "@babel/plugin-transform-nullish-coalescing-operator": "npm:7.27.1" - "@babel/plugin-transform-optional-chaining": "npm:7.27.1" - "@babel/plugin-transform-shorthand-properties": "npm:7.27.1" - "@babel/plugin-transform-template-literals": "npm:7.27.1" - "@babel/plugin-transform-unicode-regex": "npm:7.27.1" - "@babel/preset-typescript": "npm:7.27.1" - convert-source-map: "npm:2.0.0" - semver: "npm:7.7.3" +"react-native-worklets@npm:0.10.2": + version: 0.10.2 + resolution: "react-native-worklets@npm:0.10.2" + dependencies: + "@babel/plugin-transform-arrow-functions": "npm:^7.27.1" + "@babel/plugin-transform-class-properties": "npm:^7.28.6" + "@babel/plugin-transform-classes": "npm:^7.28.6" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.28.6" + "@babel/plugin-transform-optional-chaining": "npm:^7.28.6" + "@babel/plugin-transform-shorthand-properties": "npm:^7.27.1" + "@babel/plugin-transform-template-literals": "npm:^7.27.1" + "@babel/plugin-transform-unicode-regex": "npm:^7.27.1" + "@babel/preset-typescript": "npm:^7.28.5" + "@babel/types": "npm:^7.27.1" + convert-source-map: "npm:^2.0.0" + semver: "npm:^7.7.4" peerDependencies: "@babel/core": "*" + "@react-native/metro-config": "*" react: "*" - react-native: "*" - checksum: 10c0/6965ff3cef7981e10585e15c222fb78f12c54807fcd6f7f9fccdd4dea2bd59d234797fe26a9f47d5c5ba6609f653b807d67f2d03e513e179a25ba28c3ad413ff + react-native: 0.83 - 0.86 + checksum: 10c0/5c4aa9940fb63cd1b0585b618fff84716c028011c9235e10d451b6436c7fb4dec9474a5dd9fa38d74aedd4a5b46f916470fb73aedaca4b0ba47b364d26fbf193 languageName: node linkType: hard -"react-native@npm:0.81.1": - version: 0.81.1 - resolution: "react-native@npm:0.81.1" +"react-native@npm:0.86.0": + version: 0.86.0 + resolution: "react-native@npm:0.86.0" dependencies: - "@jest/create-cache-key-function": "npm:^29.7.0" - "@react-native/assets-registry": "npm:0.81.1" - "@react-native/codegen": "npm:0.81.1" - "@react-native/community-cli-plugin": "npm:0.81.1" - "@react-native/gradle-plugin": "npm:0.81.1" - "@react-native/js-polyfills": "npm:0.81.1" - "@react-native/normalize-colors": "npm:0.81.1" - "@react-native/virtualized-lists": "npm:0.81.1" + "@react-native/assets-registry": "npm:0.86.0" + "@react-native/codegen": "npm:0.86.0" + "@react-native/community-cli-plugin": "npm:0.86.0" + "@react-native/gradle-plugin": "npm:0.86.0" + "@react-native/js-polyfills": "npm:0.86.0" + "@react-native/normalize-colors": "npm:0.86.0" + "@react-native/virtualized-lists": "npm:0.86.0" abort-controller: "npm:^3.0.0" anser: "npm:^1.4.9" ansi-regex: "npm:^5.0.0" - babel-jest: "npm:^29.7.0" - babel-plugin-syntax-hermes-parser: "npm:0.29.1" + babel-plugin-syntax-hermes-parser: "npm:0.36.0" base64-js: "npm:^1.5.1" commander: "npm:^12.0.0" flow-enums-runtime: "npm:^0.0.6" - glob: "npm:^7.1.1" + hermes-compiler: "npm:250829098.0.14" invariant: "npm:^2.2.4" - jest-environment-node: "npm:^29.7.0" memoize-one: "npm:^5.0.0" - metro-runtime: "npm:^0.83.1" - metro-source-map: "npm:^0.83.1" + metro-runtime: "npm:^0.84.3" + metro-source-map: "npm:^0.84.3" nullthrows: "npm:^1.1.1" pretty-format: "npm:^29.7.0" promise: "npm:^8.3.0" react-devtools-core: "npm:^6.1.5" react-refresh: "npm:^0.14.0" regenerator-runtime: "npm:^0.13.2" - scheduler: "npm:0.26.0" + scheduler: "npm:0.27.0" semver: "npm:^7.1.3" stacktrace-parser: "npm:^0.1.10" + tinyglobby: "npm:^0.2.15" whatwg-fetch: "npm:^3.0.0" - ws: "npm:^6.2.3" + ws: "npm:^7.5.10" yargs: "npm:^17.6.2" peerDependencies: - "@types/react": ^19.1.0 - react: ^19.1.0 + "@react-native/jest-preset": 0.86.0 + "@types/react": ^19.1.1 + react: ^19.2.3 peerDependenciesMeta: + "@react-native/jest-preset": + optional: true "@types/react": optional: true bin: react-native: cli.js - checksum: 10c0/d8cf1c441e7b33a9e23eb1e0f9fcbceace05ed49b2222f2fe4f249fb84956684db02643cfc703cdfa7fb23a3e24c80c91f24b6a5acd26d4d5771fe9f18d1cd46 + checksum: 10c0/47a569c47a22b37526ac54e17d1f42c6a191ca7cd30230bc16ee5307ac53da1db89d27ba20c76e9756fb646866d89fedfc1852b1ddc15c47bde45c899079096a languageName: node linkType: hard @@ -11225,10 +11196,10 @@ __metadata: languageName: node linkType: hard -"react@npm:19.1.0": - version: 19.1.0 - resolution: "react@npm:19.1.0" - checksum: 10c0/530fb9a62237d54137a13d2cfb67a7db6a2156faed43eecc423f4713d9b20c6f2728b026b45e28fcd72e8eadb9e9ed4b089e99f5e295d2f0ad3134251bdd3698 +"react@npm:19.2.3": + version: 19.2.3 + resolution: "react@npm:19.2.3" + checksum: 10c0/094220b3ba3a76c1b668f972ace1dd15509b157aead1b40391d1c8e657e720c201d9719537375eff08f5e0514748c0319063392a6f000e31303aafc4471f1436 languageName: node linkType: hard @@ -11603,14 +11574,14 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:0.26.0": - version: 0.26.0 - resolution: "scheduler@npm:0.26.0" - checksum: 10c0/5b8d5bfddaae3513410eda54f2268e98a376a429931921a81b5c3a2873aab7ca4d775a8caac5498f8cbc7d0daeab947cf923dbd8e215d61671f9f4e392d34356 +"scheduler@npm:0.27.0": + version: 0.27.0 + resolution: "scheduler@npm:0.27.0" + checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452 languageName: node linkType: hard -"semver@npm:7.7.3, semver@npm:^7.1.3, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.7.3": +"semver@npm:7.7.3, semver@npm:^7.1.3, semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.7.3": version: 7.7.3 resolution: "semver@npm:7.7.3" bin: @@ -11628,6 +11599,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.7.4": + version: 7.8.5 + resolution: "semver@npm:7.8.5" + bin: + semver: bin/semver.js + checksum: 10c0/b1f3127a5be8125a94f37188b361c212466c292c6910adce3ec106cff5dc211ccaedc4739c11bb70fda59d6fc1f040a9bca289f4e093451521a2372e5231fe0c + languageName: node + linkType: hard + "send@npm:~0.19.1": version: 0.19.2 resolution: "send@npm:0.19.2" @@ -12351,7 +12331,7 @@ __metadata: languageName: node linkType: hard -"tinyglobby@npm:^0.2.14": +"tinyglobby@npm:^0.2.14, tinyglobby@npm:^0.2.15": version: 0.2.17 resolution: "tinyglobby@npm:0.2.17" dependencies: @@ -12384,12 +12364,12 @@ __metadata: languageName: node linkType: hard -"ts-api-utils@npm:^1.3.0": - version: 1.4.3 - resolution: "ts-api-utils@npm:1.4.3" +"ts-api-utils@npm:^2.5.0": + version: 2.5.0 + resolution: "ts-api-utils@npm:2.5.0" peerDependencies: - typescript: ">=4.2.0" - checksum: 10c0/e65dc6e7e8141140c23e1dc94984bf995d4f6801919c71d6dc27cf0cd51b100a91ffcfe5217626193e5bea9d46831e8586febdc7e172df3f1091a7384299e23a + typescript: ">=4.8.4" + checksum: 10c0/767849383c114e7f1971fa976b20e73ac28fd0c70d8d65c0004790bf4d8f89888c7e4cf6d5949f9c1beae9bc3c64835bef77bbe27fddf45a3c7b60cebcf85c8c languageName: node linkType: hard @@ -12403,13 +12383,6 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.8.1": - version: 1.14.1 - resolution: "tslib@npm:1.14.1" - checksum: 10c0/69ae09c49eea644bc5ebe1bca4fa4cc2c82b7b3e02f43b84bd891504edf66dbc6b2ec0eef31a957042de2269139e4acff911e6d186a258fb14069cd7f6febce2 - languageName: node - linkType: hard - "tslib@npm:^2.0.1, tslib@npm:^2.1.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" @@ -12417,17 +12390,6 @@ __metadata: languageName: node linkType: hard -"tsutils@npm:^3.21.0": - version: 3.21.0 - resolution: "tsutils@npm:3.21.0" - dependencies: - tslib: "npm:^1.8.1" - peerDependencies: - typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - checksum: 10c0/02f19e458ec78ead8fffbf711f834ad8ecd2cc6ade4ec0320790713dccc0a412b99e7fd907c4cda2a1dc602c75db6f12e0108e87a5afad4b2f9e90a24cabd5a2 - languageName: node - linkType: hard - "turbo-darwin-64@npm:2.7.2": version: 2.7.2 resolution: "turbo-darwin-64@npm:2.7.2" @@ -12879,7 +12841,7 @@ __metadata: languageName: node linkType: hard -"warn-once@npm:0.1.1, warn-once@npm:^0.1.0, warn-once@npm:^0.1.1": +"warn-once@npm:^0.1.0, warn-once@npm:^0.1.1": version: 0.1.1 resolution: "warn-once@npm:0.1.1" checksum: 10c0/f531e7b2382124f51e6d8f97b8c865246db8ab6ff4e53257a2d274e0f02b97d7201eb35db481843dc155815e154ad7afb53b01c4d4db15fb5aa073562496aff7 @@ -13260,7 +13222,16 @@ __metadata: languageName: node linkType: hard -"zod@npm:^4.4.3": +"zod-validation-error@npm:^3.5.0 || ^4.0.0": + version: 4.0.2 + resolution: "zod-validation-error@npm:4.0.2" + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + checksum: 10c0/0ccfec48c46de1be440b719cd02044d4abb89ed0e14c13e637cd55bf29102f67ccdba373f25def0fc7130e5f15025be4d557a7edcc95d5a3811599aade689e1b + languageName: node + linkType: hard + +"zod@npm:^3.25.0 || ^4.0.0, zod@npm:^4.4.3": version: 4.4.3 resolution: "zod@npm:4.4.3" checksum: 10c0/7ea31b558e88f9faf44f31dd185e2e1cbf51fed3081787fb96cc2534749b50c0acfc6da7f0922a7353ed092dd358c7d50c28ea96c94d04af64191bd33152eca3 From f817ee6f0a23271c830abd0f5a73c2cc9f3c199e Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 12 Jul 2026 18:34:13 -0300 Subject: [PATCH 24/45] fix(android): populate full BoundingBox fields in Nitro conversion The Android-to-Nitro BoundingBox mapping only set x/y/width/height, leaving centerX, centerY, top, left, bottom and right at their zero default even though the JS BoundingBox type declares all of them. --- .../nitro/visioncameramlkit/BoundingBox+toNitro.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BoundingBox+toNitro.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BoundingBox+toNitro.kt index 08319f6..dc6cf4f 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BoundingBox+toNitro.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BoundingBox+toNitro.kt @@ -4,8 +4,14 @@ private val emptyBoundingBox = BoundingBox( x = 0.0, y = 0.0, + centerX = 0.0, + centerY = 0.0, width = 0.0, height = 0.0, + top = 0.0, + left = 0.0, + bottom = 0.0, + right = 0.0, ) internal fun com.visioncameramlkit.domain.models.BoundingBox?.toNitroBoundingBox(): BoundingBox = @@ -13,7 +19,13 @@ internal fun com.visioncameramlkit.domain.models.BoundingBox?.toNitroBoundingBox BoundingBox( x = it.x, y = it.y, + centerX = it.centerX, + centerY = it.centerY, width = it.width, height = it.height, + top = it.top, + left = it.left, + bottom = it.bottom, + right = it.right, ) } ?: emptyBoundingBox From 94a3835fc3289a245d32d247ceb50737a875db1a Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 12 Jul 2026 18:34:32 -0300 Subject: [PATCH 25/45] fix(frame-processor): avoid nested worklet closures breaking cross-runtime execution useMLKitPlugin wrapped the native TextRecognizer/BarcodeScanner HybridObject in a JS-level worklet function, and useTextRecognition/ useBarcodeScanning wrapped that again in their own worklet. A worklet closing over another worklet, two levels deep, does not reliably survive being re-shared into a different Worklet Runtime, degrading into a non-callable object and throwing "Object is not a function" whenever called through useAsyncRunner's native-thread-backed runtime. useMLKitPlugin now returns the memoized native HybridObject directly, so each feature hook only needs a single worklet layer closing directly over it. CameraView also drops the useAsyncRunner hop entirely and calls the plugin inside the top-level onFrame worklet, matching the pattern the library's README documents. --- example/src/components/views/CameraView.tsx | 50 +++++++++------------ src/features/barcode-scanning/index.ts | 28 +++++++----- src/features/text-recognition/index.ts | 28 +++++++----- src/hooks/useMLKitPlugin.ts | 41 +++++++---------- 4 files changed, 73 insertions(+), 74 deletions(-) diff --git a/example/src/components/views/CameraView.tsx b/example/src/components/views/CameraView.tsx index 01bc5e0..81f401f 100644 --- a/example/src/components/views/CameraView.tsx +++ b/example/src/components/views/CameraView.tsx @@ -17,7 +17,6 @@ import Reanimated, { import { Camera, CommonResolutions, - useAsyncRunner, useFrameOutput, type CameraRef, } from 'react-native-vision-camera'; @@ -95,7 +94,6 @@ const CameraView = forwardRef( const scale = useSharedValue(0); const opacity = useSharedValue(0); const frameCount = useSharedValue(0); - const asyncRunner = useAsyncRunner(); const frameInterval = useMemo( () => Math.max(1, Math.round(30 / frameProcessorFps)), @@ -171,36 +169,28 @@ const CameraView = forwardRef( return; } - const accepted = asyncRunner.runAsync(() => { - 'worklet'; - - try { - let resultObject: any = null; - const withArguments = { - outputOrientation: frameOutputOrientation.value, - }; - - if (pluginId === PLUGIN_ID.TEXT_RECOGNITION) { - resultObject = textRecognitionPlugin.textRecognition( - frame, - withArguments - ); - } else if (pluginId === PLUGIN_ID.BARCODE_SCANNING) { - resultObject = barcodeScanningPlugin.barcodeScanning( - frame, - withArguments - ); - } - - if (resultObject) { - scheduleOnRN(addResult, resultObject); - } - } finally { - frame.dispose(); + try { + let resultObject: any = null; + const withArguments = { + outputOrientation: frameOutputOrientation.value, + }; + + if (pluginId === PLUGIN_ID.TEXT_RECOGNITION) { + resultObject = textRecognitionPlugin.textRecognition( + frame, + withArguments + ); + } else if (pluginId === PLUGIN_ID.BARCODE_SCANNING) { + resultObject = barcodeScanningPlugin.barcodeScanning( + frame, + withArguments + ); } - }); - if (!accepted) { + if (resultObject) { + scheduleOnRN(addResult, resultObject); + } + } finally { frame.dispose(); } }, diff --git a/src/features/barcode-scanning/index.ts b/src/features/barcode-scanning/index.ts index df3ac2b..520e222 100644 --- a/src/features/barcode-scanning/index.ts +++ b/src/features/barcode-scanning/index.ts @@ -1,7 +1,9 @@ +import { useMemo } from 'react'; import type { Frame } from 'react-native-vision-camera'; import { NativeBridge } from '../../core/NativeBridge'; import { MLKIT_FEATURE_KEYS } from '../../core/constants'; import { useMLKitPlugin } from '../../hooks/useMLKitPlugin'; +import type { BarcodeScanner } from '../../specs/BarcodeScanner.nitro'; import type { BarcodeScanningArguments, BarcodeScanningImageOptions, @@ -27,17 +29,23 @@ export const processImageBarcodeScanning = async ( * Hook for barcode scanning from camera frames. */ export const useBarcodeScanning = (options: BarcodeScanningOptions = {}) => { - const plugin = useMLKitPlugin(MLKIT_FEATURE_KEYS.BARCODE_SCANNING, options); + const plugin = useMLKitPlugin( + MLKIT_FEATURE_KEYS.BARCODE_SCANNING, + options + ); - return { - barcodeScanning: ( - frame: Frame, - args?: BarcodeScanningArguments - ): BarcodeScanningResult => { - 'worklet'; - return plugin.recognize(frame, args); - }, - }; + return useMemo( + () => ({ + barcodeScanning: ( + frame: Frame, + args?: BarcodeScanningArguments + ): BarcodeScanningResult => { + 'worklet'; + return plugin.recognize(frame, args ?? {}) as BarcodeScanningResult; + }, + }), + [plugin] + ); }; export type { diff --git a/src/features/text-recognition/index.ts b/src/features/text-recognition/index.ts index af698d7..8b72bf5 100644 --- a/src/features/text-recognition/index.ts +++ b/src/features/text-recognition/index.ts @@ -1,7 +1,9 @@ +import { useMemo } from 'react'; import type { Frame } from 'react-native-vision-camera'; import { NativeBridge } from '../../core/NativeBridge'; import { MLKIT_FEATURE_KEYS } from '../../core/constants'; import { useMLKitPlugin } from '../../hooks/useMLKitPlugin'; +import type { TextRecognizer } from '../../specs/TextRecognizer.nitro'; import type { TextRecognitionArguments, TextRecognitionImageOptions, @@ -32,17 +34,23 @@ export const processImageTextRecognition = async ( * @returns Plugin object with textRecognition method. */ export const useTextRecognition = (options: TextRecognitionOptions = {}) => { - const plugin = useMLKitPlugin(MLKIT_FEATURE_KEYS.TEXT_RECOGNITION, options); + const plugin = useMLKitPlugin( + MLKIT_FEATURE_KEYS.TEXT_RECOGNITION, + options + ); - return { - textRecognition: ( - frame: Frame, - args?: TextRecognitionArguments - ): TextRecognitionResult => { - 'worklet'; - return plugin.recognize(frame, args); - }, - }; + return useMemo( + () => ({ + textRecognition: ( + frame: Frame, + args?: TextRecognitionArguments + ): TextRecognitionResult => { + 'worklet'; + return plugin.recognize(frame, args ?? {}); + }, + }), + [plugin] + ); }; export type { diff --git a/src/hooks/useMLKitPlugin.ts b/src/hooks/useMLKitPlugin.ts index 345b6ab..08b08a2 100644 --- a/src/hooks/useMLKitPlugin.ts +++ b/src/hooks/useMLKitPlugin.ts @@ -1,34 +1,27 @@ import { useMemo } from 'react'; -import type { Frame } from 'react-native-vision-camera'; import { PluginFactory } from '../core/PluginFactory'; -import type { - MLKitBaseArguments, - MLKitBaseOptions, - MLKitFeature, -} from '../core/types'; - -export interface MLKitPlugin { - recognize: (frame: Frame, args?: MLKitBaseArguments) => any; -} +import type { MLKitBaseOptions, MLKitFeature } from '../core/types'; /** - * Generic hook to create and use any MLKit frame processor plugin. + * Generic hook to create any MLKit frame processor plugin. + * + * Returns the native `HybridObject` directly (memoized) rather than a + * JS-level wrapper, so callers can invoke its `recognize` method from a + * single Worklet closure. Wrapping it in an extra `'worklet'` function + * here would create a worklet-closing-over-a-worklet chain that does not + * reliably survive being shared into a different Worklet Runtime (e.g. + * via `useAsyncRunner`'s native-thread-backed runtime). + * @template T - The concrete HybridObject type for the requested feature. * @param {MLKitFeature} feature - The MLKit feature to use. * @param {MLKitBaseOptions} options - Options for the plugin. - * @returns {MLKitPlugin} An object with a recognize method for processing frames. + * @returns The native MLKit plugin HybridObject. */ -export const useMLKitPlugin = ( +export const useMLKitPlugin = ( feature: MLKitFeature, options: MLKitBaseOptions = {} -): MLKitPlugin => { - return useMemo(() => { - const plugin = PluginFactory.initPlugin(feature, options); - - return { - recognize: (frame: Frame, args?: MLKitBaseArguments): any => { - 'worklet'; - return plugin.recognize(frame, args ?? {}); - }, - }; - }, [feature, options]); +): T => { + return useMemo( + () => PluginFactory.initPlugin(feature, options) as T, + [feature, options] + ); }; From 985570ef4ae150704f67a2b1119ba72132eed1e9 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 12 Jul 2026 19:06:00 -0300 Subject: [PATCH 26/45] fix(camera): unify gesture handling, restore focus overlay, and add live FPS graph CameraView enabled VisionCamera v5's native gesture controllers (enableNativeZoomGesture, enableNativeTapToFocusGesture) at the same time as a hand-rolled react-native-gesture-handler GestureDetector for tap-to- focus and double-tap-to-flip. Native UIKit/CameraX recognizers intercept touches before RNGH's JS-driven detector ever sees them, so enabling any native gesture silenced the RNGH gestures entirely, and disabling zoom let pinch touches misfire as double-taps. It also meant tap-to-focus never called back into JS, so the focus-ring overlay never rendered. All gestures now live in one RNGH composition: pinch-to-zoom drives a Reanimated-backed zoom SharedValue via Gesture.Pinch(), combined with the existing tap/double-tap Gesture.Exclusive() through Gesture.Simultaneous(). The native gesture props are removed. Also: - Fixes a Reanimated "reading `value` during render" violation where `savedZoom` was seeded by reading `zoom.value` instead of the same plain initial expression. - Stops logging the expected "metering operation has been canceled" rejection that fires whenever a new focus request supersedes a still- settling one. - Restores the FPS graph toggle, which the v5 migration left as a dead no-op (state value was discarded and the `enableFpsGraph` Camera prop it fed was removed upstream in v5). Frame arrivals are now counted in the onFrame worklet and sampled on an interval to drive a real FpsGraphOverlay component. --- example/src/components/views/CameraView.tsx | 80 +++++++++++++++++-- .../src/components/views/FpsGraphOverlay.tsx | 66 +++++++++++++++ example/src/components/views/index.ts | 1 + example/src/screens/CameraScreen.tsx | 3 +- 4 files changed, 144 insertions(+), 6 deletions(-) create mode 100644 example/src/components/views/FpsGraphOverlay.tsx diff --git a/example/src/components/views/CameraView.tsx b/example/src/components/views/CameraView.tsx index 81f401f..19a8a70 100644 --- a/example/src/components/views/CameraView.tsx +++ b/example/src/components/views/CameraView.tsx @@ -1,6 +1,7 @@ import { forwardRef, useCallback, + useEffect, useMemo, useState, type ComponentProps, @@ -18,6 +19,7 @@ import { Camera, CommonResolutions, useFrameOutput, + type CameraDevice, type CameraRef, } from 'react-native-vision-camera'; import { @@ -33,6 +35,7 @@ import { useSettingsStore, useTerminalStore, } from '../../stores'; +import { FpsGraphOverlay } from './FpsGraphOverlay'; const normalizeResultObject = (data: unknown) => { if (data === null || data === undefined) return data; @@ -46,10 +49,16 @@ const normalizeResultObject = (data: unknown) => { } }; -type CameraViewProps = ComponentProps & { +const FPS_SAMPLE_INTERVAL_MS = 500; +const FPS_HISTORY_LENGTH = 20; +const FPS_GRAPH_MAX_SCALE = 60; + +type CameraViewProps = Omit, 'device'> & { + device: CameraDevice; pluginId: PLUGIN_ID; flipCamera: () => void; isFrameProcessorEnabled?: boolean; + isFpsGraphEnabled?: boolean; frameOutputOrientation: SharedValue; }; @@ -61,6 +70,7 @@ const CameraView = forwardRef( pluginId, flipCamera, isFrameProcessorEnabled = true, + isFpsGraphEnabled = false, frameOutputOrientation, torchMode, ...props @@ -77,6 +87,30 @@ const CameraView = forwardRef( } = useSettingsStore(); const { sharedOptions, pluginOptions } = usePluginOptionsStore(); + const frameArrivalCount = useSharedValue(0); + const [fps, setFps] = useState(0); + const [fpsHistory, setFpsHistory] = useState([]); + + useEffect(() => { + if (!isFpsGraphEnabled) return; + + const interval = setInterval(() => { + const framesSinceLastSample = frameArrivalCount.value; + frameArrivalCount.value = 0; + + const instantaneousFps = Math.round( + (framesSinceLastSample * 1000) / FPS_SAMPLE_INTERVAL_MS + ); + + setFps(instantaneousFps); + setFpsHistory((prev) => + [...prev, instantaneousFps].slice(-FPS_HISTORY_LENGTH) + ); + }, FPS_SAMPLE_INTERVAL_MS); + + return () => clearInterval(interval); + }, [isFpsGraphEnabled, frameArrivalCount]); + const textRecognitionPlugin = useTextRecognition({ language: pluginOptions[PLUGIN_ID.TEXT_RECOGNITION].language, ...sharedOptions, @@ -94,6 +128,12 @@ const CameraView = forwardRef( const scale = useSharedValue(0); const opacity = useSharedValue(0); const frameCount = useSharedValue(0); + const zoom = useSharedValue(device?.minZoom ?? 1); + const savedZoom = useSharedValue(device?.minZoom ?? 1); + + useEffect(() => { + zoom.value = device?.minZoom ?? 1; + }, [device, zoom]); const frameInterval = useMemo( () => Math.max(1, Math.round(30 / frameProcessorFps)), @@ -104,7 +144,12 @@ const CameraView = forwardRef( (point: { x: number; y: number }) => { if (typeof ref === 'function' || !ref?.current) return; - ref.current.focusTo(point).catch(console.error); + ref.current.focusTo(point).catch((error: Error) => { + // A new focus request cancels any still-settling previous one - + // this is expected whenever the user taps again quickly. + if (error.message.includes('canceled')) return; + console.error(error); + }); setFocusPoint(point); scale.value = 0; @@ -133,13 +178,30 @@ const CameraView = forwardRef( scheduleOnRN(flipCamera); }); + const pinchGesture = Gesture.Pinch() + .onStart(() => { + savedZoom.value = zoom.value; + }) + .onUpdate((event) => { + if (!device) return; + const nextZoom = savedZoom.value * event.scale; + zoom.value = Math.min( + Math.max(nextZoom, device.minZoom), + device.maxZoom + ); + }); + const gestures = []; if (enableDoubleTapGesture) gestures.push(doubleTapGesture); if (enableTapGesture) gestures.push(tapGesture); - const gesture = + const tapGestureComposition = gestures.length > 0 ? Gesture.Exclusive(...gestures) : Gesture.Tap(); + const gesture = enableZoomGesture + ? Gesture.Simultaneous(pinchGesture, tapGestureComposition) + : tapGestureComposition; + const focusOverlayStyle = useAnimatedStyle(() => ({ opacity: opacity.value, transform: [{ scale: scale.value }], @@ -158,6 +220,8 @@ const CameraView = forwardRef( onFrame(frame) { 'worklet'; + frameArrivalCount.value += 1; + if (!isFrameProcessorEnabled) { frame.dispose(); return; @@ -211,8 +275,7 @@ const CameraView = forwardRef( outputs={[frameOutput]} constraints={[{ resolutionBias: frameOutput }]} torchMode={torchMode} - enableNativeZoomGesture={enableZoomGesture} - enableNativeTapToFocusGesture={enableTapGesture} + zoom={zoom} resizeMode="cover" style={styles.container} /> @@ -230,6 +293,13 @@ const CameraView = forwardRef( ]} /> )} + {isFpsGraphEnabled && ( + + )} ); diff --git a/example/src/components/views/FpsGraphOverlay.tsx b/example/src/components/views/FpsGraphOverlay.tsx new file mode 100644 index 0000000..cd9fc8f --- /dev/null +++ b/example/src/components/views/FpsGraphOverlay.tsx @@ -0,0 +1,66 @@ +import { StyleSheet, Text, View } from 'react-native'; +import { useTheme } from '../../providers/ThemeProvider'; + +type FpsGraphOverlayProps = { + fps: number; + history: number[]; + maxFps: number; +}; + +const FpsGraphOverlay = ({ fps, history, maxFps }: FpsGraphOverlayProps) => { + const { theme } = useTheme(); + + return ( + + + {fps} FPS + + + {history.map((sample, index) => ( + + ))} + + + ); +}; + +const styles = StyleSheet.create({ + container: { + position: 'absolute', + top: 12, + left: 12, + borderRadius: 8, + paddingHorizontal: 8, + paddingVertical: 6, + opacity: 0.9, + }, + label: { + fontSize: 12, + fontWeight: '600', + marginBottom: 4, + }, + bars: { + flexDirection: 'row', + alignItems: 'flex-end', + height: 28, + gap: 2, + }, + bar: { + width: 3, + borderRadius: 1, + }, +}); + +export { FpsGraphOverlay }; diff --git a/example/src/components/views/index.ts b/example/src/components/views/index.ts index 576ceff..27da1fb 100644 --- a/example/src/components/views/index.ts +++ b/example/src/components/views/index.ts @@ -1,4 +1,5 @@ export { CameraView } from './CameraView'; +export { FpsGraphOverlay } from './FpsGraphOverlay'; export { HeaderView } from './HeaderView'; export { NoCameraPermissionErrorView } from './NoCameraPermissionErrorView'; export { NoDeviceErrorView } from './NoDeviceErrorView'; diff --git a/example/src/screens/CameraScreen.tsx b/example/src/screens/CameraScreen.tsx index 2b2e581..9af6083 100644 --- a/example/src/screens/CameraScreen.tsx +++ b/example/src/screens/CameraScreen.tsx @@ -43,7 +43,7 @@ const CameraScreen = () => { const [cameraPosition, setCameraPosition] = useState('back'); const [torchState, setTorchState] = useState('off'); - const [, setIsFpsGraphEnabled] = useState(false); + const [isFpsGraphEnabled, setIsFpsGraphEnabled] = useState(false); const outputOrientation = useSharedValue(DEFAULT_ORIENTATION); @@ -91,6 +91,7 @@ const CameraScreen = () => { isFrameProcessorEnabled={isFrameProcessorEnabled} frameOutputOrientation={outputOrientation} torchMode={torchState} + isFpsGraphEnabled={isFpsGraphEnabled} /> Date: Sun, 12 Jul 2026 19:06:20 -0300 Subject: [PATCH 27/45] fix(deps): upgrade @gorhom/bottom-sheet to 5.2.14 for gesture/reanimated compatibility The Terminal bottom sheet presented but never animated open. Installed 5.2.8 predates react-native-gesture-handler v3.0.0 (2026-05-28) and bottom-sheet's own "Full support for react-native-reanimated 4 and worklets" issue was still open upstream at the time. 5.2.14 was already within the declared "^5" range but had never been re-resolved. --- example/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/example/package.json b/example/package.json index 3bd51a8..4268e09 100644 --- a/example/package.json +++ b/example/package.json @@ -10,7 +10,7 @@ "build:ios": "react-native build-ios --mode Debug --destination \"${IOS_DESTINATION:-generic/platform=iOS Simulator}\"" }, "dependencies": { - "@gorhom/bottom-sheet": "^5", + "@gorhom/bottom-sheet": "^5.2.14", "@react-native-community/blur": "^4.4.1", "@react-native-community/slider": "^5.1.2", "@react-navigation/elements": "^2.9.3", diff --git a/yarn.lock b/yarn.lock index 8f35795..f3ffe8d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2200,9 +2200,9 @@ __metadata: languageName: node linkType: hard -"@gorhom/bottom-sheet@npm:^5": - version: 5.2.8 - resolution: "@gorhom/bottom-sheet@npm:5.2.8" +"@gorhom/bottom-sheet@npm:^5.2.14": + version: 5.2.14 + resolution: "@gorhom/bottom-sheet@npm:5.2.14" dependencies: "@gorhom/portal": "npm:1.0.14" invariant: "npm:^2.2.4" @@ -2218,7 +2218,7 @@ __metadata: optional: true "@types/react-native": optional: true - checksum: 10c0/9bb71a375cdcaec650e8c84ab5e3e42f55b3ae19d9e08cf90688cf72706588d211f3aeef28b857a37e9e411efa8fdf038429c90a8855167c7ab3dce5c595dfca + checksum: 10c0/744c718aaa4e66989043378932ea31d8ff510cff289b5561115530ff49218af080d28dbe409191b33ab7d87b1cfab4e7c16cfb0015cf2b6917b26c61f033bc05 languageName: node linkType: hard @@ -11007,7 +11007,7 @@ __metadata: "@babel/core": "npm:^7.25.2" "@babel/preset-env": "npm:^7.25.3" "@babel/runtime": "npm:^7.25.0" - "@gorhom/bottom-sheet": "npm:^5" + "@gorhom/bottom-sheet": "npm:^5.2.14" "@react-native-community/blur": "npm:^4.4.1" "@react-native-community/cli": "npm:20.1.0" "@react-native-community/cli-platform-android": "npm:20.1.0" From 90db2a36ed2e1ca0c31ad5d768d02c67dac3ef57 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 12 Jul 2026 20:08:51 -0300 Subject: [PATCH 28/45] feat(example): add exposure, low-light boost, FPS, stabilization, and orientation settings Surfaces VisionCamera v5 Camera-behavior capabilities that were fully supported by the library but never exposed in the Settings tab: - Exposure Bias slider, clamped to the device's supported range. - Low Light Boost switch, gated on device.supportsLowLightBoost - the enableLowLightBoost prop is only included on when it should be true, since passing it as false still triggers native validation and throws on devices that don't support the feature at all. - Target FPS constraint picker (15/24/30/60/120), applied after the existing resolutionBias constraint so the ML frame output's resolution match keeps priority. - Video Stabilization mode picker, covering all 8 real TargetStabilizationMode values. - Orientation Source picker (interface/device). Also cleans up stale copy: relabels "Frame Processor FPS (runAtTargetFps)" (runAtTargetFps doesn't exist in v5) to describe the actual JS-side frame throttle, and removes the deprecated "Frame Process Interval" picker, which duplicated that throttle at the native level via an option already marked @deprecated. --- example/src/components/views/CameraView.tsx | 29 ++++++- example/src/screens/SettingsScreen.tsx | 91 +++++++++++++++++---- example/src/stores/settingsStore.ts | 27 ++++++ 3 files changed, 128 insertions(+), 19 deletions(-) diff --git a/example/src/components/views/CameraView.tsx b/example/src/components/views/CameraView.tsx index 19a8a70..97e8a63 100644 --- a/example/src/components/views/CameraView.tsx +++ b/example/src/components/views/CameraView.tsx @@ -84,6 +84,11 @@ const CameraView = forwardRef( enableZoomGesture, enableTapGesture, enableDoubleTapGesture, + exposureBias, + enableLowLightBoost, + targetFps, + videoStabilizationMode, + orientationSource, } = useSettingsStore(); const { sharedOptions, pluginOptions } = usePluginOptionsStore(); @@ -130,11 +135,24 @@ const CameraView = forwardRef( const frameCount = useSharedValue(0); const zoom = useSharedValue(device?.minZoom ?? 1); const savedZoom = useSharedValue(device?.minZoom ?? 1); + const exposure = useSharedValue(0); useEffect(() => { zoom.value = device?.minZoom ?? 1; }, [device, zoom]); + useEffect(() => { + if (!device?.supportsExposureBias) { + exposure.value = 0; + return; + } + + exposure.value = Math.min( + Math.max(exposureBias, device.minExposureBias), + device.maxExposureBias + ); + }, [device, exposureBias, exposure]); + const frameInterval = useMemo( () => Math.max(1, Math.round(30 / frameProcessorFps)), [frameProcessorFps] @@ -269,13 +287,22 @@ const CameraView = forwardRef( > diff --git a/example/src/screens/SettingsScreen.tsx b/example/src/screens/SettingsScreen.tsx index 174458a..f5f9d7e 100644 --- a/example/src/screens/SettingsScreen.tsx +++ b/example/src/screens/SettingsScreen.tsx @@ -27,6 +27,16 @@ const SettingsScreen = () => { setEnableTapGesture, enableDoubleTapGesture, setEnableDoubleTapGesture, + exposureBias, + setExposureBias, + enableLowLightBoost, + setEnableLowLightBoost, + targetFps, + setTargetFps, + videoStabilizationMode, + setVideoStabilizationMode, + orientationSource, + setOrientationSource, } = useSettingsStore(); const { sharedOptions, pluginOptions, setSharedOption, setPluginOption } = @@ -46,8 +56,8 @@ const SettingsScreen = () => { onValueChange={setFrameProcessorEnabled} /> { value={Boolean(sharedOptions.invertColors)} onValueChange={(value) => setSharedOption('invertColors', value)} /> - - setSharedOption('frameProcessInterval', Number(value)) - } - /> { ]} onValueChange={setPixelFormat} /> + + + setTargetFps(Number(value))} + /> + + setVideoStabilizationMode(value as typeof videoStabilizationMode) + } + /> + + setOrientationSource(value as typeof orientationSource) + } + /> void; enableDoubleTapGesture: boolean; setEnableDoubleTapGesture: (enabled: boolean) => void; + exposureBias: number; + setExposureBias: (bias: number) => void; + enableLowLightBoost: boolean; + setEnableLowLightBoost: (enabled: boolean) => void; + targetFps: number; + setTargetFps: (fps: number) => void; + videoStabilizationMode: TargetStabilizationMode; + setVideoStabilizationMode: (mode: TargetStabilizationMode) => void; + orientationSource: OrientationSource; + setOrientationSource: (source: OrientationSource) => void; }; export const useSettingsStore = create((set) => ({ @@ -33,4 +47,17 @@ export const useSettingsStore = create((set) => ({ enableDoubleTapGesture: true, setEnableDoubleTapGesture: (enabled: boolean) => set({ enableDoubleTapGesture: enabled }), + exposureBias: 0, + setExposureBias: (bias: number) => set({ exposureBias: bias }), + enableLowLightBoost: false, + setEnableLowLightBoost: (enabled: boolean) => + set({ enableLowLightBoost: enabled }), + targetFps: 30, + setTargetFps: (fps: number) => set({ targetFps: fps }), + videoStabilizationMode: 'auto', + setVideoStabilizationMode: (mode: TargetStabilizationMode) => + set({ videoStabilizationMode: mode }), + orientationSource: 'device', + setOrientationSource: (source: OrientationSource) => + set({ orientationSource: source }), })); From cb70bd865b00ddd618979f8af2fd090fd76ddbfd Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 12 Jul 2026 20:09:12 -0300 Subject: [PATCH 29/45] docs: rewrite README and API docs for VisionCamera v5 README.md, docs/text-recognition.md, and docs/barcode-scanning.md still documented the removed v4 API end-to-end (useFrameProcessor, runAsync(frame, cb), runAtTargetFps, react-native-worklets-core plus its Babel plugin) and listed the wrong peer dependencies, even though the library and example app were migrated to v5 earlier. runAtTargetFps no longer exists anywhere in react-native-vision-camera's source. Replaces every v4 reference with the real v5 usage (useFrameOutput, direct in-worklet calls, frame.dispose(), the actual v5 peer deps and Babel plugin), and points links at visioncamera.margelo.com instead of the old react-native-vision-camera.com domain. Also rewords the frameProcessInterval JSDoc in src/core/types.ts, which pointed consumers at the same removed runAtTargetFps API. --- README.md | 136 +++++++++++++++++++++------------------ docs/barcode-scanning.md | 27 ++++++-- docs/text-recognition.md | 27 ++++++-- src/core/types.ts | 15 +++-- 4 files changed, 121 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 4b2bc93..91c24cf 100644 --- a/README.md +++ b/README.md @@ -16,40 +16,35 @@ [![MIT License][license-shield]][license-url] [![NPM Version][npm-version-shield]][npm-version-url] -A [React Native Vision Camera](https://github.com/mrousavy/react-native-vision-camera) plugin that exposes high-performance [Google ML Kit](https://developers.google.com/ml-kit) [frame processor](https://react-native-vision-camera.com/docs/guides/frame-processors) features such as text recognition (OCR), face detection, barcode scanning, pose detection, and more. +A [React Native Vision Camera](https://github.com/mrousavy/react-native-vision-camera) plugin that exposes high-performance [Google ML Kit](https://developers.google.com/ml-kit) [frame output](https://visioncamera.margelo.com/docs/frame-output) features such as text recognition (OCR), face detection, barcode scanning, pose detection, and more. > The example app is intentionally heavy and demo-focused. For integration details, follow the documentation below. ## Requirements - iOS 12+ and Android SDK 21+ -- [react-native-vision-camera](https://www.npmjs.com/package/react-native-vision-camera) -- [react-native-worklets-core](https://www.npmjs.com/package/react-native-worklets-core) +- [react-native-vision-camera](https://www.npmjs.com/package/react-native-vision-camera) `>=5` (Nitro-based v5, not v4) +- [react-native-nitro-modules](https://www.npmjs.com/package/react-native-nitro-modules) +- [react-native-nitro-image](https://www.npmjs.com/package/react-native-nitro-image) +- [react-native-vision-camera-worklets](https://www.npmjs.com/package/react-native-vision-camera-worklets) +- [react-native-worklets](https://www.npmjs.com/package/react-native-worklets) -Install Vision Camera (React Native): +Install Vision Camera and its Nitro/worklets dependencies: ```sh -npm i react-native-vision-camera +npm i react-native-vision-camera react-native-nitro-modules react-native-nitro-image react-native-vision-camera-worklets react-native-worklets cd ios && pod install ``` -Install Worklets Core: - -```sh -npm i react-native-worklets-core -# or -yarn add react-native-worklets-core -``` - Add the Babel plugin in `babel.config.js`: ```js module.exports = { - plugins: [['react-native-worklets-core/plugin']], + plugins: [['react-native-worklets/plugin']], }; ``` -> For Expo, follow the Vision Camera guide: [react-native-vision-camera.com/docs/guides](https://react-native-vision-camera.com/docs/guides) +> For Expo, follow the Vision Camera guide: [visioncamera.margelo.com/docs](https://visioncamera.margelo.com/docs) ## Installation @@ -123,13 +118,13 @@ Android-only keys: `faceMeshDetection`, `subjectSegmentation`, `documentScanner` - [Text Recognition API](docs/text-recognition.md) - [Barcode Scanning API](docs/barcode-scanning.md) -### Text Recognition (Frame Processor) +### Text Recognition (Frame Output) ```ts import { - useFrameProcessor, - runAsync, - runAtTargetFps, + useFrameOutput, + CommonResolutions, + Camera, } from 'react-native-vision-camera'; import { useTextRecognition } from 'react-native-vision-camera-mlkit'; @@ -139,23 +134,29 @@ const { textRecognition } = useTextRecognition({ invertColors: false, }); -const frameProcessor = useFrameProcessor( - (frame) => { +const frameOutput = useFrameOutput({ + targetResolution: CommonResolutions.VGA_16_9, + pixelFormat: 'yuv', + onFrame(frame) { 'worklet'; - runAtTargetFps(10, () => { - 'worklet'; - runAsync(frame, () => { - 'worklet'; - const result = textRecognition(frame, { - outputOrientation: 'portrait', - }); - console.log(result.text); + try { + const result = textRecognition(frame, { + outputOrientation: 'portrait', }); - }); + console.log(result.text); + } finally { + frame.dispose(); + } }, - [textRecognition] -); +}); + +; ``` `TextRecognitionOptions`: @@ -163,7 +164,7 @@ const frameProcessor = useFrameProcessor( - `language?: 'LATIN' | 'CHINESE' | 'DEVANAGARI' | 'JAPANESE' | 'KOREAN'` - `scaleFactor?: number` (0.9-1.0) - `invertColors?: boolean` -- `frameProcessInterval?: number` (deprecated, use `runAtTargetFps`) +- `frameProcessInterval?: number` (deprecated, throttle frames manually inside `onFrame` instead) `TextRecognitionArguments`: @@ -193,13 +194,13 @@ console.log(result.blocks); > The native bridge normalizes URIs (`file://` is removed on iOS and added on Android if missing). Supported formats: JPEG, PNG, WebP. -### Barcode Scanning (Frame Processor) +### Barcode Scanning (Frame Output) ```ts import { - useFrameProcessor, - runAsync, - runAtTargetFps, + useFrameOutput, + CommonResolutions, + Camera, } from 'react-native-vision-camera'; import { useBarcodeScanning } from 'react-native-vision-camera-mlkit'; @@ -210,33 +211,39 @@ const { barcodeScanning } = useBarcodeScanning({ invertColors: false, }); -const frameProcessor = useFrameProcessor( - (frame) => { +const frameOutput = useFrameOutput({ + targetResolution: CommonResolutions.VGA_16_9, + pixelFormat: 'yuv', + onFrame(frame) { 'worklet'; - runAtTargetFps(10, () => { - 'worklet'; - runAsync(frame, () => { - 'worklet'; - const result = barcodeScanning(frame, { - outputOrientation: 'portrait', - }); - - for (const barcode of result.barcodes) { - console.log( - barcode.formatName, - barcode.valueTypeName, - barcode.rawValue - ); - if (barcode.value?.type === 'TYPE_URL') { - console.log(barcode.value.data.url); - } - } + try { + const result = barcodeScanning(frame, { + outputOrientation: 'portrait', }); - }); + + for (const barcode of result.barcodes) { + console.log( + barcode.formatName, + barcode.valueTypeName, + barcode.rawValue + ); + if (barcode.value?.type === 'TYPE_URL') { + console.log(barcode.value.data.url); + } + } + } finally { + frame.dispose(); + } }, - [barcodeScanning] -); +}); + +; ``` `BarcodeScanningOptions`: @@ -245,7 +252,7 @@ const frameProcessor = useFrameProcessor( - `enableAllPotentialBarcodes?: boolean` (Android only) - `scaleFactor?: number` (0.9-1.0) - `invertColors?: boolean` -- `frameProcessInterval?: number` (deprecated, use `runAtTargetFps`) +- `frameProcessInterval?: number` (deprecated, throttle frames manually inside `onFrame` instead) Supported `formats` values: @@ -344,9 +351,10 @@ assertFeatureAvailable(MLKIT_FEATURE_KEYS.TEXT_RECOGNITION); ## Performance -- Follow the Vision Camera [performance guide](https://react-native-vision-camera.com/docs/guides/performance) -- Prefer `runAsync(...)` for heavy ML work to keep the frame processor responsive. -- Use `runAtTargetFps(...)` to throttle processing instead of `frameProcessInterval`. +- Follow the Vision Camera [performance guide](https://visioncamera.margelo.com/docs/performance) +- Always call `frame.dispose()` exactly once per frame inside `onFrame` - the Camera pipeline reuses a small buffer pool and stalls (dropping frames) if buffers aren't released. +- Throttle processing by skipping frames yourself inside `onFrame` (see the `frameProcessInterval` deprecation note above) instead of relying on a native-side interval. +- `useAsyncRunner()` can offload genuinely independent heavy work to a separate thread, but avoid wrapping a single native plugin call in it - passing a plugin's worklet-callable method through nested worklet closures into a different Worklet Runtime is fragile and can throw at runtime. ## iOS Orientation Notes (Text Recognition) diff --git a/docs/barcode-scanning.md b/docs/barcode-scanning.md index 27ddaae..096b6ce 100644 --- a/docs/barcode-scanning.md +++ b/docs/barcode-scanning.md @@ -15,9 +15,10 @@ import { } from 'react-native-vision-camera-mlkit'; ``` -## Frame Processor +## Frame Output -Use `useBarcodeScanning()` for live camera frames. +Use `useBarcodeScanning()` for live camera frames, inside a `useFrameOutput` +`onFrame` worklet. ```ts const { barcodeScanning } = useBarcodeScanning({ @@ -27,8 +28,19 @@ const { barcodeScanning } = useBarcodeScanning({ invertColors: false, }); -const result = barcodeScanning(frame, { - outputOrientation: 'portrait', +const frameOutput = useFrameOutput({ + onFrame(frame) { + 'worklet'; + + try { + const result = barcodeScanning(frame, { + outputOrientation: 'portrait', + }); + console.log(result.barcodes); + } finally { + frame.dispose(); + } + }, }); ``` @@ -38,7 +50,7 @@ const result = barcodeScanning(frame, { - `enableAllPotentialBarcodes?: boolean` (Android only) - `scaleFactor?: number` (`0.9`-`1.0`) - `invertColors?: boolean` -- `frameProcessInterval?: number` (deprecated; prefer `runAtTargetFps`) +- `frameProcessInterval?: number` (deprecated; throttle frames manually inside `onFrame` instead) ### Frame arguments @@ -125,6 +137,7 @@ For static image processing, promise rejections can use: ## Performance guidance - Filter `formats` whenever possible for better performance. -- Use `runAsync(frame, ...)` to avoid blocking frame processing. -- Use `runAtTargetFps(...)` for throttling. +- Always call `frame.dispose()` exactly once per frame inside `onFrame`. +- Throttle by skipping frames manually inside `onFrame` instead of relying on + a native-side interval. - Keep `scaleFactor` as high as possible for decoding reliability. diff --git a/docs/text-recognition.md b/docs/text-recognition.md index f8c582e..c0e0901 100644 --- a/docs/text-recognition.md +++ b/docs/text-recognition.md @@ -14,9 +14,10 @@ import { } from 'react-native-vision-camera-mlkit'; ``` -## Frame Processor +## Frame Output -Use `useTextRecognition()` for live camera frames. +Use `useTextRecognition()` for live camera frames, inside a `useFrameOutput` +`onFrame` worklet. ```ts const { textRecognition } = useTextRecognition({ @@ -25,8 +26,19 @@ const { textRecognition } = useTextRecognition({ invertColors: false, }); -const result = textRecognition(frame, { - outputOrientation: 'portrait', +const frameOutput = useFrameOutput({ + onFrame(frame) { + 'worklet'; + + try { + const result = textRecognition(frame, { + outputOrientation: 'portrait', + }); + console.log(result.text); + } finally { + frame.dispose(); + } + }, }); ``` @@ -35,7 +47,7 @@ const result = textRecognition(frame, { - `language?: 'LATIN' | 'CHINESE' | 'DEVANAGARI' | 'JAPANESE' | 'KOREAN'` - `scaleFactor?: number` (`0.9`-`1.0`) - `invertColors?: boolean` -- `frameProcessInterval?: number` (deprecated; prefer `runAtTargetFps`) +- `frameProcessInterval?: number` (deprecated; throttle frames manually inside `onFrame` instead) ### Frame arguments @@ -87,6 +99,7 @@ For static image processing, promise rejections can use: ## Performance guidance -- Use `runAsync(frame, ...)` to avoid blocking frame processing. -- Use `runAtTargetFps(...)` for throttling. +- Always call `frame.dispose()` exactly once per frame inside `onFrame`. +- Throttle by skipping frames manually inside `onFrame` instead of relying on + a native-side interval. - Keep `scaleFactor` as high as possible for OCR accuracy. diff --git a/src/core/types.ts b/src/core/types.ts index e148674..6ffb723 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -35,18 +35,21 @@ export type MLKitBaseOptions = { */ invertColors?: boolean; /** - * @deprecated Use `runAtTargetFps()` from Vision Camera instead for better frame rate control. + * @deprecated Throttle frames manually inside your `useFrameOutput` `onFrame` callback instead. * Process one frame, then skip the next N frames before processing again. * For example, `frameProcessInterval: 2` means: process frame 1, skip frames 2-3, process frame 4, etc. * @example * ```ts - * const frameProcessor = useFrameProcessor((frame) => { - * 'worklet' - * runAtTargetFps(10, () => { + * const frameOutput = useFrameOutput({ + * onFrame(frame) { * 'worklet' + * if (frameCount.value++ % 10 !== 0) { + * frame.dispose() + * return + * } * const result = textRecognition(frame) - * }) - * }, []) + * }, + * }) * ``` * @default 0 (process every frame) */ From d3efc76592a845f96a7ff8dc1752a0a0caea418c Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 12 Jul 2026 21:26:54 -0300 Subject: [PATCH 30/45] chore(release): bump version to 2.0.0 for breaking VisionCamera v5 migration --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c8eb06d..73b87b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-vision-camera-mlkit", - "version": "1.0.1", + "version": "2.0.0", "description": "A powerful React Native Vision Camera plugin delivering high-performance Google ML Kit frame processor features—including text recognition (OCR), face detection, barcode scanning, pose detection, and more. Seamlessly bridges native ML Kit capabilities for real-time, on-device computer vision in your React Native apps.", "main": "./lib/module/index.js", "types": "./lib/typescript/src/index.d.ts", From b899a150e020fd9ee2cfecdf3cc71d5d49af63f1 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Mon, 13 Jul 2026 12:11:08 -0300 Subject: [PATCH 31/45] feat: add shared Region of Interest (ROI) cropping for text recognition and barcode scanning (#13) * feat(core): add shared RegionOfInterest type and validator Introduces the public RegionOfInterest/RegionOfInterestUnit types and a fail-fast JS-side validator, wired into both the live-frame (PluginFactory) and static-image (NativeBridge) paths before any native call is made. Extends the Nitro-facing MLKitRecognizerOptions so the field actually crosses the bridge for text recognition and barcode scanning. * feat(android): implement region of interest crop and coordinate remap Crops the working bitmap to the resolved ROI pixel rect after rotation and before scale/invert, on both the live-frame (Nitro) and static-image (legacy bridge) paths for text recognition and barcode scanning. Disables the raw-MediaImage fast path whenever an ROI is set, since it can't crop. Adds a shared coordinate-remap primitive that maps result bounding boxes and corner points back to full source-frame coordinates using the recorded crop offset and scale factor - this also fixes result coordinates for the pre-existing scaleFactor option, which previously had no remapping at all. * feat(ios): implement region of interest crop and coordinate remap Mirrors the Android ROI implementation: crops via CGImage/CIImage after orientation resolution and before scale/invert, on both the live-frame (Nitro) and static-image (legacy bridge) paths for text recognition and barcode scanning. Disables the raw-CMSampleBuffer fast path whenever an ROI is set, since it can't crop. Adds the same shared coordinate-remap primitive as Android, mapping result coordinates back to full source-frame space using the recorded crop offset and scale factor. Domain type is named DomainRegionOfInterest (file included) to avoid colliding with the Nitro-generated RegionOfInterest, matching the existing DomainOrientation/Orientation precedent. Also fixes two smaller pre-existing gaps hit while wiring this through: RecognizeBarcodesUseCase's static-image overload was dropping outputOrientation when reconstructing ImagePreprocessingOptions, and a CMSampleBuffer conversion-failure path used fatalError instead of failing gracefully - now reachable in normal operation via ROI validation. Includes the Podfile.lock update from re-running pod install after adding the new native files. * feat(example): add region of interest control and camera overlay Adds a shared RegionOfInterestControl (enable switch + 4 normalized sliders) used by both the Settings tab (live camera options) and the Image screen (static-image options), plus an ROIOverlay component that renders the configured rectangle over the camera preview - both for composing an ROI and for visually confirming the native crop/remap lines up with the recognized result coordinates. * docs: document the roi option for text recognition and barcode scanning Adds a roi row to every frame/image options list in README.md and both feature docs, plus a "Region of Interest" reference section in the main README explaining the type shape, default normalized coordinates, and usage. * test: add unit tests for validateRegionOfInterest Covers valid normalized and pixel regions, negative x/y, non-positive width/height, and normalized x+width/y+height exceeding 1. --- README.md | 27 +++ .../BarcodeScannerOptions+toDomain.kt | 1 + .../visioncameramlkit/HybridBarcodeScanner.kt | 9 +- .../HybridFrameSpec+toProcessedImage.kt | 16 +- .../visioncameramlkit/HybridTextRecognizer.kt | 9 +- .../RegionOfInterest+toDomain.kt | 21 +++ .../TextRecognizerOptions+toDomain.kt | 1 + .../usecases/RecognizeBarcodesUseCase.kt | 5 +- .../usecases/RecognizeTextUseCase.kt | 5 +- .../handlers/StaticBarcodeScanningHandler.kt | 2 + .../handlers/StaticTextRecognitionHandler.kt | 2 + .../parsers/RegionOfInterestOptionParser.kt | 32 ++++ .../models/BarcodeScanningResult+remap.kt | 14 ++ .../domain/models/CoordinateRemap.kt | 35 ++++ .../models/ImagePreprocessingOptions.kt | 1 + .../domain/models/ProcessedImage.kt | 7 + .../domain/models/RegionOfInterest.kt | 57 ++++++ .../models/TextRecognitionResult+remap.kt | 35 ++++ .../infrastructure/image/ImagePreprocessor.kt | 41 ++++- docs/barcode-scanning.md | 2 + docs/text-recognition.md | 2 + example/ios/Podfile.lock | 4 +- .../components/ui/RegionOfInterestControl.tsx | 107 +++++++++++ example/src/components/ui/index.ts | 1 + example/src/components/views/CameraView.tsx | 2 + example/src/components/views/ROIOverlay.tsx | 41 +++++ example/src/components/views/index.ts | 1 + example/src/screens/ImageScreen.tsx | 5 + example/src/screens/SettingsScreen.tsx | 11 ++ .../UseCases/RecognizeBarcodesUseCase.swift | 10 +- .../UseCases/RecognizeTextUseCase.swift | 6 +- .../StaticBarcodeScanningHandler.swift | 26 ++- .../StaticTextRecognitionHandler.swift | 26 ++- .../BarcodeScannerOptions+toDomain.swift | 3 +- ios/Bridge/Hybrid/HybridBarcodeScanner.swift | 3 +- ios/Bridge/Hybrid/HybridTextRecognizer.swift | 3 +- .../Hybrid/RegionOfInterest+toDomain.swift | 14 ++ .../TextRecognizerOptions+toDomain.swift | 3 +- .../Models/BarcodeScanningResult+remap.swift | 35 ++++ ios/Domain/Models/CoordinateRemap.swift | 52 ++++++ .../Models/DomainRegionOfInterest.swift | 89 +++++++++ .../DomainTextRecognitionResult+remap.swift | 70 ++++++++ .../Models/ImagePreprocessingOptions.swift | 5 +- ios/Domain/Models/ProcessedImage.swift | 26 +++ .../Image/ImagePreprocessor.swift | 170 ++++++++++++++---- .../validateRegionOfInterest.test.ts | 82 +++++++++ src/core/NativeBridge.ts | 9 +- src/core/PluginFactory.ts | 5 + src/core/types.ts | 36 ++++ src/core/validateRegionOfInterest.ts | 29 +++ src/index.ts | 2 + src/specs/MLKitVisionTypes.ts | 8 + 52 files changed, 1138 insertions(+), 70 deletions(-) create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/RegionOfInterest+toDomain.kt create mode 100644 android/src/main/java/com/visioncameramlkit/bridge/parsers/RegionOfInterestOptionParser.kt create mode 100644 android/src/main/java/com/visioncameramlkit/domain/models/BarcodeScanningResult+remap.kt create mode 100644 android/src/main/java/com/visioncameramlkit/domain/models/CoordinateRemap.kt create mode 100644 android/src/main/java/com/visioncameramlkit/domain/models/RegionOfInterest.kt create mode 100644 android/src/main/java/com/visioncameramlkit/domain/models/TextRecognitionResult+remap.kt create mode 100644 example/src/components/ui/RegionOfInterestControl.tsx create mode 100644 example/src/components/views/ROIOverlay.tsx create mode 100644 ios/Bridge/Hybrid/RegionOfInterest+toDomain.swift create mode 100644 ios/Domain/Models/BarcodeScanningResult+remap.swift create mode 100644 ios/Domain/Models/CoordinateRemap.swift create mode 100644 ios/Domain/Models/DomainRegionOfInterest.swift create mode 100644 ios/Domain/Models/DomainTextRecognitionResult+remap.swift create mode 100644 src/__tests__/validateRegionOfInterest.test.ts create mode 100644 src/core/validateRegionOfInterest.ts diff --git a/README.md b/README.md index 91c24cf..049fb9e 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,7 @@ const frameOutput = useFrameOutput({ - `language?: 'LATIN' | 'CHINESE' | 'DEVANAGARI' | 'JAPANESE' | 'KOREAN'` - `scaleFactor?: number` (0.9-1.0) - `invertColors?: boolean` +- `roi?: RegionOfInterest` (crop processing to a rectangle of the frame; see [Region of Interest](#region-of-interest)) - `frameProcessInterval?: number` (deprecated, throttle frames manually inside `onFrame` instead) `TextRecognitionArguments`: @@ -191,6 +192,7 @@ console.log(result.blocks); - `language?: 'LATIN' | 'CHINESE' | 'DEVANAGARI' | 'JAPANESE' | 'KOREAN'` - `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` - `invertColors?: boolean` +- `roi?: RegionOfInterest` (crop processing to a rectangle of the image; see [Region of Interest](#region-of-interest)) > The native bridge normalizes URIs (`file://` is removed on iOS and added on Android if missing). Supported formats: JPEG, PNG, WebP. @@ -252,6 +254,7 @@ const frameOutput = useFrameOutput({ - `enableAllPotentialBarcodes?: boolean` (Android only) - `scaleFactor?: number` (0.9-1.0) - `invertColors?: boolean` +- `roi?: RegionOfInterest` (crop processing to a rectangle of the frame; see [Region of Interest](#region-of-interest)) - `frameProcessInterval?: number` (deprecated, throttle frames manually inside `onFrame` instead) Supported `formats` values: @@ -305,6 +308,7 @@ for (const barcode of result.barcodes) { - `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` - `invertColors?: boolean` - `scaleFactor?: number` (0.9-1.0) +- `roi?: RegionOfInterest` (crop processing to a rectangle of the image; see [Region of Interest](#region-of-interest)) `BarcodeScanningResult` includes: @@ -349,6 +353,29 @@ import { assertFeatureAvailable(MLKIT_FEATURE_KEYS.TEXT_RECOGNITION); ``` +### Region of Interest + +Every feature accepts an optional `roi` to crop processing to a rectangle of the frame/image before running ML Kit, reducing CPU/GPU work. Result coordinates (bounding boxes, corners) are always mapped back to full source coordinates, so overlays don't need to account for the crop. + +```ts +type RegionOfInterest = { + x: number; + y: number; + width: number; + height: number; + /** @default 'normalized' */ + unit?: 'normalized' | 'pixel'; +}; +``` + +By default `x`/`y`/`width`/`height` are normalized to the `0..1` range relative to the source frame/image (so `x + width` and `y + height` must each be `<= 1`); set `unit: 'pixel'` to use absolute pixel values instead. + +```ts +const { textRecognition } = useTextRecognition({ + roi: { x: 0.25, y: 0.25, width: 0.5, height: 0.5 }, +}); +``` + ## Performance - Follow the Vision Camera [performance guide](https://visioncamera.margelo.com/docs/performance) diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions+toDomain.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions+toDomain.kt index 8525a5b..0e1d027 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions+toDomain.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions+toDomain.kt @@ -15,4 +15,5 @@ internal fun BarcodeScannerOptions.toImagePreprocessingOptions(): ImagePreproces ImagePreprocessingOptions( invertColors = invertColors ?: false, scaleFactor = scaleFactor?.toFloat() ?: 1.0f, + roi = roi.toDomainRegionOfInterest(), ) diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt index 9eeed36..5b72857 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt @@ -3,6 +3,7 @@ package com.margelo.nitro.visioncameramlkit import androidx.annotation.Keep import com.facebook.proguard.annotations.DoNotStrip import com.margelo.nitro.camera.HybridFrameSpec +import com.visioncameramlkit.domain.models.remap import com.visioncameramlkit.infrastructure.image.ImagePreprocessor import com.visioncameramlkit.infrastructure.mlkit.factories.BarcodeScanningServiceFactory @@ -16,8 +17,12 @@ class HybridBarcodeScanner( private val imageOptions = options.toImagePreprocessingOptions() private val recognitionService = BarcodeScanningServiceFactory.create(barcodeOptions) - override fun recognize(frame: HybridFrameSpec, args: MLKitBaseArguments?): BarcodeScannerResult { + override fun recognize( + frame: HybridFrameSpec, + args: MLKitBaseArguments?, + ): BarcodeScannerResult { val processedImage = frame.toProcessedImage(imagePreprocessor, imageOptions) - return recognitionService.recognize(processedImage).toNitroResult() + val result = recognitionService.recognize(processedImage) + return result.remap(processedImage.metadata).toNitroResult() } } diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridFrameSpec+toProcessedImage.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridFrameSpec+toProcessedImage.kt index b552278..ed6bbd1 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridFrameSpec+toProcessedImage.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridFrameSpec+toProcessedImage.kt @@ -31,7 +31,8 @@ internal fun ImagePreprocessor.preprocessFrame( val needsBitmapProcessing = options.invertColors || effectiveScale < 1.0f || - frame.isMirrored + frame.isMirrored || + options.roi != null if (!needsBitmapProcessing) { val mediaImage = @@ -52,15 +53,17 @@ internal fun ImagePreprocessor.preprocessFrame( } val orientedBitmap = image.toBitmap(frame.orientation, frame.isMirrored) + val (croppedBitmap, cropRect) = cropToRegionOfInterest(orientedBitmap, options.roi) + val scaledBitmap = if (effectiveScale < 1.0f) { - orientedBitmap.scale( - (orientedBitmap.width * effectiveScale).toInt(), - (orientedBitmap.height * effectiveScale).toInt(), + croppedBitmap.scale( + (croppedBitmap.width * effectiveScale).toInt(), + (croppedBitmap.height * effectiveScale).toInt(), false, ) } else { - orientedBitmap + croppedBitmap } val processedBitmap = @@ -78,6 +81,9 @@ internal fun ImagePreprocessor.preprocessFrame( height = processedBitmap.height, rotation = 0, isInverted = options.invertColors, + offsetX = cropRect.left, + offsetY = cropRect.top, + scaleFactor = effectiveScale, ), ) } diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt index 1645be6..83c9653 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt @@ -3,6 +3,7 @@ package com.margelo.nitro.visioncameramlkit import androidx.annotation.Keep import com.facebook.proguard.annotations.DoNotStrip import com.margelo.nitro.camera.HybridFrameSpec +import com.visioncameramlkit.domain.models.remap import com.visioncameramlkit.infrastructure.image.ImagePreprocessor import com.visioncameramlkit.infrastructure.mlkit.factories.TextRecognitionServiceFactory @@ -16,8 +17,12 @@ class HybridTextRecognizer( private val imageOptions = options.toImagePreprocessingOptions() private val recognitionService = TextRecognitionServiceFactory.create(textOptions.language) - override fun recognize(frame: HybridFrameSpec, args: MLKitBaseArguments?): TextRecognitionResult { + override fun recognize( + frame: HybridFrameSpec, + args: MLKitBaseArguments?, + ): TextRecognitionResult { val processedImage = frame.toProcessedImage(imagePreprocessor, imageOptions) - return recognitionService.recognize(processedImage).toNitroResult() + val result = recognitionService.recognize(processedImage) + return result.remap(processedImage.metadata).toNitroResult() } } diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/RegionOfInterest+toDomain.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/RegionOfInterest+toDomain.kt new file mode 100644 index 0000000..f3e80a9 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/RegionOfInterest+toDomain.kt @@ -0,0 +1,21 @@ +package com.margelo.nitro.visioncameramlkit + +internal fun RegionOfInterest?.toDomainRegionOfInterest(): com.visioncameramlkit.domain.models.RegionOfInterest? = + this?.let { + com.visioncameramlkit.domain.models.RegionOfInterest( + x = it.x.toFloat(), + y = it.y.toFloat(), + width = it.width.toFloat(), + height = it.height.toFloat(), + unit = it.unit.toDomainRegionOfInterestUnit(), + ) + } + +private fun RegionOfInterestUnit?.toDomainRegionOfInterestUnit(): com.visioncameramlkit.domain.models.RegionOfInterestUnit = + when (this) { + RegionOfInterestUnit.PIXEL -> com.visioncameramlkit.domain.models.RegionOfInterestUnit.PIXEL + + RegionOfInterestUnit.NORMALIZED, + null, + -> com.visioncameramlkit.domain.models.RegionOfInterestUnit.NORMALIZED + } diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions+toDomain.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions+toDomain.kt index 7fef6b4..fdea701 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions+toDomain.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions+toDomain.kt @@ -14,4 +14,5 @@ internal fun TextRecognizerOptions.toImagePreprocessingOptions(): ImagePreproces ImagePreprocessingOptions( invertColors = invertColors ?: false, scaleFactor = scaleFactor?.toFloat() ?: 1.0f, + roi = roi.toDomainRegionOfInterest(), ) diff --git a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt b/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt index 047e70f..d03f17a 100644 --- a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt +++ b/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt @@ -3,6 +3,7 @@ package com.visioncameramlkit.application.usecases import com.visioncameramlkit.domain.models.BarcodeScanningOptions import com.visioncameramlkit.domain.models.BarcodeScanningResult import com.visioncameramlkit.domain.models.ImagePreprocessingOptions +import com.visioncameramlkit.domain.models.remap import com.visioncameramlkit.domain.services.IImagePreprocessor import com.visioncameramlkit.domain.services.IRecognitionService import java.io.File @@ -21,9 +22,11 @@ class RecognizeBarcodesUseCase( invertColors = imageOptions.invertColors, orientation = imageOptions.orientation, scaleFactor = imageOptions.scaleFactor, + roi = imageOptions.roi, ) val processedImage = imagePreprocessor.preprocessImage(imageFile, preprocessingOptions) - return recognitionService.recognize(processedImage) + val result = recognitionService.recognize(processedImage) + return result.remap(processedImage.metadata) } } diff --git a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeTextUseCase.kt b/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeTextUseCase.kt index 47f3925..46883cd 100644 --- a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeTextUseCase.kt +++ b/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeTextUseCase.kt @@ -3,6 +3,7 @@ package com.visioncameramlkit.application.usecases import com.visioncameramlkit.domain.models.ImagePreprocessingOptions import com.visioncameramlkit.domain.models.TextRecognitionOptions import com.visioncameramlkit.domain.models.TextRecognitionResult +import com.visioncameramlkit.domain.models.remap import com.visioncameramlkit.domain.services.IImagePreprocessor import com.visioncameramlkit.domain.services.IRecognitionService import java.io.File @@ -21,9 +22,11 @@ class RecognizeTextUseCase( invertColors = imageOptions.invertColors, orientation = imageOptions.orientation, scaleFactor = imageOptions.scaleFactor, + roi = imageOptions.roi, ) val processedImage = imagePreprocessor.preprocessImage(imageFile, preprocessingOptions) - return recognitionService.recognize(processedImage) + val result = recognitionService.recognize(processedImage) + return result.remap(processedImage.metadata) } } diff --git a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticBarcodeScanningHandler.kt b/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticBarcodeScanningHandler.kt index 6526044..9fe891a 100644 --- a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticBarcodeScanningHandler.kt +++ b/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticBarcodeScanningHandler.kt @@ -7,6 +7,7 @@ import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReadableMap import com.visioncameramlkit.application.usecases.RecognizeBarcodesUseCase import com.visioncameramlkit.bridge.parsers.BarcodeScanningOptionParser +import com.visioncameramlkit.bridge.parsers.RegionOfInterestOptionParser import com.visioncameramlkit.domain.models.BarcodeScanningOptions import com.visioncameramlkit.domain.models.ImagePreprocessingOptions import com.visioncameramlkit.domain.models.Orientation @@ -127,6 +128,7 @@ class StaticBarcodeScanningHandler( invertColors = options.getOrDefault("invertColors", false), orientation = options.getString("orientation")?.let { parseOrientation(it) }, scaleFactor = options.getOrDefault("scaleFactor", 1.0f), + roi = RegionOfInterestOptionParser.parse(options), ) private fun parseBarcodeOptions(options: ReadableMap): BarcodeScanningOptions = diff --git a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt b/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt index c033532..14ff378 100644 --- a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt +++ b/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt @@ -6,6 +6,7 @@ import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReadableMap import com.visioncameramlkit.application.usecases.RecognizeTextUseCase +import com.visioncameramlkit.bridge.parsers.RegionOfInterestOptionParser import com.visioncameramlkit.domain.models.ImagePreprocessingOptions import com.visioncameramlkit.domain.models.Orientation import com.visioncameramlkit.domain.models.TextRecognitionLanguage @@ -134,6 +135,7 @@ class StaticTextRecognitionHandler( ImagePreprocessingOptions( invertColors = options.getBoolean("invertColors"), orientation = options.getString("orientation")?.let { parseOrientation(it) }, + roi = RegionOfInterestOptionParser.parse(options), ) private fun parseOrientation(orientation: String): Orientation? = diff --git a/android/src/main/java/com/visioncameramlkit/bridge/parsers/RegionOfInterestOptionParser.kt b/android/src/main/java/com/visioncameramlkit/bridge/parsers/RegionOfInterestOptionParser.kt new file mode 100644 index 0000000..68303ce --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/bridge/parsers/RegionOfInterestOptionParser.kt @@ -0,0 +1,32 @@ +package com.visioncameramlkit.bridge.parsers + +import com.facebook.react.bridge.ReadableMap +import com.visioncameramlkit.domain.models.RegionOfInterest +import com.visioncameramlkit.domain.models.RegionOfInterestUnit + +object RegionOfInterestOptionParser { + /** + * Parses an optional `roi` key from [readableMap] into a domain [RegionOfInterest]. + * Returns `null` when the key is absent, matching the "no ROI" default. + */ + fun parse(readableMap: ReadableMap): RegionOfInterest? { + if (!readableMap.hasKey("roi") || readableMap.isNull("roi")) { + return null + } + val roiMap = readableMap.getMap("roi") ?: return null + + return RegionOfInterest( + x = roiMap.getDouble("x").toFloat(), + y = roiMap.getDouble("y").toFloat(), + width = roiMap.getDouble("width").toFloat(), + height = roiMap.getDouble("height").toFloat(), + unit = parseUnit(roiMap.getString("unit")), + ) + } + + private fun parseUnit(unit: String?): RegionOfInterestUnit = + when (unit) { + "pixel" -> RegionOfInterestUnit.PIXEL + else -> RegionOfInterestUnit.NORMALIZED + } +} diff --git a/android/src/main/java/com/visioncameramlkit/domain/models/BarcodeScanningResult+remap.kt b/android/src/main/java/com/visioncameramlkit/domain/models/BarcodeScanningResult+remap.kt new file mode 100644 index 0000000..c5db25d --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/domain/models/BarcodeScanningResult+remap.kt @@ -0,0 +1,14 @@ +package com.visioncameramlkit.domain.models + +/** + * Remaps every bounding box and corner in this result from processed-image coordinates + * back to source image/frame coordinates, using [metadata]'s crop offset and scale + * factor. See [BoundingBox.remap] and [Corner.remap] for the primitive. + */ +fun BarcodeScanningResult.remap(metadata: ImageMetadata): BarcodeScanningResult = copy(barcodes = barcodes.map { it.remap(metadata) }) + +private fun BarcodeData.remap(metadata: ImageMetadata): BarcodeData = + copy( + bounds = bounds?.remap(metadata), + corners = corners?.map { it.remap(metadata) }, + ) diff --git a/android/src/main/java/com/visioncameramlkit/domain/models/CoordinateRemap.kt b/android/src/main/java/com/visioncameramlkit/domain/models/CoordinateRemap.kt new file mode 100644 index 0000000..1fd1f9f --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/domain/models/CoordinateRemap.kt @@ -0,0 +1,35 @@ +package com.visioncameramlkit.domain.models + +/** + * Translates a coordinate produced against a processed (cropped and/or scaled) image + * back into the original source image/frame's pixel space. + * + * Preprocessing crops first (in pre-scale pixel space), then scales. To invert that, + * this first undoes the scale factor (dividing by it), then adds back the crop offset, + * which was recorded in pre-scale pixel space: `source = offset + processed / scaleFactor`. + */ +private fun remapPoint( + value: Double, + offset: Int, + scaleFactor: Float, +): Double = offset + value / scaleFactor + +fun BoundingBox.remap(metadata: ImageMetadata): BoundingBox = + BoundingBox( + x = remapPoint(x, metadata.offsetX, metadata.scaleFactor), + y = remapPoint(y, metadata.offsetY, metadata.scaleFactor), + centerX = remapPoint(centerX, metadata.offsetX, metadata.scaleFactor), + centerY = remapPoint(centerY, metadata.offsetY, metadata.scaleFactor), + width = width / metadata.scaleFactor, + height = height / metadata.scaleFactor, + top = remapPoint(top, metadata.offsetY, metadata.scaleFactor), + left = remapPoint(left, metadata.offsetX, metadata.scaleFactor), + bottom = remapPoint(bottom, metadata.offsetY, metadata.scaleFactor), + right = remapPoint(right, metadata.offsetX, metadata.scaleFactor), + ) + +fun Corner.remap(metadata: ImageMetadata): Corner = + Corner( + x = remapPoint(x, metadata.offsetX, metadata.scaleFactor), + y = remapPoint(y, metadata.offsetY, metadata.scaleFactor), + ) diff --git a/android/src/main/java/com/visioncameramlkit/domain/models/ImagePreprocessingOptions.kt b/android/src/main/java/com/visioncameramlkit/domain/models/ImagePreprocessingOptions.kt index 1bf9bda..ea4563a 100644 --- a/android/src/main/java/com/visioncameramlkit/domain/models/ImagePreprocessingOptions.kt +++ b/android/src/main/java/com/visioncameramlkit/domain/models/ImagePreprocessingOptions.kt @@ -6,4 +6,5 @@ data class ImagePreprocessingOptions( val outputOrientation: OutputOrientation? = null, val scaleFactor: Float = 1.0f, val orientation: Orientation? = null, + val roi: RegionOfInterest? = null, ) diff --git a/android/src/main/java/com/visioncameramlkit/domain/models/ProcessedImage.kt b/android/src/main/java/com/visioncameramlkit/domain/models/ProcessedImage.kt index da5856c..24da288 100644 --- a/android/src/main/java/com/visioncameramlkit/domain/models/ProcessedImage.kt +++ b/android/src/main/java/com/visioncameramlkit/domain/models/ProcessedImage.kt @@ -12,4 +12,11 @@ data class ImageMetadata( val height: Int, val rotation: Int, val isInverted: Boolean, + // Crop origin (in post-rotation, pre-scale pixel space) applied by an ROI, if any. + // Downstream result remapping adds this back onto ML Kit's result coordinates. + val offsetX: Int = 0, + val offsetY: Int = 0, + // Scale factor applied during preprocessing, needed to unscale result coordinates + // back to source image/frame pixel space. + val scaleFactor: Float = 1.0f, ) diff --git a/android/src/main/java/com/visioncameramlkit/domain/models/RegionOfInterest.kt b/android/src/main/java/com/visioncameramlkit/domain/models/RegionOfInterest.kt new file mode 100644 index 0000000..1810b80 --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/domain/models/RegionOfInterest.kt @@ -0,0 +1,57 @@ +package com.visioncameramlkit.domain.models + +import android.graphics.Rect + +enum class RegionOfInterestUnit { + NORMALIZED, + PIXEL, +} + +data class RegionOfInterest( + val x: Float, + val y: Float, + val width: Float, + val height: Float, + val unit: RegionOfInterestUnit = RegionOfInterestUnit.NORMALIZED, +) { + init { + require(x >= 0 && y >= 0) { + "Invalid RegionOfInterest: x and y must be >= 0 (got x=$x, y=$y)." + } + require(width > 0 && height > 0) { + "Invalid RegionOfInterest: width and height must be > 0 (got width=$width, height=$height)." + } + if (unit == RegionOfInterestUnit.NORMALIZED) { + require(x + width <= 1f && y + height <= 1f) { + "Invalid RegionOfInterest: normalized x + width and y + height must be <= 1 " + + "(got x=$x, width=$width, y=$y, height=$height)." + } + } + } +} + +/** + * Resolves this [RegionOfInterest] into a pixel-space [Rect] for an image of the given + * dimensions, clamping the result to stay within the image bounds. + */ +fun RegionOfInterest.resolveToPixelRect( + imageWidth: Int, + imageHeight: Int, +): Rect { + val (px, py, pw, ph) = + when (unit) { + RegionOfInterestUnit.PIXEL -> { + listOf(x, y, width, height) + } + + RegionOfInterestUnit.NORMALIZED -> { + listOf(x * imageWidth, y * imageHeight, width * imageWidth, height * imageHeight) + } + } + + val left = px.toInt().coerceIn(0, imageWidth) + val top = py.toInt().coerceIn(0, imageHeight) + val right = (px + pw).toInt().coerceIn(left, imageWidth) + val bottom = (py + ph).toInt().coerceIn(top, imageHeight) + return Rect(left, top, right, bottom) +} diff --git a/android/src/main/java/com/visioncameramlkit/domain/models/TextRecognitionResult+remap.kt b/android/src/main/java/com/visioncameramlkit/domain/models/TextRecognitionResult+remap.kt new file mode 100644 index 0000000..3a1923c --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/domain/models/TextRecognitionResult+remap.kt @@ -0,0 +1,35 @@ +package com.visioncameramlkit.domain.models + +/** + * Remaps every bounding box and corner in this result tree from processed-image + * coordinates back to source image/frame coordinates, using [metadata]'s crop offset + * and scale factor. See [BoundingBox.remap] and [Corner.remap] for the primitive. + */ +fun TextRecognitionResult.remap(metadata: ImageMetadata): TextRecognitionResult = copy(blocks = blocks.map { it.remap(metadata) }) + +private fun TextBlock.remap(metadata: ImageMetadata): TextBlock = + copy( + bounds = bounds?.remap(metadata), + corners = corners?.map { it.remap(metadata) }, + lines = lines.map { it.remap(metadata) }, + ) + +private fun TextLine.remap(metadata: ImageMetadata): TextLine = + copy( + bounds = bounds?.remap(metadata), + corners = corners?.map { it.remap(metadata) }, + elements = elements.map { it.remap(metadata) }, + ) + +private fun TextElement.remap(metadata: ImageMetadata): TextElement = + copy( + bounds = bounds?.remap(metadata), + corners = corners?.map { it.remap(metadata) }, + symbols = symbols.map { it.remap(metadata) }, + ) + +private fun TextSymbol.remap(metadata: ImageMetadata): TextSymbol = + copy( + bounds = bounds?.remap(metadata), + corners = corners?.map { it.remap(metadata) }, + ) diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt index 3c19257..070a9b4 100644 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt @@ -8,6 +8,7 @@ import android.graphics.ColorMatrix import android.graphics.ColorMatrixColorFilter import android.graphics.Matrix import android.graphics.Paint +import android.graphics.Rect import androidx.core.graphics.scale import androidx.exifinterface.media.ExifInterface import com.google.mlkit.vision.common.InputImage @@ -15,6 +16,8 @@ import com.visioncameramlkit.domain.models.ImageMetadata import com.visioncameramlkit.domain.models.ImagePreprocessingOptions import com.visioncameramlkit.domain.models.Orientation import com.visioncameramlkit.domain.models.ProcessedImage +import com.visioncameramlkit.domain.models.RegionOfInterest +import com.visioncameramlkit.domain.models.resolveToPixelRect import com.visioncameramlkit.domain.services.IImagePreprocessor import java.io.File @@ -26,6 +29,23 @@ class ImagePreprocessor : IImagePreprocessor { return scale.coerceIn(0.9f, 1.0f) } + /** + * Crops [bitmap] to the given [roi], if any. Returns the (possibly unchanged) bitmap + * alongside the pixel offset of the crop origin, so callers can remap result + * coordinates back to the pre-crop pixel space. + */ + fun cropToRegionOfInterest( + bitmap: Bitmap, + roi: RegionOfInterest?, + ): Pair { + if (roi == null) { + return bitmap to Rect(0, 0, bitmap.width, bitmap.height) + } + val rect = roi.resolveToPixelRect(bitmap.width, bitmap.height) + val croppedBitmap = createBitmap(bitmap, rect.left, rect.top, rect.width(), rect.height()) + return croppedBitmap to rect + } + fun invertBitmap(bitmap: Bitmap): Bitmap = createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888).apply { val canvas = Canvas(this) @@ -82,28 +102,30 @@ class ImagePreprocessor : IImagePreprocessor { val rotatedBitmap = rotateBitmap(bitmap, effectiveOrientation) val effectiveScale = clampScale(options.scaleFactor) + val (croppedBitmap, cropRect) = cropToRegionOfInterest(rotatedBitmap, options.roi) + val processedBitmap = if (options.invertColors) { val scaledBitmap = if (effectiveScale < 1.0f) { - rotatedBitmap.scale( - (rotatedBitmap.width * effectiveScale).toInt(), - (rotatedBitmap.height * effectiveScale).toInt(), + croppedBitmap.scale( + (croppedBitmap.width * effectiveScale).toInt(), + (croppedBitmap.height * effectiveScale).toInt(), false, ) } else { - rotatedBitmap + croppedBitmap } invertBitmap(scaledBitmap) } else { if (effectiveScale < 1.0f) { - rotatedBitmap.scale( - (rotatedBitmap.width * effectiveScale).toInt(), - (rotatedBitmap.height * effectiveScale).toInt(), + croppedBitmap.scale( + (croppedBitmap.width * effectiveScale).toInt(), + (croppedBitmap.height * effectiveScale).toInt(), false, ) } else { - rotatedBitmap + croppedBitmap } } @@ -115,6 +137,9 @@ class ImagePreprocessor : IImagePreprocessor { height = processedBitmap.height, rotation = 0, // Static images are already oriented isInverted = options.invertColors, + offsetX = cropRect.left, + offsetY = cropRect.top, + scaleFactor = effectiveScale, ) return ProcessedImage(inputImage, metadata) diff --git a/docs/barcode-scanning.md b/docs/barcode-scanning.md index 096b6ce..88f7e97 100644 --- a/docs/barcode-scanning.md +++ b/docs/barcode-scanning.md @@ -50,6 +50,7 @@ const frameOutput = useFrameOutput({ - `enableAllPotentialBarcodes?: boolean` (Android only) - `scaleFactor?: number` (`0.9`-`1.0`) - `invertColors?: boolean` +- `roi?: RegionOfInterest` (crop processing to a rectangle of the frame; see the [Region of Interest](../README.md#region-of-interest) section in the main README) - `frameProcessInterval?: number` (deprecated; throttle frames manually inside `onFrame` instead) ### Frame arguments @@ -77,6 +78,7 @@ const result = await processImageBarcodeScanning(imageUri, { - `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` - `invertColors?: boolean` - `scaleFactor?: number` (`0.9`-`1.0`) +- `roi?: RegionOfInterest` (crop processing to a rectangle of the image; see the [Region of Interest](../README.md#region-of-interest) section in the main README) ## Supported formats diff --git a/docs/text-recognition.md b/docs/text-recognition.md index c0e0901..a639433 100644 --- a/docs/text-recognition.md +++ b/docs/text-recognition.md @@ -47,6 +47,7 @@ const frameOutput = useFrameOutput({ - `language?: 'LATIN' | 'CHINESE' | 'DEVANAGARI' | 'JAPANESE' | 'KOREAN'` - `scaleFactor?: number` (`0.9`-`1.0`) - `invertColors?: boolean` +- `roi?: RegionOfInterest` (crop processing to a rectangle of the frame; see the [Region of Interest](../README.md#region-of-interest) section in the main README) - `frameProcessInterval?: number` (deprecated; throttle frames manually inside `onFrame` instead) ### Frame arguments @@ -72,6 +73,7 @@ const result = await processImageTextRecognition(imageUri, { - `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` - `invertColors?: boolean` - `scaleFactor?: number` (`0.9`-`1.0`) +- `roi?: RegionOfInterest` (crop processing to a rectangle of the image; see the [Region of Interest](../README.md#region-of-interest) section in the main README) ## Result shape diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 1d05c6b..ee67160 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -2447,7 +2447,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - VisionCameraMLKit (1.0.1): + - VisionCameraMLKit (2.0.0): - GoogleMLKit/BarcodeScanning - GoogleMLKit/TextRecognition - GoogleMLKit/TextRecognitionChinese @@ -2908,7 +2908,7 @@ SPEC CHECKSUMS: RNSVG: 6cae8d4082913be2eb43eaec2a456222c42195ed RNWorklets: 53439575fed8525fb8d1cabcc05a3d4d91e6ffe4 VisionCamera: 2d364657571d6b6404830269d05b1de244586a40 - VisionCameraMLKit: fa9794aa8b135e32be2d6aeacc22bc07b0b34d82 + VisionCameraMLKit: fe1db106ab49d735ac1972c0af27c06778ef8c7e VisionCameraWorklets: 8393549fac54f37a4cf6a7b1088688cf9b309b7f Yoga: 94172d87dd5d83fc929bf1ee53121b5d4490b5f2 diff --git a/example/src/components/ui/RegionOfInterestControl.tsx b/example/src/components/ui/RegionOfInterestControl.tsx new file mode 100644 index 0000000..c700ee3 --- /dev/null +++ b/example/src/components/ui/RegionOfInterestControl.tsx @@ -0,0 +1,107 @@ +import { useState } from 'react'; +import type { RegionOfInterest } from 'react-native-vision-camera-mlkit'; +import { SectionSlider } from './SectionSlider'; +import { SectionSwitch } from './SectionSwitch'; + +type NormalizedRect = { + x: number; + y: number; + width: number; + height: number; +}; + +type RegionOfInterestControlProps = { + value: RegionOfInterest | undefined; + onValueChange: (roi: RegionOfInterest | undefined) => void; +}; + +const DEFAULT_RECT: NormalizedRect = { + x: 0.25, + y: 0.25, + width: 0.5, + height: 0.5, +}; + +const RegionOfInterestControl = ({ + value, + onValueChange, +}: RegionOfInterestControlProps) => { + const [rect, setRect] = useState(() => ({ + x: value?.x ?? DEFAULT_RECT.x, + y: value?.y ?? DEFAULT_RECT.y, + width: value?.width ?? DEFAULT_RECT.width, + height: value?.height ?? DEFAULT_RECT.height, + })); + + const enabled = value !== undefined; + + const updateRect = (patch: Partial) => { + const next = { ...rect, ...patch }; + const clamped: NormalizedRect = { + x: Math.min(next.x, 1 - next.width), + y: Math.min(next.y, 1 - next.height), + width: next.width, + height: next.height, + }; + + setRect(clamped); + if (enabled) { + onValueChange({ ...clamped, unit: 'normalized' }); + } + }; + + return ( + <> + + onValueChange(next ? { ...rect, unit: 'normalized' } : undefined) + } + /> + updateRect({ x })} + /> + updateRect({ y })} + /> + updateRect({ width })} + /> + updateRect({ height })} + /> + + ); +}; + +export { RegionOfInterestControl }; diff --git a/example/src/components/ui/index.ts b/example/src/components/ui/index.ts index bec669a..76a1fdb 100644 --- a/example/src/components/ui/index.ts +++ b/example/src/components/ui/index.ts @@ -4,6 +4,7 @@ export { CameraControls } from './CameraControls'; export { Divider } from './Divider'; export { GithubFab } from './GithubFab'; export { LinkCard } from './LinkCard'; +export { RegionOfInterestControl } from './RegionOfInterestControl'; export { Section } from './Section'; export { SectionImageInput } from './SectionImageInput'; export { SectionPicker } from './SectionPicker'; diff --git a/example/src/components/views/CameraView.tsx b/example/src/components/views/CameraView.tsx index 97e8a63..d9149d7 100644 --- a/example/src/components/views/CameraView.tsx +++ b/example/src/components/views/CameraView.tsx @@ -36,6 +36,7 @@ import { useTerminalStore, } from '../../stores'; import { FpsGraphOverlay } from './FpsGraphOverlay'; +import { ROIOverlay } from './ROIOverlay'; const normalizeResultObject = (data: unknown) => { if (data === null || data === undefined) return data; @@ -327,6 +328,7 @@ const CameraView = forwardRef( maxFps={FPS_GRAPH_MAX_SCALE} /> )} + {sharedOptions.roi && } ); diff --git a/example/src/components/views/ROIOverlay.tsx b/example/src/components/views/ROIOverlay.tsx new file mode 100644 index 0000000..0617d2c --- /dev/null +++ b/example/src/components/views/ROIOverlay.tsx @@ -0,0 +1,41 @@ +import { StyleSheet, View } from 'react-native'; +import type { RegionOfInterest } from 'react-native-vision-camera-mlkit'; +import { useTheme } from '../../providers/ThemeProvider'; + +type ROIOverlayProps = { + roi: RegionOfInterest; +}; + +const ROIOverlay = ({ roi }: ROIOverlayProps) => { + const { theme } = useTheme(); + + if (roi.unit === 'pixel') return null; + + return ( + + ); +}; + +const styles = StyleSheet.create({ + rect: { + position: 'absolute', + borderWidth: 2, + borderStyle: 'dashed', + borderRadius: 4, + }, +}); + +export { ROIOverlay }; diff --git a/example/src/components/views/index.ts b/example/src/components/views/index.ts index 27da1fb..d473728 100644 --- a/example/src/components/views/index.ts +++ b/example/src/components/views/index.ts @@ -5,4 +5,5 @@ export { NoCameraPermissionErrorView } from './NoCameraPermissionErrorView'; export { NoDeviceErrorView } from './NoDeviceErrorView'; export { NoPluginErrorView } from './NoPluginErrorView'; export { PortalSegmentedToggle } from './PortalSegmentedToggle'; +export { ROIOverlay } from './ROIOverlay'; export { TerminalBottomSheet } from './TerminalBottomSheet'; diff --git a/example/src/screens/ImageScreen.tsx b/example/src/screens/ImageScreen.tsx index a1ba3fe..c6d43bf 100644 --- a/example/src/screens/ImageScreen.tsx +++ b/example/src/screens/ImageScreen.tsx @@ -15,6 +15,7 @@ import { import { Button, Divider, + RegionOfInterestControl, Section, SectionImageInput, SectionPicker, @@ -212,6 +213,10 @@ const ImageScreen = () => { } /> )} + setSharedOption('roi', roi)} + /> {pluginId === PLUGIN_ID.BARCODE_SCANNING && ( <> { onValueChange={setEnableDoubleTapGesture} />
+ +
+ setSharedOption('roi', roi)} + /> +
); }; diff --git a/ios/Application/UseCases/RecognizeBarcodesUseCase.swift b/ios/Application/UseCases/RecognizeBarcodesUseCase.swift index 43d3059..4a07b3d 100644 --- a/ios/Application/UseCases/RecognizeBarcodesUseCase.swift +++ b/ios/Application/UseCases/RecognizeBarcodesUseCase.swift @@ -39,7 +39,8 @@ import Foundation ) } - return try recognitionService.recognize(image: processedImage) + let result = try recognitionService.recognize(image: processedImage) + return result.remapped(using: processedImage.metadata) } } @@ -51,8 +52,10 @@ import Foundation return try autoreleasepool { let preprocessingOptions = ImagePreprocessingOptions( invertColors: imageOptions.invertColors, + outputOrientation: imageOptions.outputOrientation, scaleFactor: imageOptions.scaleFactor, - orientation: imageOptions.orientation + orientation: imageOptions.orientation, + roi: imageOptions.roi ) guard @@ -69,7 +72,8 @@ import Foundation } _ = barcodeOptions - return try recognitionService.recognize(image: processedImage) + let result = try recognitionService.recognize(image: processedImage) + return result.remapped(using: processedImage.metadata) } } } diff --git a/ios/Application/UseCases/RecognizeTextUseCase.swift b/ios/Application/UseCases/RecognizeTextUseCase.swift index fbef864..2f4cafc 100644 --- a/ios/Application/UseCases/RecognizeTextUseCase.swift +++ b/ios/Application/UseCases/RecognizeTextUseCase.swift @@ -41,7 +41,8 @@ import Foundation ) } - return try recognitionService.recognize(image: processedImage) + let result = try recognitionService.recognize(image: processedImage) + return result.remapped(using: processedImage.metadata) } } @@ -66,7 +67,8 @@ import Foundation ) } - return try recognitionService.recognize(image: processedImage) + let result = try recognitionService.recognize(image: processedImage) + return result.remapped(using: processedImage.metadata) } } } diff --git a/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift b/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift index d43d8d4..218ae19 100644 --- a/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift +++ b/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift @@ -92,11 +92,35 @@ import Foundation let invertColors = options["invertColors"] as? Bool ?? false let orientation = (options["orientation"] as? String).flatMap { DomainOrientation(string: $0) } let scaleFactor = CGFloat((options["scaleFactor"] as? NSNumber)?.doubleValue ?? 1.0) + let roi = parseRegionOfInterest(options["roi"]) return ImagePreprocessingOptions( invertColors: invertColors, scaleFactor: scaleFactor, - orientation: orientation + orientation: orientation, + roi: roi + ) + } + + private func parseRegionOfInterest(_ value: Any?) -> DomainRegionOfInterest? { + guard let dictionary = value as? [String: Any], + let x = (dictionary["x"] as? NSNumber)?.doubleValue, + let y = (dictionary["y"] as? NSNumber)?.doubleValue, + let width = (dictionary["width"] as? NSNumber)?.doubleValue, + let height = (dictionary["height"] as? NSNumber)?.doubleValue + else { + return nil + } + + let unit: DomainRegionOfInterestUnit = + (dictionary["unit"] as? String) == "pixel" ? .pixel : .normalized + + return DomainRegionOfInterest( + x: CGFloat(x), + y: CGFloat(y), + width: CGFloat(width), + height: CGFloat(height), + unit: unit ) } diff --git a/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift b/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift index de791a7..3377065 100644 --- a/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift +++ b/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift @@ -98,10 +98,34 @@ import Foundation { let invertColors = options["invertColors"] as? Bool ?? false let orientation = (options["orientation"] as? String).flatMap { DomainOrientation(string: $0) } + let roi = parseRegionOfInterest(options["roi"]) return ImagePreprocessingOptions( invertColors: invertColors, - orientation: orientation + orientation: orientation, + roi: roi + ) + } + + private func parseRegionOfInterest(_ value: Any?) -> DomainRegionOfInterest? { + guard let dictionary = value as? [String: Any], + let x = (dictionary["x"] as? NSNumber)?.doubleValue, + let y = (dictionary["y"] as? NSNumber)?.doubleValue, + let width = (dictionary["width"] as? NSNumber)?.doubleValue, + let height = (dictionary["height"] as? NSNumber)?.doubleValue + else { + return nil + } + + let unit: DomainRegionOfInterestUnit = + (dictionary["unit"] as? String) == "pixel" ? .pixel : .normalized + + return DomainRegionOfInterest( + x: CGFloat(x), + y: CGFloat(y), + width: CGFloat(width), + height: CGFloat(height), + unit: unit ) } diff --git a/ios/Bridge/Hybrid/BarcodeScannerOptions+toDomain.swift b/ios/Bridge/Hybrid/BarcodeScannerOptions+toDomain.swift index b459768..371d095 100644 --- a/ios/Bridge/Hybrid/BarcodeScannerOptions+toDomain.swift +++ b/ios/Bridge/Hybrid/BarcodeScannerOptions+toDomain.swift @@ -13,7 +13,8 @@ extension BarcodeScannerOptions { func toImagePreprocessingOptions() -> ImagePreprocessingOptions { return ImagePreprocessingOptions( invertColors: invertColors ?? false, - scaleFactor: CGFloat(scaleFactor ?? 1.0) + scaleFactor: CGFloat(scaleFactor ?? 1.0), + roi: roi?.toDomainRegionOfInterest() ) } } diff --git a/ios/Bridge/Hybrid/HybridBarcodeScanner.swift b/ios/Bridge/Hybrid/HybridBarcodeScanner.swift index 07cca0d..7f17eec 100644 --- a/ios/Bridge/Hybrid/HybridBarcodeScanner.swift +++ b/ios/Bridge/Hybrid/HybridBarcodeScanner.swift @@ -34,7 +34,8 @@ final class HybridBarcodeScanner: HybridBarcodeScannerSpec { throw RuntimeError.error(withMessage: "Failed to preprocess VisionCamera frame.") } - return try recognitionService.recognize(image: processedImage).toNitroResult() + let result = try recognitionService.recognize(image: processedImage) + return result.remapped(using: processedImage.metadata).toNitroResult() #else throw RuntimeError.error(withMessage: "BarcodeScanning is not enabled in the native ML Kit configuration.") #endif diff --git a/ios/Bridge/Hybrid/HybridTextRecognizer.swift b/ios/Bridge/Hybrid/HybridTextRecognizer.swift index 9e5e933..75bff67 100644 --- a/ios/Bridge/Hybrid/HybridTextRecognizer.swift +++ b/ios/Bridge/Hybrid/HybridTextRecognizer.swift @@ -34,7 +34,8 @@ final class HybridTextRecognizer: HybridTextRecognizerSpec { throw RuntimeError.error(withMessage: "Failed to preprocess VisionCamera frame.") } - return try recognitionService.recognize(image: processedImage).toNitroResult() + let result = try recognitionService.recognize(image: processedImage) + return result.remapped(using: processedImage.metadata).toNitroResult() #else throw RuntimeError.error(withMessage: "TextRecognition is not enabled in the native ML Kit configuration.") #endif diff --git a/ios/Bridge/Hybrid/RegionOfInterest+toDomain.swift b/ios/Bridge/Hybrid/RegionOfInterest+toDomain.swift new file mode 100644 index 0000000..bcc46d1 --- /dev/null +++ b/ios/Bridge/Hybrid/RegionOfInterest+toDomain.swift @@ -0,0 +1,14 @@ +import Foundation + +extension RegionOfInterest { + func toDomainRegionOfInterest() -> DomainRegionOfInterest { + let domainUnit: DomainRegionOfInterestUnit = unit == .pixel ? .pixel : .normalized + return DomainRegionOfInterest( + x: CGFloat(x), + y: CGFloat(y), + width: CGFloat(width), + height: CGFloat(height), + unit: domainUnit + ) + } +} diff --git a/ios/Bridge/Hybrid/TextRecognizerOptions+toDomain.swift b/ios/Bridge/Hybrid/TextRecognizerOptions+toDomain.swift index 5656281..dfc3b86 100644 --- a/ios/Bridge/Hybrid/TextRecognizerOptions+toDomain.swift +++ b/ios/Bridge/Hybrid/TextRecognizerOptions+toDomain.swift @@ -12,7 +12,8 @@ extension TextRecognizerOptions { func toImagePreprocessingOptions() -> ImagePreprocessingOptions { return ImagePreprocessingOptions( invertColors: invertColors ?? false, - scaleFactor: CGFloat(scaleFactor ?? 1.0) + scaleFactor: CGFloat(scaleFactor ?? 1.0), + roi: roi?.toDomainRegionOfInterest() ) } } diff --git a/ios/Domain/Models/BarcodeScanningResult+remap.swift b/ios/Domain/Models/BarcodeScanningResult+remap.swift new file mode 100644 index 0000000..0cd9442 --- /dev/null +++ b/ios/Domain/Models/BarcodeScanningResult+remap.swift @@ -0,0 +1,35 @@ +import Foundation + +#if MLKIT_BARCODE_SCANNING + #if canImport(MLKitVision) + import MLKitVision + + extension BarcodeScanningResult { + /// Remaps each barcode's bounding box and corner points back onto full-source-image + /// coordinates, undoing the ROI crop offset and preprocessing scale. + func remapped(using metadata: ImageMetadata) -> BarcodeScanningResult { + return BarcodeScanningResult( + barcodes: barcodes.map { $0.remapped(using: metadata) } + ) + } + } + + extension BarcodeData { + fileprivate func remapped(using metadata: ImageMetadata) -> BarcodeData { + return BarcodeData( + bounds: bounds?.remapped(using: metadata), + corners: corners?.map { $0.remapped(using: metadata) }, + format: format, + formatName: formatName, + valueType: valueType, + valueTypeName: valueTypeName, + rawValue: rawValue, + displayValue: displayValue, + rawBytes: rawBytes, + isPotential: isPotential, + value: value + ) + } + } + #endif +#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Domain/Models/CoordinateRemap.swift b/ios/Domain/Models/CoordinateRemap.swift new file mode 100644 index 0000000..c3048f8 --- /dev/null +++ b/ios/Domain/Models/CoordinateRemap.swift @@ -0,0 +1,52 @@ +import Foundation + +#if canImport(MLKitVision) + import MLKitVision + + /// Shared primitive for mapping a processed-image coordinate (post-crop, post-scale) back onto + /// full-source-image coordinates: translate by the crop offset, then unscale. + /// `sourceX = offsetX + processedX / scaleFactor`. + /// Domain result coordinates are `Double` (matching `DomainBoundingBox`/`DomainCorner`), while + /// `ImageMetadata`'s offset/scale are `CGFloat`; both convert freely since neither loses + /// precision on 64-bit Apple platforms. + func remapPoint(x: Double, y: Double, metadata: ImageMetadata) -> (x: Double, y: Double) { + let scale = metadata.scaleFactor == 0 ? 1.0 : Double(metadata.scaleFactor) + return ( + x: Double(metadata.offsetX) + x / scale, + y: Double(metadata.offsetY) + y / scale + ) + } + + extension DomainBoundingBox { + /// Remaps every coordinate field onto full-source-image coordinates using the given + /// preprocessing metadata. `width`/`height` only unscale (no translation), matching every + /// other field which translates by the crop offset then unscales. + func remapped(using metadata: ImageMetadata) -> DomainBoundingBox { + let scale = metadata.scaleFactor == 0 ? 1.0 : Double(metadata.scaleFactor) + let origin = remapPoint(x: x, y: y, metadata: metadata) + let center = remapPoint(x: centerX, y: centerY, metadata: metadata) + let topLeft = remapPoint(x: left, y: top, metadata: metadata) + let bottomRight = remapPoint(x: right, y: bottom, metadata: metadata) + + return DomainBoundingBox( + x: origin.x, + y: origin.y, + centerX: center.x, + centerY: center.y, + width: width / scale, + height: height / scale, + top: topLeft.y, + left: topLeft.x, + bottom: bottomRight.y, + right: bottomRight.x + ) + } + } + + extension DomainCorner { + func remapped(using metadata: ImageMetadata) -> DomainCorner { + let point = remapPoint(x: x, y: y, metadata: metadata) + return DomainCorner(x: point.x, y: point.y) + } + } +#endif diff --git a/ios/Domain/Models/DomainRegionOfInterest.swift b/ios/Domain/Models/DomainRegionOfInterest.swift new file mode 100644 index 0000000..eed5b1f --- /dev/null +++ b/ios/Domain/Models/DomainRegionOfInterest.swift @@ -0,0 +1,89 @@ +import Foundation + +enum DomainRegionOfInterestUnit { + case normalized + case pixel +} + +/// Domain-layer Region of Interest, distinct from the Nitro-bridge `RegionOfInterest` +/// (see `RegionOfInterest+toDomain.swift` for the conversion), matching how `DomainOrientation` +/// is kept separate from the Nitro `Orientation` type. +struct DomainRegionOfInterest { + let x: CGFloat + let y: CGFloat + let width: CGFloat + let height: CGFloat + let unit: DomainRegionOfInterestUnit + + init( + x: CGFloat, + y: CGFloat, + width: CGFloat, + height: CGFloat, + unit: DomainRegionOfInterestUnit = .normalized + ) { + self.x = x + self.y = y + self.width = width + self.height = height + self.unit = unit + } +} + +enum RegionOfInterestValidationError: Error, LocalizedError { + case negativeOrigin(x: CGFloat, y: CGFloat) + case nonPositiveSize(width: CGFloat, height: CGFloat) + case normalizedOutOfBounds(x: CGFloat, width: CGFloat, y: CGFloat, height: CGFloat) + + var errorDescription: String? { + switch self { + case .negativeOrigin(let x, let y): + return "Invalid RegionOfInterest: x and y must be >= 0 (got x=\(x), y=\(y))." + case .nonPositiveSize(let width, let height): + return + "Invalid RegionOfInterest: width and height must be > 0 (got width=\(width), height=\(height))." + case .normalizedOutOfBounds(let x, let width, let y, let height): + return + "Invalid RegionOfInterest: normalized x + width and y + height must be <= 1 (got x=\(x), width=\(width), y=\(y), height=\(height))." + } + } +} + +extension DomainRegionOfInterest { + /// Mirrors the JS-side `validateRegionOfInterest` rules so native preprocessing fails fast + /// instead of producing a nonsensical crop deep inside the pipeline. + func validate() throws { + if x < 0 || y < 0 { + throw RegionOfInterestValidationError.negativeOrigin(x: x, y: y) + } + + if width <= 0 || height <= 0 { + throw RegionOfInterestValidationError.nonPositiveSize(width: width, height: height) + } + + if unit == .normalized && (x + width > 1 || y + height > 1) { + throw RegionOfInterestValidationError.normalizedOutOfBounds( + x: x, width: width, y: y, height: height + ) + } + } + + /// Resolves this region into a pixel rectangle clamped to the given image dimensions. + /// `imageWidth`/`imageHeight` must be in the same pixel space the crop will be applied to + /// (post-orientation-normalization, pre-scale). + func resolvedPixelRect(imageWidth: CGFloat, imageHeight: CGFloat) -> CGRect { + let (px, py, pw, ph): (CGFloat, CGFloat, CGFloat, CGFloat) + switch unit { + case .pixel: + (px, py, pw, ph) = (x, y, width, height) + case .normalized: + (px, py, pw, ph) = (x * imageWidth, y * imageHeight, width * imageWidth, height * imageHeight) + } + + let left = max(0, min(px, imageWidth)) + let top = max(0, min(py, imageHeight)) + let right = max(left, min(px + pw, imageWidth)) + let bottom = max(top, min(py + ph, imageHeight)) + return CGRect(x: left, y: top, width: right - left, height: bottom - top) + } +} diff --git a/ios/Domain/Models/DomainTextRecognitionResult+remap.swift b/ios/Domain/Models/DomainTextRecognitionResult+remap.swift new file mode 100644 index 0000000..cd1c42f --- /dev/null +++ b/ios/Domain/Models/DomainTextRecognitionResult+remap.swift @@ -0,0 +1,70 @@ +import Foundation + +#if canImport(MLKitVision) + import MLKitVision + + extension DomainTextRecognitionResult { + /// Remaps every bounding box and corner point in the result tree back onto full-source-image + /// coordinates, undoing the ROI crop offset and preprocessing scale. No-op when neither a + /// crop nor a scale was applied (offset is zero and scale is 1.0). + func remapped(using metadata: ImageMetadata) -> DomainTextRecognitionResult { + return DomainTextRecognitionResult( + text: text, + blocks: blocks.map { $0.remapped(using: metadata) } + ) + } + } + + extension DomainTextBlock { + fileprivate func remapped(using metadata: ImageMetadata) -> DomainTextBlock { + return DomainTextBlock( + text: text, + bounds: bounds?.remapped(using: metadata), + corners: corners?.map { $0.remapped(using: metadata) }, + lines: lines.map { $0.remapped(using: metadata) }, + languages: languages + ) + } + } + + extension DomainTextLine { + fileprivate func remapped(using metadata: ImageMetadata) -> DomainTextLine { + return DomainTextLine( + text: text, + bounds: bounds?.remapped(using: metadata), + corners: corners?.map { $0.remapped(using: metadata) }, + elements: elements.map { $0.remapped(using: metadata) }, + confidence: confidence, + angle: angle, + languages: languages + ) + } + } + + extension DomainTextElement { + fileprivate func remapped(using metadata: ImageMetadata) -> DomainTextElement { + return DomainTextElement( + text: text, + bounds: bounds?.remapped(using: metadata), + corners: corners?.map { $0.remapped(using: metadata) }, + symbols: symbols.map { $0.remapped(using: metadata) }, + confidence: confidence, + angle: angle, + languages: languages + ) + } + } + + extension DomainTextSymbol { + fileprivate func remapped(using metadata: ImageMetadata) -> DomainTextSymbol { + return DomainTextSymbol( + text: text, + bounds: bounds?.remapped(using: metadata), + corners: corners?.map { $0.remapped(using: metadata) }, + confidence: confidence, + angle: angle, + languages: languages + ) + } + } +#endif diff --git a/ios/Domain/Models/ImagePreprocessingOptions.swift b/ios/Domain/Models/ImagePreprocessingOptions.swift index 68a2b1a..664775c 100644 --- a/ios/Domain/Models/ImagePreprocessingOptions.swift +++ b/ios/Domain/Models/ImagePreprocessingOptions.swift @@ -5,16 +5,19 @@ public struct ImagePreprocessingOptions { let outputOrientation: OutputOrientation? let scaleFactor: CGFloat let orientation: DomainOrientation? + let roi: DomainRegionOfInterest? init( invertColors: Bool = false, outputOrientation: OutputOrientation? = nil, scaleFactor: CGFloat = 1.0, - orientation: DomainOrientation? = nil + orientation: DomainOrientation? = nil, + roi: DomainRegionOfInterest? = nil ) { self.invertColors = invertColors self.outputOrientation = outputOrientation self.scaleFactor = scaleFactor self.orientation = orientation + self.roi = roi } } diff --git a/ios/Domain/Models/ProcessedImage.swift b/ios/Domain/Models/ProcessedImage.swift index b11fc42..b93bde7 100644 --- a/ios/Domain/Models/ProcessedImage.swift +++ b/ios/Domain/Models/ProcessedImage.swift @@ -13,5 +13,31 @@ import Foundation let height: Int let orientation: UIImage.Orientation let isInverted: Bool + /// Origin of the ROI crop, in the same pixel space as `width`/`height` (post-orientation, + /// pre-scale). Downstream code adds this back onto ML Kit result coordinates to remap them + /// to full-source-image coordinates. Zero when no ROI crop was applied. + let offsetX: CGFloat + let offsetY: CGFloat + /// Scale factor applied during preprocessing, needed to unscale result coordinates when + /// remapping them back to full-source-image coordinates. + let scaleFactor: CGFloat + + init( + width: Int, + height: Int, + orientation: UIImage.Orientation, + isInverted: Bool, + offsetX: CGFloat = 0, + offsetY: CGFloat = 0, + scaleFactor: CGFloat = 1.0 + ) { + self.width = width + self.height = height + self.orientation = orientation + self.isInverted = isInverted + self.offsetX = offsetX + self.offsetY = offsetY + self.scaleFactor = scaleFactor + } } #endif diff --git a/ios/Infrastructure/Image/ImagePreprocessor.swift b/ios/Infrastructure/Image/ImagePreprocessor.swift index 1cf38db..4a617f8 100644 --- a/ios/Infrastructure/Image/ImagePreprocessor.swift +++ b/ios/Infrastructure/Image/ImagePreprocessor.swift @@ -26,15 +26,34 @@ import Foundation return min(max(scale, 0.9), 1.0) } - // Create scaled VisionImage from CIImage using Core Image affine transform + // Create scaled VisionImage from CIImage using Core Image affine transform. + // Crop happens before scale, matching the static-image pipeline's crop-then-scale order. private func createScaledVisionImage( from ciImage: CIImage, scaleFactor: CGFloat, frameOrientation: UIImage.Orientation, - outputOrientation: OutputOrientation? + outputOrientation: OutputOrientation?, + roi: DomainRegionOfInterest? ) -> VisionImage? { + var workingImage = ciImage + + if let roi = roi { + guard (try? roi.validate()) != nil else { + return nil + } + let extent = workingImage.extent + let pixelRect = roi.resolvedPixelRect( + imageWidth: extent.width, + imageHeight: extent.height + ) + // CIImage's coordinate origin is at the extent's origin (not necessarily .zero), + // so the crop rect must be offset into the image's own coordinate space. + let cropRect = pixelRect.offsetBy(dx: extent.minX, dy: extent.minY) + workingImage = workingImage.cropped(to: cropRect) + } + // Apply scaling using CIAffineTransform - let scaledCIImage = ciImage.transformed( + let scaledCIImage = workingImage.transformed( by: CGAffineTransform(scaleX: scaleFactor, y: scaleFactor) ) @@ -84,6 +103,12 @@ import Foundation let effectiveScale = clampScale(options.scaleFactor) let frameOrientation = frame.orientation.asUIImageOrientation + if let roi = options.roi { + guard (try? roi.validate()) != nil else { + return nil + } + } + let image: VisionImage if options.invertColors { @@ -92,36 +117,65 @@ import Foundation sampleBuffer: sampleBuffer, frameOrientation: frameOrientation, outputOrientation: options.outputOrientation, - scaleFactor: effectiveScale + scaleFactor: effectiveScale, + roi: options.roi ) else { return nil } image = invertedImage } else { - image = createVisionImageFromFrame( - sampleBuffer: sampleBuffer, - frameOrientation: frameOrientation, - outputOrientation: options.outputOrientation, - scaleFactor: effectiveScale - ) + guard + let convertedImage = createVisionImageFromFrame( + sampleBuffer: sampleBuffer, + frameOrientation: frameOrientation, + outputOrientation: options.outputOrientation, + scaleFactor: effectiveScale, + roi: options.roi + ) + else { + return nil + } + image = convertedImage } guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil } + // Use pre-crop, pre-scale pixel-buffer dimensions to resolve the ROI's pixel rect so the + // crop offset lands in the same coordinate space as the scaled width/height below. + let originalWidth = CGFloat(CVPixelBufferGetWidth(imageBuffer)) + let originalHeight = CGFloat(CVPixelBufferGetHeight(imageBuffer)) + + var offsetX: CGFloat = 0 + var offsetY: CGFloat = 0 + var croppedWidth = originalWidth + var croppedHeight = originalHeight + + if let roi = options.roi { + let pixelRect = roi.resolvedPixelRect( + imageWidth: originalWidth, + imageHeight: originalHeight + ) + offsetX = pixelRect.origin.x + offsetY = pixelRect.origin.y + croppedWidth = pixelRect.width + croppedHeight = pixelRect.height + } + // Use scaled dimensions for metadata - let originalWidth = CVPixelBufferGetWidth(imageBuffer) - let originalHeight = CVPixelBufferGetHeight(imageBuffer) - let width = Int(CGFloat(originalWidth) * effectiveScale) - let height = Int(CGFloat(originalHeight) * effectiveScale) + let width = Int(croppedWidth * effectiveScale) + let height = Int(croppedHeight * effectiveScale) let metadata = ImageMetadata( width: width, height: height, orientation: frameOrientation, - isInverted: options.invertColors + isInverted: options.invertColors, + offsetX: offsetX, + offsetY: offsetY, + scaleFactor: effectiveScale ) return ProcessedImage(image: image, metadata: metadata) @@ -131,8 +185,9 @@ import Foundation sampleBuffer: CMSampleBuffer, frameOrientation: UIImage.Orientation, outputOrientation: OutputOrientation?, - scaleFactor: CGFloat - ) -> VisionImage { + scaleFactor: CGFloat, + roi: DomainRegionOfInterest? + ) -> VisionImage? { // MLKit works more reliably when frames are converted to UIImage // YUV CMSampleBuffer can have orientation and format handling issues if let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { @@ -151,23 +206,27 @@ import Foundation || pixelFormatType == kCVPixelFormatType_Lossless_420YpCbCr8BiPlanarFullRange - // Force conversion when scaling is needed to ensure consistent behavior - // Never bypass scaling if scaleFactor < 1.0 - if needsConversion || scaleFactor < 1.0 { - if let convertedImage = convertToSupportedFormat( - pixelBuffer: pixelBuffer, - frameOrientation: frameOrientation, - outputOrientation: outputOrientation, - scaleFactor: scaleFactor - ) { - return convertedImage + // Force conversion when scaling or an ROI crop is needed to ensure consistent behavior. + // Never bypass scaling/cropping if scaleFactor < 1.0 or an ROI is set, since the raw + // CMSampleBuffer fast path below cannot crop. + if needsConversion || scaleFactor < 1.0 || roi != nil { + guard + let convertedImage = convertToSupportedFormat( + pixelBuffer: pixelBuffer, + frameOrientation: frameOrientation, + outputOrientation: outputOrientation, + scaleFactor: scaleFactor, + roi: roi + ) + else { + // Conversion failed - this should not happen for scaling/cropping cases + return nil } - // Conversion failed - this should not happen for scaling cases - fatalError("Conversion failed when scaling is required") + return convertedImage } } - // Fallback: Use CMSampleBuffer directly (for BGRA and scaleFactor == 1.0) + // Fallback: Use CMSampleBuffer directly (for BGRA, scaleFactor == 1.0, and no ROI) let image = VisionImage(buffer: sampleBuffer) image.orientation = getVisionOrientation( frameOrientation: frameOrientation, @@ -180,7 +239,8 @@ import Foundation sampleBuffer: CMSampleBuffer, frameOrientation: UIImage.Orientation, outputOrientation: OutputOrientation?, - scaleFactor: CGFloat + scaleFactor: CGFloat, + roi: DomainRegionOfInterest? ) -> VisionImage? { guard let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil @@ -197,7 +257,8 @@ import Foundation from: invertedCIImage, scaleFactor: scaleFactor, frameOrientation: frameOrientation, - outputOrientation: outputOrientation + outputOrientation: outputOrientation, + roi: roi ) } @@ -231,7 +292,8 @@ import Foundation pixelBuffer: CVPixelBuffer, frameOrientation: UIImage.Orientation, outputOrientation: OutputOrientation?, - scaleFactor: CGFloat + scaleFactor: CGFloat, + roi: DomainRegionOfInterest? ) -> VisionImage? { // Convert to CIImage let ciImage = CIImage(cvPixelBuffer: pixelBuffer) @@ -241,7 +303,8 @@ import Foundation from: ciImage, scaleFactor: scaleFactor, frameOrientation: frameOrientation, - outputOrientation: outputOrientation + outputOrientation: outputOrientation, + roi: roi ) } @@ -260,6 +323,40 @@ import Foundation let effectiveScale = clampScale(options.scaleFactor) var processedImage = uiImage + var offsetX: CGFloat = 0 + var offsetY: CGFloat = 0 + + // Apply optional ROI crop, right after decode/orientation-resolution and before + // scale/invert (UIImage.imageOrientation metadata already encodes orientation for the + // static path, so there's no separate rotate step here). + if let roi = options.roi { + guard (try? roi.validate()) != nil else { + return nil + } + + // Crop in the same pixel space ML Kit will see: the CGImage's pixel dimensions, + // not the orientation-aware `image.size`. + guard let cgImage = processedImage.cgImage else { + return nil + } + + let pixelRect = roi.resolvedPixelRect( + imageWidth: CGFloat(cgImage.width), + imageHeight: CGFloat(cgImage.height) + ) + + guard let croppedCGImage = cgImage.cropping(to: pixelRect) else { + return nil + } + + offsetX = pixelRect.origin.x + offsetY = pixelRect.origin.y + processedImage = UIImage( + cgImage: croppedCGImage, + scale: processedImage.scale, + orientation: processedImage.imageOrientation + ) + } // Apply optional downscale for static images. if effectiveScale < 1.0 { @@ -287,7 +384,10 @@ import Foundation width: pixelWidth, height: pixelHeight, orientation: visionOrientation, - isInverted: options.invertColors + isInverted: options.invertColors, + offsetX: offsetX, + offsetY: offsetY, + scaleFactor: effectiveScale ) return ProcessedImage(image: visionImage, metadata: metadata) diff --git a/src/__tests__/validateRegionOfInterest.test.ts b/src/__tests__/validateRegionOfInterest.test.ts new file mode 100644 index 0000000..4ce81f4 --- /dev/null +++ b/src/__tests__/validateRegionOfInterest.test.ts @@ -0,0 +1,82 @@ +import { validateRegionOfInterest } from '../core/validateRegionOfInterest'; +import type { RegionOfInterest } from '../core/types'; + +describe('validateRegionOfInterest', () => { + it('accepts a valid normalized region', () => { + expect(() => + validateRegionOfInterest({ x: 0.25, y: 0.25, width: 0.5, height: 0.5 }) + ).not.toThrow(); + }); + + it('accepts a valid normalized region with unit explicitly set', () => { + expect(() => + validateRegionOfInterest({ + x: 0, + y: 0, + width: 1, + height: 1, + unit: 'normalized', + }) + ).not.toThrow(); + }); + + it('accepts a valid pixel region regardless of magnitude', () => { + expect(() => + validateRegionOfInterest({ + x: 100, + y: 100, + width: 800, + height: 600, + unit: 'pixel', + }) + ).not.toThrow(); + }); + + it('throws when x is negative', () => { + expect(() => + validateRegionOfInterest({ x: -0.1, y: 0, width: 0.5, height: 0.5 }) + ).toThrow(/x and y must be >= 0/); + }); + + it('throws when y is negative', () => { + expect(() => + validateRegionOfInterest({ x: 0, y: -0.1, width: 0.5, height: 0.5 }) + ).toThrow(/x and y must be >= 0/); + }); + + it('throws when width is zero or negative', () => { + expect(() => + validateRegionOfInterest({ x: 0, y: 0, width: 0, height: 0.5 }) + ).toThrow(/width and height must be > 0/); + }); + + it('throws when height is zero or negative', () => { + expect(() => + validateRegionOfInterest({ x: 0, y: 0, width: 0.5, height: -1 }) + ).toThrow(/width and height must be > 0/); + }); + + it('throws when normalized x + width exceeds 1', () => { + expect(() => + validateRegionOfInterest({ x: 0.6, y: 0, width: 0.5, height: 0.5 }) + ).toThrow(/x \+ width and y \+ height must be <= 1/); + }); + + it('throws when normalized y + height exceeds 1', () => { + expect(() => + validateRegionOfInterest({ x: 0, y: 0.6, width: 0.5, height: 0.5 }) + ).toThrow(/x \+ width and y \+ height must be <= 1/); + }); + + it('does not apply the normalized bounds check for pixel regions', () => { + const roi: RegionOfInterest = { + x: 500, + y: 500, + width: 500, + height: 500, + unit: 'pixel', + }; + + expect(() => validateRegionOfInterest(roi)).not.toThrow(); + }); +}); diff --git a/src/core/NativeBridge.ts b/src/core/NativeBridge.ts index 3667ea0..26f8e58 100644 --- a/src/core/NativeBridge.ts +++ b/src/core/NativeBridge.ts @@ -1,7 +1,8 @@ import { Platform } from 'react-native'; import VisionCameraMLKitModule from '../VisionCameraMLKitSpec'; import { INVALID_URI_ERROR, MISSING_MODULE_ERROR } from '../shared/constants'; -import type { MLKitFeature } from './types'; +import { validateRegionOfInterest } from './validateRegionOfInterest'; +import type { ImageProcessingBaseOptions, MLKitFeature } from './types'; /** * Bridge class for native MLKit image processing @@ -17,7 +18,7 @@ export class NativeBridge { static async processImage( feature: MLKitFeature, uri: string, - options: object = {} + options: ImageProcessingBaseOptions = {} ): Promise { if ( !VisionCameraMLKitModule || @@ -27,6 +28,10 @@ export class NativeBridge { } const normalizedUri = this.validateAndNormalizeUri(uri); + if (options.roi) { + validateRegionOfInterest(options.roi); + } + return await VisionCameraMLKitModule.processImage( feature, normalizedUri, diff --git a/src/core/PluginFactory.ts b/src/core/PluginFactory.ts index f1c39d9..27c5e5f 100644 --- a/src/core/PluginFactory.ts +++ b/src/core/PluginFactory.ts @@ -3,6 +3,7 @@ import { FEATURE_ERROR_MAP } from '../shared/constants'; import type { BarcodeScanningOptions } from '../features/barcode-scanning/types'; import type { TextRecognitionOptions } from '../features/text-recognition/types'; import { MLKIT_FEATURE_KEYS } from './constants'; +import { validateRegionOfInterest } from './validateRegionOfInterest'; import { VisionCameraMLKit } from './VisionCameraMLKit'; import type { MLKitBaseOptions, MLKitFeature } from './types'; @@ -74,6 +75,10 @@ buildscript { static initPlugin(feature: MLKitFeature, options: MLKitBaseOptions) { PluginFactory.assertFeatureAvailable(feature); + if (options.roi) { + validateRegionOfInterest(options.roi); + } + switch (feature) { case MLKIT_FEATURE_KEYS.TEXT_RECOGNITION: return VisionCameraMLKit.createTextRecognizer( diff --git a/src/core/types.ts b/src/core/types.ts index 6ffb723..ff24836 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -21,6 +21,30 @@ export type Corner = { y: number; }; +/** + * Whether a {@link RegionOfInterest}'s `x`/`y`/`width`/`height` are + * normalized to the `0..1` range (relative to the source frame/image) or + * absolute pixel values. + */ +export type RegionOfInterestUnit = 'normalized' | 'pixel'; + +/** + * Restricts ML Kit processing to a rectangular region of the source + * frame/image. Cropping happens before recognition to reduce CPU/GPU work; + * result coordinates (bounding boxes, corners) are always mapped back to + * full source coordinates, so overlays don't need to know a crop happened. + */ +export type RegionOfInterest = { + x: number; + y: number; + width: number; + height: number; + /** + * @default 'normalized' + */ + unit?: RegionOfInterestUnit; +}; + export type MLKitBaseOptions = { /** * Optional image downscaling for performance optimization. @@ -34,6 +58,12 @@ export type MLKitBaseOptions = { * @default false */ invertColors?: boolean; + /** + * Crop processing to a rectangular region of the frame before running + * ML Kit, to reduce CPU/GPU work. Result coordinates are mapped back to + * full source coordinates. + */ + roi?: RegionOfInterest; /** * @deprecated Throttle frames manually inside your `useFrameOutput` `onFrame` callback instead. * Process one frame, then skip the next N frames before processing again. @@ -92,6 +122,12 @@ export type ImageProcessingBaseOptions = { * @default false */ invertColors?: boolean; + /** + * Crop processing to a rectangular region of the image before running + * ML Kit, to reduce CPU/GPU work. Result coordinates are mapped back to + * full source coordinates. + */ + roi?: RegionOfInterest; }; export type ImageProcessingError = diff --git a/src/core/validateRegionOfInterest.ts b/src/core/validateRegionOfInterest.ts new file mode 100644 index 0000000..2d4abba --- /dev/null +++ b/src/core/validateRegionOfInterest.ts @@ -0,0 +1,29 @@ +import type { RegionOfInterest } from './types'; + +/** + * Validates a {@link RegionOfInterest} before it crosses into native code. + * Fails fast on out-of-bounds values instead of letting native preprocessing + * fail deep inside the crop step. + * @throws {Error} If the region is invalid for its unit. + */ +export const validateRegionOfInterest = (roi: RegionOfInterest): void => { + const { x, y, width, height, unit = 'normalized' } = roi; + + if (x < 0 || y < 0) { + throw new Error( + `Invalid RegionOfInterest: x and y must be >= 0 (got x=${x}, y=${y}).` + ); + } + + if (width <= 0 || height <= 0) { + throw new Error( + `Invalid RegionOfInterest: width and height must be > 0 (got width=${width}, height=${height}).` + ); + } + + if (unit === 'normalized' && (x + width > 1 || y + height > 1)) { + throw new Error( + `Invalid RegionOfInterest: normalized x + width and y + height must be <= 1 (got x=${x}, width=${width}, y=${y}, height=${height}).` + ); + } +}; diff --git a/src/index.ts b/src/index.ts index bd55070..8774457 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,6 +25,8 @@ export { type MLKitBaseOptions, type MLKitFeature, type Orientation, + type RegionOfInterest, + type RegionOfInterestUnit, } from './core/types'; export type { BarcodeScanner } from './specs/BarcodeScanner.nitro'; diff --git a/src/specs/MLKitVisionTypes.ts b/src/specs/MLKitVisionTypes.ts index 17e06db..f0e7b65 100644 --- a/src/specs/MLKitVisionTypes.ts +++ b/src/specs/MLKitVisionTypes.ts @@ -1,5 +1,6 @@ import type { BarcodeFormat } from '../features/barcode-scanning/types'; import type { TextRecognitionLanguage } from '../features/text-recognition/types'; +import type { RegionOfInterest } from '../core/types'; /** * ML Kit features that can be compiled into this package. @@ -35,6 +36,13 @@ export interface MLKitRecognizerOptions { * Legacy frame skipping option kept for compatibility while v5 examples use frame output throttling. */ frameProcessInterval?: number; + + /** + * Crop processing to a rectangular region of the frame before running + * ML Kit, to reduce CPU/GPU work. Result coordinates are mapped back to + * full source frame coordinates. + */ + roi?: RegionOfInterest; } /** From 8106013aa1cf9c1769e7da5d21e8c6f17431eaca Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Tue, 14 Jul 2026 00:10:34 -0300 Subject: [PATCH 32/45] fix(ios): register VisionCameraMLKitModule for static image processing Swift-only native modules are never auto-discovered by React Native's bridge; they require a companion Objective-C file using RCT_EXTERN_MODULE to actually register the class. That file never existed for VisionCameraMLKitModule, so TurboModuleRegistry.getEnforcing threw at runtime for any static-image call (processImageTextRecognition / processImageBarcodeScanning) on iOS - confirmed via device testing. Pre-existing since the original v1 rewrite, unrelated to the ROI feature; never caught because the static-image path had not been exercised on a real iOS build before. The live-frame path is unaffected - it uses Nitro HybridObjects, a separate registration mechanism, already working. Android is unaffected: its equivalent module registers explicitly via BaseReactPackage/ReactModuleInfoProvider (isTurboModule = false), which does not depend on this bridge file. --- .../Modules/VisionCameraMLKitModuleBridge.m | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 ios/Infrastructure/Modules/VisionCameraMLKitModuleBridge.m diff --git a/ios/Infrastructure/Modules/VisionCameraMLKitModuleBridge.m b/ios/Infrastructure/Modules/VisionCameraMLKitModuleBridge.m new file mode 100644 index 0000000..3e5e466 --- /dev/null +++ b/ios/Infrastructure/Modules/VisionCameraMLKitModuleBridge.m @@ -0,0 +1,11 @@ +#import + +@interface RCT_EXTERN_MODULE(VisionCameraMLKitModule, NSObject) + +RCT_EXTERN_METHOD(processImage:(NSString *)feature + path:(NSString *)path + options:(id)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) + +@end From f5b5df65ace7b04af103b295b05c6b7a5264c4ee Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 26 Jul 2026 21:25:21 -0300 Subject: [PATCH 33/45] fix(example): replace @gorhom/bottom-sheet with @swmansion/react-native-bottom-sheet (#14) * fix(example): replace @gorhom/bottom-sheet with @swmansion/react-native-bottom-sheet Removes the suspected root cause of the reverted Terminal sheet toggle and close-on-blur bugs: the new library's sheet host, gestures, snapping, and scroll negotiation all run natively and depend on neither react-native-gesture-handler nor react-native-reanimated, unlike @gorhom's implementation which was never validated against our RNGH v3 combo. TerminalProvider moves from an imperative BottomSheetModal ref (present()/dismiss()) to a controlled index state (0 closed, 1 open), and stabilizes open/close with useCallback (previously recreated on every isOpen change). TerminalBottomSheet swaps ModalBottomSheet/FlatList in for BottomSheetModal/BottomSheetFlatList and hand-builds the handle pill and rounded background the old library provided by default, to keep the same visual styling. Percentage snap points (25/60/90%) become pixel detents derived from useWindowDimensions, with 0 as the new explicit closed detent. Scoped narrowly to the swap itself: behavior (button-only open, no toggle, no auto-close-on-blur) is unchanged from before. Toggle/close-on-blur remain a separate follow-up, now cheaper thanks to the controlled index model. * fix(example): restore sheet z-order and add Terminal toggle/close-on-blur The library swap dropped @gorhom's containerStyle zIndex/elevation (assumed to be gorhom-specific boilerplate), which was actually what kept the Terminal sheet above PortalProvider's zIndex:1/elevation:1 host view (used by PortalSegmentedToggle). Restored via ModalBottomSheet's `style` prop, since the new library exposes no equivalent containerStyle prop directly. Also delivers the toggle and close-on-blur behavior called out as out-of-scope in the previous commit, now that it's cheap with the controlled-index model: - TerminalProvider exposes a stabilized `toggle()` alongside open/close. - CameraControls' terminal button (icon-only, no explicit label) now toggles instead of only ever opening; renamed onOpenTerminal -> onToggleTerminal to match the sibling onToggleTorch/onToggleFpsGraph props. ImageScreen's two explicit "open" call sites (auto-open after processing, "Open Terminal" button) are unchanged, since toggling those would be the wrong affordance. - CameraScreen and ImageScreen close the terminal via useFocusEffect's cleanup on blur (covers Settings navigation, Home, and switching between Camera/Image through PortalSegmentedToggle's stack replace), instead of the previous global NavigationContainer.onStateChange attempt that broke opening the sheet. --- example/ios/Podfile.lock | 178 ++++++++++-------- example/package.json | 2 +- example/src/App.tsx | 6 +- example/src/components/ui/CameraControls.tsx | 10 +- .../components/views/TerminalBottomSheet.tsx | 105 +++++++---- example/src/providers/TerminalProvider.tsx | 29 ++- example/src/screens/CameraScreen.tsx | 13 +- example/src/screens/ImageScreen.tsx | 11 +- yarn.lock | 48 ++--- 9 files changed, 222 insertions(+), 180 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index ee67160..3570963 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -2115,6 +2115,28 @@ PODS: - React-perflogger (= 0.86.0) - React-utils (= 0.86.0) - ReactNativeDependencies + - ReactNativeBottomSheet (0.17.0-next.1): + - hermes-engine + - RCTRequired + - RCTTypeSafety + - React-Core + - React-Core-prebuilt + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - ReactNativeDependencies + - Yoga - ReactNativeDependencies (0.86.0) - RNGestureHandler (3.0.2): - hermes-engine @@ -2586,6 +2608,7 @@ DEPENDENCIES: - ReactAppDependencyProvider (from `build/generated/ios/ReactAppDependencyProvider`) - ReactCodegen (from `build/generated/ios/ReactCodegen`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "ReactNativeBottomSheet (from `../node_modules/@swmansion/react-native-bottom-sheet`)" - ReactNativeDependencies (from `../node_modules/react-native/third-party-podspecs/ReactNativeDependencies.podspec`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNPermissions (from `../node_modules/react-native-permissions`) @@ -2779,6 +2802,8 @@ EXTERNAL SOURCES: :path: build/generated/ios/ReactCodegen ReactCommon: :path: "../node_modules/react-native/ReactCommon" + ReactNativeBottomSheet: + :path: "../node_modules/@swmansion/react-native-bottom-sheet" ReactNativeDependencies: :podspec: "../node_modules/react-native/third-party-podspecs/ReactNativeDependencies.podspec" RNGestureHandler: @@ -2822,96 +2847,97 @@ SPEC CHECKSUMS: MLKitTextRecognitionKorean: 4877b6a8d95cd8846586e83f85b2f3e9ac93a737 MLKitVision: e858c5f125ecc288e4a31127928301eaba9ae0c1 nanopb: 438bc412db1928dac798aa6fd75726007be04262 - NitroImage: 2f592086cbdecbbaa1c10dd97e0cdd43b1bb0817 - NitroModules: b4174dd303728e16ad1afb79f64c1a5c69a3b373 + NitroImage: 0bbe0fc7032343a8e574e9ebcfafbbd81e1b37c2 + NitroModules: 7e004af1d2cbc3072b071de62aeb9c147ef79d34 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 RCTDeprecation: 2a74a2c57675e64419bd89078efde81f7c1de90b RCTRequired: 30451112e6fef4e6f31b4e7eee0845156e35e4b0 RCTSwiftUI: 5aaf0b07e747ba749dc6acc94d8bd41eea4b570f - RCTSwiftUIWrapper: ab2ca548be15d63afa95103afc8685a7c3eab78b + RCTSwiftUIWrapper: 1feaf484029fd6294fe8bc401d28e1d803eca142 RCTTypeSafety: 3eaed17dbddb0b989208b062ea14c44d412b9780 React: 2574546f2d017abd14d0c9b48cf2b6a0547c2591 React-callinvoker: 03cd4b931d1d583d87aae99b8f7b6fe26bf571ee - React-Core: 1c824d9c7dd8aa760b5f1b50d5a54c2a3f598f87 + React-Core: b0a0038e05720fb8148759404e9c354e3e60043c React-Core-prebuilt: 7da85c26e5616d52f119b81ce5e3e6fc5c9bda49 - React-CoreModules: 2f9ed75bca7f6dea2b70e8a1f4a5ca9b6be52d76 - React-cxxreact: 7103d5ba69848c039e11079e74ceede05efe795e + React-CoreModules: a1121b3182200be11e078940159c67ff95ee98b4 + React-cxxreact: 8f74977959064bee99af63bb0ad95b6c3dbec921 React-debug: 8cc8d99ccc664ef9e873027f7fc3fb0a50496012 - React-defaultsnativemodule: 603c108411d39d7bb5ccb8c270f1f50cb6207e10 - React-domnativemodule: c49a502edc85f515a029c341fe5fba155fa6675c - React-Fabric: acc4915d719db793c5853e5c74b54ea5aa48e865 - React-FabricComponents: aebebc98914a4a8fc962fa7b5b98ebaf250acb68 - React-FabricImage: 42e99e48ff73c8b20cee229e622977d0b97b6247 - React-featureflags: c6d8d8d52acf3a956485726076b73eeff7935340 - React-featureflagsnativemodule: c992de92dcf30dcf09efdec17752abe4128ec55f - React-graphics: 239fd9b0512e539d3563f0825618f4e49795eefd - React-hermes: ae4685ca9fa5f47003bc594d3f146e29284136d1 - React-idlecallbacksnativemodule: 10a5be842ab181953c772a3f28cdf94572833eb0 - React-ImageManager: c36a56c3b13eba92e1d0d30da35d0a1ccf29cd6c - React-intersectionobservernativemodule: 71404bfa47d31e4143cc9db3e26178b5cfcd07e1 - React-jserrorhandler: ca2eeb03c1a77bbfab1b5425b943e3e8d92295f2 - React-jsi: c4b6daf8a31ac54f2db49cd6cce29720fec1f3a8 - React-jsiexecutor: acdc1a217b7ea29bcf6315b770d01e6b1c2fca14 - React-jsinspector: 95d6394efe9fb4a64ed33afc076b32a6384c8514 - React-jsinspectorcdp: 6ce51378a552feaaaac1a7b0d995fa0c49fa4f8c - React-jsinspectornetwork: 0db435c9264f200635fdf294a3b643dd3946d4cf - React-jsinspectortracing: 2e4470e1f301ff597bd65299aa95da4bae6e5c4f - React-jsitooling: 796bc991cdce68e2cc059d0271a28e0d4f8b0891 - React-jsitracing: f3008e7b5e1d9de8d9f2ca1b85824ed86b0cea00 - React-logger: fff73f4ceecad968c97baafdc77dcf84befc38b6 - React-Mapbuffer: ce449ccdf3b80384415b925606be8a4bdcfc65d3 - React-microtasksnativemodule: 2eb3f49d0d8e77b5343455eccd057010b8d38b6b - React-mutationobservernativemodule: f0a0d5ae9b51caf7becbeabf836d716cfedb6bf2 - react-native-blur: ea6c78ddeaa7aa0fce36ea29d1f81dd23063032a - react-native-image-picker: 23540feacc79c63c60857f318fdfa8477c26e70a - react-native-safe-area-context: 91a90d98c310adcc90a511e5aeb6046d7c19d885 - react-native-slider: f9910f69950b9953c46e091377c1265b71eb01f0 - React-NativeModulesApple: a092d89b58f635ebfab88048b0eda9fb516819fd - React-networking: 968bbbe73590149feb1e72b2af4f6a68e4796ece + React-defaultsnativemodule: 7c8182addbb79ec576c589186b51e3fb405da7da + React-domnativemodule: 576bd1e6ad81477997dcb556e6db1f005134356b + React-Fabric: 4cbd22315ddb9e33441437f851117d314d1b4ad3 + React-FabricComponents: efe374aecaf1bbc2fb9a195ece4b50fd9f66bfa0 + React-FabricImage: cfeffc0b527de8bc2dc5a50618dad119361d0ec3 + React-featureflags: 98931b22f5f4b861a3985b75bd79c5fcbe4618ab + React-featureflagsnativemodule: 518c2e279cdb3835604f7e8fb15f4c2691336b4c + React-graphics: ac9ff09da733d98c0e5eddb589dc7f9f082c9531 + React-hermes: bd1a14a982e63ee75e9c9cb8d11d3d4b5f330196 + React-idlecallbacksnativemodule: b6378d3bca83d8284e1abaa51251ee1ce570d85a + React-ImageManager: 4936349da191d1bc29ed2bbaf5a2dd74bfdf5ed8 + React-intersectionobservernativemodule: d9a6451f2aa1f1c989d5d5e0d967db2c07d0c48c + React-jserrorhandler: 2455567e21fcc036f96015b2b8eb4eb13e0d8d05 + React-jsi: bf5ec8e7a7b26bddc39ab7f821af5aad8bb94a33 + React-jsiexecutor: cf6c957f578dd491ec8ae516a39aff2cf7369f1a + React-jsinspector: 63e9b9f9a5b6c51bf38deea2b379a63754c0ab0d + React-jsinspectorcdp: 327082854cdebb1f500a669c51146b9fd871058c + React-jsinspectornetwork: bbc5c2e881252075d63dc679f18cd856c2def94e + React-jsinspectortracing: ea6ec857ead0de4e6bbb2e12ee48ae4381938874 + React-jsitooling: 52e278666b35e856414d613d7dc50d1f0d50d9a5 + React-jsitracing: 642172314677a1d1780b81f4e2e4a3f4d3ccf238 + React-logger: 35cda3ab8977c6c4546e3cf6dc8f91bacec25934 + React-Mapbuffer: c058c051f571589e37a29bce18d0e9b730faa9d0 + React-microtasksnativemodule: 03752e60738a797bb49f4d6cb0291dfa398099b8 + React-mutationobservernativemodule: 2e568e13c98bd185fe83266795233fa7ad2cbc19 + react-native-blur: baf5874b4abf3bb8b2012172a3d0905653a1a2b5 + react-native-image-picker: 2146157d5d37a34c2063d1d2ed01081b56d0b87b + react-native-safe-area-context: 3cd1f988c96f187e15b49b86b0a2068a6cb19393 + react-native-slider: 73473ab537b86b7ca1b21a28d16019d3cb63e927 + React-NativeModulesApple: a4713821c9caae40ae33eef933a6e692a6f4a81d + React-networking: b344a1a41a4729ef859123b7a234f3092761cb25 React-oscompat: 0b72a7e926954a0415ccd83e0748b6561fe45367 - React-perflogger: 865984e492514aa6e5279fc3e663132cfa4d5022 - React-performancecdpmetrics: 2efbf9bdb48c8d8446f4dd10e8bf0dc5d711772f - React-performancetimeline: 9d256484bff1513481be9f234baba694dc3b52e2 + React-perflogger: a0d581c6cc174e847877107ddac3f4fc9b518dc7 + React-performancecdpmetrics: 91d3d0dce2e3a239ea68cc2882e75b11a61cbf2e + React-performancetimeline: 4b9e04002c1ddba95167276a86424e5face65605 React-RCTActionSheet: 902c79deec52f99cc48b1051b59bcbe86787d339 - React-RCTAnimation: 09cf722039ae30ff5d64e2b011ff054ae651c3b6 - React-RCTAppDelegate: a47de6fddb7eb0c028abba83138a5ce283bd7e77 - React-RCTBlob: 38c418d067e0c61818223503063310122e44c588 - React-RCTFabric: 480ca2a105730e1790cfd13291d086cb53725825 - React-RCTFBReactNativeSpec: 63131378510a5191515a4adfc308e65b465106f4 - React-RCTImage: 3ce36f82441b76b715818ee7ee95f6f5b34f9ee1 - React-RCTLinking: 2f7b5ed4983122e5115732d25a4360960eab583c - React-RCTNetwork: dcd3b180f33da86f5dc5e928a816eb5464fa7f16 - React-RCTRuntime: 6ef8e778fbab426b12eb5a9b5f7a0e86313c5a10 - React-RCTSettings: b97727ec8c55c35bd284457a647c938c040b942b - React-RCTText: 4d1b88f6d3e1a43afe46706d956ec6664c87b984 - React-RCTVibration: 415d14d6a1a64bf947fcef6b193915a494431168 + React-RCTAnimation: f50eac1c9dbea36c4abc69cb24e2da4364374055 + React-RCTAppDelegate: a5d746fec869d15a8f6eecce60510c18e52135d5 + React-RCTBlob: e544dc72edc6f277c67e85493b0a405e1993c250 + React-RCTFabric: 8003ea99830c294857d41469a888b033e5ad91a6 + React-RCTFBReactNativeSpec: 8a3506041284fdc75da90e6fa40fb9b575414a39 + React-RCTImage: fb85496986a53d9e11a835124a083b7c7c33f898 + React-RCTLinking: b8eafcdedaebf981a18396db61d1190f5279ea81 + React-RCTNetwork: 03573c4fcc73e28f48fea7aea69310b6955a749c + React-RCTRuntime: 08d58920b217b2d7715d5da595794e63520cd2d9 + React-RCTSettings: f9b278b46e5de1ff8b843bb7aefaee721f06572d + React-RCTText: 7266943febf3382dac8adce9ba0de95b90c26e9e + React-RCTVibration: 27e1952701ec858922062f25a59b0c48e7153151 React-rendererconsistency: ef8519bdd9931261c6561bfad6506356b8108387 - React-renderercss: 1aa1bf99fa2ace143eb87d5190fd43609fc1e832 - React-rendererdebug: 40a4fb3dea21a7ac1759ca0eb6c88a924aecb075 - React-RuntimeApple: 84fadbb4fe8ca531e15e29a22af05911f17569c6 - React-RuntimeCore: f4d9af5f16d37e63308cb03b36c89d01e7f68a06 - React-runtimeexecutor: 4d59410f66af529d04c84c8b152ced07dabc471e - React-RuntimeHermes: fd719d8f4d9ce79636fe2e09e94b0ac31b7b263b - React-runtimescheduler: 784033620aa5515e2f45a60369eed4d32f9400b8 - React-timing: a16df9ae98f950396d9ce3abf43cb0cb2f21194c - React-utils: b68ee619aef28d8f9b85532d98db0327f7bdf743 - React-viewtransitionnativemodule: 11fe091101d381451b1542c37b2745900254f096 - React-webperformancenativemodule: b5d249419f1546663845c82f24de4e18a3180997 - ReactAppDependencyProvider: dcdd0e1b9559a6d8d8aea05286f4ed085091978e - ReactCodegen: afe0d436ee089d7bea6418429d8312b1d23a6801 - ReactCommon: d5c1bb4427bf51c443de5926aac332c89ddd9363 + React-renderercss: edc1b0a18b12d681f8edbc779039dc6db32cf7b2 + React-rendererdebug: 074ec3420f3bab2bea498da65e5a17e9288c90d0 + React-RuntimeApple: 180682587e82ae4fc525f7d6636ed5db39c1e78c + React-RuntimeCore: 991cdb27072f7885750fdad4a70c2b919533e4ef + React-runtimeexecutor: bfec6ca5e62c6b2460a4adf0fddce0c4db0a8748 + React-RuntimeHermes: dcb07afe0f38c1cbaf48b7a1bf4904e867529726 + React-runtimescheduler: fdf0b71e519e416ad38c76525df43eba8dd78db2 + React-timing: 7b538f379678210bf30d8541eb4cf007c000c6f3 + React-utils: a15da1da76aee3dd0ed9f51d62bf1c09be17c221 + React-viewtransitionnativemodule: 8556c844dcd691ab3fe1b32bbf8f58c8c9a49c75 + React-webperformancenativemodule: 4d1d0258107fe0cb8ef5c9ac17a6a099785f2995 + ReactAppDependencyProvider: adf5403892170fbe996fce485a703f25df08db66 + ReactCodegen: ee39bbc365b4791c9b5183b52c533ebf586e1d8d + ReactCommon: 4d01bfb533b93a2103329dfefc74db6a9e272888 + ReactNativeBottomSheet: 3551d418c1d10cd49e0907d68300374544fb8d00 ReactNativeDependencies: fa0a54b3f5319ae0e3b9aff32bfee7a424b88e66 - RNGestureHandler: 953af68599a35f425ea962dda2ee15ee4e3ca719 - RNPermissions: 82a6127b299cfb3f97c8d36919804a916e58ce26 - RNReanimated: fcf6a8cdd698fb09a811a8d6c5bcfe36575d965d - RNScreens: 62693e770c4b73093c018aa7845b691f5ba9988d - RNSVG: 6cae8d4082913be2eb43eaec2a456222c42195ed - RNWorklets: 53439575fed8525fb8d1cabcc05a3d4d91e6ffe4 - VisionCamera: 2d364657571d6b6404830269d05b1de244586a40 - VisionCameraMLKit: fe1db106ab49d735ac1972c0af27c06778ef8c7e - VisionCameraWorklets: 8393549fac54f37a4cf6a7b1088688cf9b309b7f + RNGestureHandler: ef19613372e4c87374fe9494862e366d82d1852c + RNPermissions: 38d9cf58be74d148123c93d0e0713092e8e59260 + RNReanimated: 516d842d105966ae05a6a1ad7ad2a0dc1e968d6b + RNScreens: 37cece0483decca1c3300bb1c2225c798039b8fc + RNSVG: ffe18ba0e8fbad864bf22d6f0060a4ee88ec9bf3 + RNWorklets: 82a5f27dcf3bb69267ec6ce662a756a4d3f61522 + VisionCamera: 73a21a8ed0feecd7c579d79ae25eaf8c3e63c767 + VisionCameraMLKit: c85a989fbaae33532636fdf9cab21b287ddf8a3f + VisionCameraWorklets: 0f70d789dfce3d2efc67db7e106e2f4a32bbe72e Yoga: 94172d87dd5d83fc929bf1ee53121b5d4490b5f2 PODFILE CHECKSUM: bfa9c28a161369f7b66f55890deec95fe59f1504 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/example/package.json b/example/package.json index 4268e09..04adc3d 100644 --- a/example/package.json +++ b/example/package.json @@ -10,12 +10,12 @@ "build:ios": "react-native build-ios --mode Debug --destination \"${IOS_DESTINATION:-generic/platform=iOS Simulator}\"" }, "dependencies": { - "@gorhom/bottom-sheet": "^5.2.14", "@react-native-community/blur": "^4.4.1", "@react-native-community/slider": "^5.1.2", "@react-navigation/elements": "^2.9.3", "@react-navigation/native": "^7.1.26", "@react-navigation/native-stack": "^7.9.0", + "@swmansion/react-native-bottom-sheet": "0.17.0-next.1", "lucide-react-native": "^0.562.0", "react": "19.2.3", "react-native": "0.86.0", diff --git a/example/src/App.tsx b/example/src/App.tsx index 71918c2..960904f 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,4 +1,4 @@ -import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; +import { BottomSheetProvider } from '@swmansion/react-native-bottom-sheet'; import { StyleSheet } from 'react-native'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { SafeAreaProvider } from 'react-native-safe-area-context'; @@ -15,13 +15,13 @@ const App = () => ( - + - + diff --git a/example/src/components/ui/CameraControls.tsx b/example/src/components/ui/CameraControls.tsx index 3b6fccc..e9aa8c0 100644 --- a/example/src/components/ui/CameraControls.tsx +++ b/example/src/components/ui/CameraControls.tsx @@ -23,7 +23,7 @@ type CameraControlsProps = { onToggleTorch: () => void; onToggleFpsGraph: () => void; onOpenSettings: () => void; - onOpenTerminal: () => void; + onToggleTerminal: () => void; outputOrientation: SharedValue; }; @@ -33,7 +33,7 @@ const CameraControls = ({ onToggleTorch, onToggleFpsGraph, onOpenSettings, - onOpenTerminal, + onToggleTerminal, outputOrientation, }: CameraControlsProps) => { const Torch = torch === 'on' ? Zap : ZapOff; @@ -71,9 +71,9 @@ const CameraControls = ({ diff --git a/example/src/components/views/TerminalBottomSheet.tsx b/example/src/components/views/TerminalBottomSheet.tsx index 5c7a2a4..65a1de0 100644 --- a/example/src/components/views/TerminalBottomSheet.tsx +++ b/example/src/components/views/TerminalBottomSheet.tsx @@ -1,12 +1,12 @@ -import { BottomSheetFlatList, BottomSheetModal } from '@gorhom/bottom-sheet'; +import { ModalBottomSheet } from '@swmansion/react-native-bottom-sheet'; +import { useCallback, useMemo, useState } from 'react'; import { - forwardRef, - useCallback, - useMemo, - useState, - type ComponentProps, -} from 'react'; -import { Pressable, StyleSheet, View } from 'react-native'; + FlatList, + Pressable, + StyleSheet, + useWindowDimensions, + View, +} from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useTheme } from '../../providers/ThemeProvider'; import { @@ -15,31 +15,30 @@ import { } from '../../stores/terminalStore'; import { TerminalEntryRow, Text } from '../ui'; -type Optional = Omit & Partial>; +type TerminalBottomSheetProps = { + index: number; + onIndexChange: (index: number) => void; +}; -type TerminalBottomSheetProps = Optional< - ComponentProps, - 'children' ->; - -const TerminalBottomSheet = forwardRef< - BottomSheetModal, - TerminalBottomSheetProps ->((props, ref) => { +const TerminalBottomSheet = ({ + index, + onIndexChange, +}: TerminalBottomSheetProps) => { const { theme } = useTheme(); const insets = useSafeAreaInsets(); + const { width: windowWidth, height: windowHeight } = useWindowDimensions(); const entries = useTerminalStore((state) => state.entries); const clear = useTerminalStore((state) => state.clear); const [wrapLines, setWrapLines] = useState(true); - const snapPoints = useMemo(() => ['25%', '60%', '90%'], []); + const detents = useMemo( + () => [0, windowHeight * 0.25, windowHeight * 0.6, windowHeight * 0.9], + [windowHeight] + ); - const handleClose = useCallback(() => { - if (!ref || typeof ref === 'function') return; - ref.current?.dismiss(); - }, [ref]); + const handleClose = useCallback(() => onIndexChange(0), [onIndexChange]); const renderItem = useCallback( ({ item }: { item: TerminalEntry }) => ( @@ -64,17 +63,38 @@ const TerminalBottomSheet = forwardRef< [] ); + const surface = useMemo( + () => ( + + ), + [theme.colors.surface] + ); + return ( - + + + - item.id} renderItem={renderItem} @@ -121,9 +141,9 @@ const TerminalBottomSheet = forwardRef< maxToRenderPerBatch={8} ItemSeparatorComponent={ItemSeparatorComponent} /> - + ); -}); +}; const styles = StyleSheet.create({ header: { @@ -150,10 +170,21 @@ const styles = StyleSheet.create({ paddingHorizontal: 16, paddingTop: 12, }, - modalContainer: { + surface: { + borderRadius: 15, + }, + sheetHost: { zIndex: 10, elevation: 10, }, + handleContainer: { + alignItems: 'center', + paddingVertical: 10, + }, + handleIndicator: { + height: 4, + borderRadius: 4, + }, itemSeparator: { height: 12, }, @@ -163,6 +194,4 @@ const styles = StyleSheet.create({ }, }); -TerminalBottomSheet.displayName = 'TerminalBottomSheet'; - export { TerminalBottomSheet }; diff --git a/example/src/providers/TerminalProvider.tsx b/example/src/providers/TerminalProvider.tsx index 993ceba..4e42b32 100644 --- a/example/src/providers/TerminalProvider.tsx +++ b/example/src/providers/TerminalProvider.tsx @@ -1,19 +1,20 @@ -import type { SNAP_POINT_TYPE } from '@gorhom/bottom-sheet'; -import { BottomSheetModal } from '@gorhom/bottom-sheet'; import type { ReactNode } from 'react'; import { createContext, useCallback, useContext, useMemo, - useRef, useState, } from 'react'; import { TerminalBottomSheet } from '../components/views'; +const CLOSED_INDEX = 0; +const OPEN_INDEX = 1; + type TerminalContextType = { open: () => void; close: () => void; + toggle: () => void; isOpen: boolean; }; @@ -24,29 +25,25 @@ type TerminalProviderProps = { }; export const TerminalProvider = ({ children }: TerminalProviderProps) => { - const modalRef = useRef(null); - - const [isOpen, setIsOpen] = useState(false); + const [index, setIndex] = useState(CLOSED_INDEX); - const handleChange = useCallback( - (index: number, _position: number, _type: SNAP_POINT_TYPE) => - setIsOpen(index >= 0), + const open = useCallback(() => setIndex(OPEN_INDEX), []); + const close = useCallback(() => setIndex(CLOSED_INDEX), []); + const toggle = useCallback( + () => + setIndex((prev) => (prev === CLOSED_INDEX ? OPEN_INDEX : CLOSED_INDEX)), [] ); const value = useMemo( - () => ({ - open: () => modalRef.current?.present(), - close: () => modalRef.current?.dismiss(), - isOpen, - }), - [isOpen] + () => ({ open, close, toggle, isOpen: index > CLOSED_INDEX }), + [open, close, toggle, index] ); return ( {children} - + ); }; diff --git a/example/src/screens/CameraScreen.tsx b/example/src/screens/CameraScreen.tsx index 9af6083..f287c26 100644 --- a/example/src/screens/CameraScreen.tsx +++ b/example/src/screens/CameraScreen.tsx @@ -1,12 +1,13 @@ import { StackActions, + useFocusEffect, useIsFocused, useNavigation, useRoute, type NavigationProp, type RouteProp, } from '@react-navigation/native'; -import { useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { useSharedValue } from 'react-native-reanimated'; import { type CameraRef, @@ -37,7 +38,7 @@ const DEFAULT_ORIENTATION: Orientation = 'portrait'; const CameraScreen = () => { const navigation = useNavigation>(); const { params, name } = useRoute>(); - const { open: openTerminal } = useTerminal(); + const { toggle: toggleTerminal, close: closeTerminal } = useTerminal(); const cameraRef = useRef(null); @@ -76,6 +77,12 @@ const CameraScreen = () => { !hasCameraPermission && requestCameraPermission(); }, [hasCameraPermission, requestCameraPermission]); + useFocusEffect( + useCallback(() => { + return () => closeTerminal(); + }, [closeTerminal]) + ); + if (!hasCameraPermission) return ; if (!device) return ; if (!isPluginId(params.id)) return ; @@ -98,7 +105,7 @@ const CameraScreen = () => { torch={torchState} onToggleTorch={toggleTorch} onToggleFpsGraph={toggleFpsGraph} - onOpenTerminal={openTerminal} + onToggleTerminal={toggleTerminal} onOpenSettings={openSettings} outputOrientation={outputOrientation} /> diff --git a/example/src/screens/ImageScreen.tsx b/example/src/screens/ImageScreen.tsx index c6d43bf..f8c0629 100644 --- a/example/src/screens/ImageScreen.tsx +++ b/example/src/screens/ImageScreen.tsx @@ -1,11 +1,12 @@ import { StackActions, + useFocusEffect, useNavigation, useRoute, type NavigationProp, type RouteProp, } from '@react-navigation/native'; -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import { Image, ScrollView, StyleSheet, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { @@ -40,10 +41,16 @@ const ImageScreen = () => { const { theme } = useTheme(); const insets = useSafeAreaInsets(); - const { open: openTerminal } = useTerminal(); + const { open: openTerminal, close: closeTerminal } = useTerminal(); const addEntry = useTerminalStore((state) => state.addEntry); + useFocusEffect( + useCallback(() => { + return () => closeTerminal(); + }, [closeTerminal]) + ); + const BOTTOM_OFFSET = insets.bottom + 46; const navigation = useNavigation>(); diff --git a/yarn.lock b/yarn.lock index f3ffe8d..3538de0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2200,40 +2200,6 @@ __metadata: languageName: node linkType: hard -"@gorhom/bottom-sheet@npm:^5.2.14": - version: 5.2.14 - resolution: "@gorhom/bottom-sheet@npm:5.2.14" - dependencies: - "@gorhom/portal": "npm:1.0.14" - invariant: "npm:^2.2.4" - peerDependencies: - "@types/react": "*" - "@types/react-native": "*" - react: "*" - react-native: "*" - react-native-gesture-handler: ">=2.16.1" - react-native-reanimated: "*" - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-native": - optional: true - checksum: 10c0/744c718aaa4e66989043378932ea31d8ff510cff289b5561115530ff49218af080d28dbe409191b33ab7d87b1cfab4e7c16cfb0015cf2b6917b26c61f033bc05 - languageName: node - linkType: hard - -"@gorhom/portal@npm:1.0.14": - version: 1.0.14 - resolution: "@gorhom/portal@npm:1.0.14" - dependencies: - nanoid: "npm:^3.3.1" - peerDependencies: - react: "*" - react-native: "*" - checksum: 10c0/86f33afc2ac2656a86a6f3fd1e41565419839576ede2c38333434a93a0a2fe4fb6fc18ab3360579427f2a1fc3b4564b933cc5ae1793a7e2825c93860a00b215f - languageName: node - linkType: hard - "@hapi/hoek@npm:^9.0.0, @hapi/hoek@npm:^9.3.0": version: 9.3.0 resolution: "@hapi/hoek@npm:9.3.0" @@ -3720,6 +3686,16 @@ __metadata: languageName: node linkType: hard +"@swmansion/react-native-bottom-sheet@npm:0.17.0-next.1": + version: 0.17.0-next.1 + resolution: "@swmansion/react-native-bottom-sheet@npm:0.17.0-next.1" + peerDependencies: + react: ">=18.0.0" + react-native: ">=0.76.0" + checksum: 10c0/8ea6751efc9cb2c11ede4f90d2378c95270dc707c08b9718b11bf28c1a4e5b09d33887801e682a7273c9cb1ce8675daba7cf0da0e6ac5f944faf259326d528a9 + languageName: node + linkType: hard + "@tootallnate/quickjs-emscripten@npm:^0.23.0": version: 0.23.0 resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" @@ -9808,7 +9784,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.1, nanoid@npm:^3.3.11": +"nanoid@npm:^3.3.11": version: 3.3.11 resolution: "nanoid@npm:3.3.11" bin: @@ -11007,7 +10983,6 @@ __metadata: "@babel/core": "npm:^7.25.2" "@babel/preset-env": "npm:^7.25.3" "@babel/runtime": "npm:^7.25.0" - "@gorhom/bottom-sheet": "npm:^5.2.14" "@react-native-community/blur": "npm:^4.4.1" "@react-native-community/cli": "npm:20.1.0" "@react-native-community/cli-platform-android": "npm:20.1.0" @@ -11019,6 +10994,7 @@ __metadata: "@react-navigation/elements": "npm:^2.9.3" "@react-navigation/native": "npm:^7.1.26" "@react-navigation/native-stack": "npm:^7.9.0" + "@swmansion/react-native-bottom-sheet": "npm:0.17.0-next.1" "@types/react": "npm:^19.2.0" lucide-react-native: "npm:^0.562.0" react: "npm:19.2.3" From 4d9b4302fca89654f3dd67501339516453f04ed6 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 26 Jul 2026 21:52:05 -0300 Subject: [PATCH 34/45] fix: drop peer/build dependencies the library doesn't actually need (#15) react-native-vision-camera-worklets and react-native-worklets were never imported anywhere in src/, android/, or ios/ - the library's live-frame hooks are worklet-callable Nitro HybridObject methods, not code that imports the Worklets packages directly. They're an app-level opt-in required to use react-native-vision-camera's frame-processor feature at all, a requirement flowing from Vision Camera's own frame-processor API contract, not from this library. Vision Camera itself doesn't declare them as peer dependencies either, confirming this. Blanketing them as required peer deps also wrongly implied the static-image APIs (processImageTextRecognition/processImageBarcodeScanning, which go through the classic native bridge and never touch Vision Camera or Worklets) need a camera at all. react-native-nitro-image is similarly unused anywhere in this library's own code - confirmed absent from src/, android/src, ios/, nitro.json, and the iOS podspec. It's a hard transitive peer/pod dependency of react-native-vision-camera itself (its own peerDependencies and podspec both declare it), so consumers still get nagged for it via Vision Camera's own peer-dependency declaration - verified empirically via `yarn install`, which surfaces "react-native-vision-camera-mlkit doesn't provide react-native-nitro-image ... requested by react-native-vision-camera" without this library redeclaring it. Also removes the matching unused `implementation project(":react-native-nitro-image")` line from android/build.gradle - not referenced by any Kotlin source or generated Nitrogen output on either platform. README updated to reflect the real dependency shape: react-native-vision-camera + react-native-nitro-modules for the base install, react-native-vision-camera-worklets + react-native-worklets only if using the live-frame hooks, and an explicit callout that the static-image APIs need neither. --- README.md | 25 +++++++++++++++++++------ android/build.gradle | 1 - package.json | 8 +------- yarn.lock | 6 ------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 049fb9e..596329f 100644 --- a/README.md +++ b/README.md @@ -25,25 +25,38 @@ A [React Native Vision Camera](https://github.com/mrousavy/react-native-vision-c - iOS 12+ and Android SDK 21+ - [react-native-vision-camera](https://www.npmjs.com/package/react-native-vision-camera) `>=5` (Nitro-based v5, not v4) - [react-native-nitro-modules](https://www.npmjs.com/package/react-native-nitro-modules) -- [react-native-nitro-image](https://www.npmjs.com/package/react-native-nitro-image) -- [react-native-vision-camera-worklets](https://www.npmjs.com/package/react-native-vision-camera-worklets) -- [react-native-worklets](https://www.npmjs.com/package/react-native-worklets) -Install Vision Camera and its Nitro/worklets dependencies: +Install Vision Camera and its Nitro dependency: ```sh -npm i react-native-vision-camera react-native-nitro-modules react-native-nitro-image react-native-vision-camera-worklets react-native-worklets +npm i react-native-vision-camera react-native-nitro-modules cd ios && pod install ``` -Add the Babel plugin in `babel.config.js`: +> `react-native-vision-camera` has its own peer dependencies (currently +> `react-native-nitro-image`); follow the prompts from your package manager +> or the [Vision Camera docs](https://visioncamera.margelo.com/docs) to +> install those too. + +If you're using the **live-frame hooks** (`useTextRecognition`, +`useBarcodeScanning`), you also need Vision Camera's frame processor +dependencies, since those hooks run inside a `useFrameOutput` worklet: + +```sh +npm i react-native-vision-camera-worklets react-native-worklets +``` ```js +// babel.config.js module.exports = { plugins: [['react-native-worklets/plugin']], }; ``` +The **static-image APIs** (`processImageTextRecognition`, +`processImageBarcodeScanning`) don't use frame processors and don't need +Vision Camera, Worklets, or a camera at all. + > For Expo, follow the Vision Camera guide: [visioncamera.margelo.com/docs](https://visioncamera.margelo.com/docs) ## Installation diff --git a/android/build.gradle b/android/build.gradle index 74e837f..5dccb88 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -182,7 +182,6 @@ def kotlin_version = getExtOrDefault("kotlinVersion") dependencies { implementation "com.facebook.react:react-android" api project(":react-native-vision-camera") - implementation project(":react-native-nitro-image") implementation project(":react-native-nitro-modules") implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "androidx.camera:camera-core:1.5.2" diff --git a/package.json b/package.json index 73b87b7..edd8f36 100644 --- a/package.json +++ b/package.json @@ -103,11 +103,8 @@ "react": "19.2.3", "react-native": "0.86.0", "react-native-builder-bob": "^0.40.13", - "react-native-nitro-image": "0.15.1", "react-native-nitro-modules": "0.36.1", "react-native-vision-camera": "^5.1.0", - "react-native-vision-camera-worklets": "^5.1.0", - "react-native-worklets": "0.10.2", "release-it": "^19.0.4", "turbo": "^2.5.6", "typescript": "^5.9.2" @@ -115,11 +112,8 @@ "peerDependencies": { "react": "*", "react-native": "*", - "react-native-nitro-image": "*", "react-native-nitro-modules": "*", - "react-native-vision-camera": ">=5", - "react-native-vision-camera-worklets": "*", - "react-native-worklets": "*" + "react-native-vision-camera": ">=5" }, "workspaces": [ "example" diff --git a/yarn.lock b/yarn.lock index 3538de0..33faa2a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11045,22 +11045,16 @@ __metadata: react: "npm:19.2.3" react-native: "npm:0.86.0" react-native-builder-bob: "npm:^0.40.13" - react-native-nitro-image: "npm:0.15.1" react-native-nitro-modules: "npm:0.36.1" react-native-vision-camera: "npm:^5.1.0" - react-native-vision-camera-worklets: "npm:^5.1.0" - react-native-worklets: "npm:0.10.2" release-it: "npm:^19.0.4" turbo: "npm:^2.5.6" typescript: "npm:^5.9.2" peerDependencies: react: "*" react-native: "*" - react-native-nitro-image: "*" react-native-nitro-modules: "*" react-native-vision-camera: ">=5" - react-native-vision-camera-worklets: "*" - react-native-worklets: "*" languageName: unknown linkType: soft From 5468b85dcc8306577dfdd72fc8ab05fc1de25ceb Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 26 Jul 2026 22:33:34 -0300 Subject: [PATCH 35/45] docs: fix inaccurate claims and gaps across README/CONTRIBUTING/docs and npm metadata (#17) * docs: stop overselling unimplemented ML Kit features in npm metadata and README The npm description and keywords listed face detection and pose detection alongside text recognition and barcode scanning as if all were delivered. Only text recognition v2 and barcode scanning are implemented in src/ (see the README's own roadmap table) - everything else is unimplemented Phase 3 work. Reworded the description to reflect what ships today and frame the rest as roadmap, and dropped face-detection/pose-detection from keywords since they misrepresent the package's actual capabilities to anyone evaluating it on npm. Applied the same fix to the README's intro paragraph, which had the identical problem. Also restructured the README's iOS troubleshooting content: the Apple Silicon simulator note was oddly nested under "iOS Orientation Notes" (an unrelated topic), and docs/xcode-26.4-fmt-consteval-workaround.md was never linked from anywhere. Moved both into a new "Troubleshooting" section. * docs: expand thin API result-shape sections in feature docs docs/text-recognition.md's "Result shape" section just named the nesting levels without describing fields, despite the page's stated goal of documenting the API "in more detail than the README quick-start". Expanded it into the full TextRecognitionResult field breakdown (bounds/corners/ languages at every level, Android-only confidence/angle/symbols), sourced from src/features/text-recognition/types.ts, and added the missing TextRecognitionResult type to the import example. docs/barcode-scanning.md's BarcodeData field list didn't explain that format/valueType are raw ML Kit numeric codes versus the formatName/ valueTypeName string equivalents apps should actually match against, or that bounds/corners can be absent and rawValue/rawBytes can be null for potential barcodes - both surprising to a new consumer without reading BarcodeData's type definition directly. * docs: fix inaccurate claims and gaps in CONTRIBUTING and CODE_OF_CONDUCT CONTRIBUTING.md: - Claimed pre-commit hooks verify tests pass; lefthook.yml's pre-commit only runs eslint and tsc, and the commit message check lives in a separate commit-msg hook, not pre-commit. Corrected both claims and pointed contributors at `yarn test` before opening a PR. - Referenced "the pull request template" but no .github/PULL_REQUEST_TEMPLATE.md exists in this repo - replaced with a pointer to the issue templates that do exist. - This is a Nitro Module (native interface in src/specs/*.nitro.ts) but CONTRIBUTING never mentioned `yarn specs` (nitrogen codegen), needed whenever a .nitro.ts spec file changes. Added a "Working with the Nitro spec" section and the missing script to the Scripts list, plus `yarn clean` and `yarn prepare`. - Linked docs/xcode-26.4-fmt-consteval-workaround.md, which was previously unreferenced from anywhere in the repo despite being directly relevant to building the example app's iOS target. - Commit type list was missing `perf`, `ci`, and `build`, all of which are actually used in this repo's git history per commitlint's conventional config. CODE_OF_CONDUCT.md: - Directed harassment/conduct reports to the public GitHub issue tracker, which isn't private - inconsistent with SECURITY.md's own private reporting channel for vulnerabilities. Switched to a direct email contact. --- CODE_OF_CONDUCT.md | 4 +++- CONTRIBUTING.md | 27 +++++++++++++++++++++------ README.md | 8 +++++++- docs/barcode-scanning.md | 6 +++--- docs/text-recognition.md | 23 ++++++++++++++++------- package.json | 6 +++--- 6 files changed, 53 insertions(+), 21 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 805edd4..9439505 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -59,7 +59,9 @@ representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at [issues](https://github.com/pedrol2b/react-native-vision-camera-mlkit/issues). +reported to the community leaders responsible for enforcement by emailing +**pedrolbb.e@gmail.com**. Please do not report Code of Conduct violations via +public GitHub issues, since those reports are not private. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5e6e45d..84f3e2f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,6 +27,16 @@ If you want to use Android Studio or XCode to edit the native code, you can open To edit the Java or Kotlin files, open `android` in Android studio and find the source files at `react-native-vision-camera-mlkit` under `Android`. +> If you hit an iOS build failure in Pods referencing `fmt/include/fmt/base.h` or `FMT_USE_CONSTEVAL` on Xcode 26.4, see [docs/xcode-26.4-fmt-consteval-workaround.md](docs/xcode-26.4-fmt-consteval-workaround.md). + +### Working with the Nitro spec + +This library is a [Nitro Module](https://nitro.margelo.com). The native interface is defined in `src/specs/*.nitro.ts`. If you change a `.nitro.ts` spec file, regenerate the generated Swift/Kotlin/C++ bindings before rebuilding the example app: + +```sh +yarn specs +``` + You can use various commands from the root directory to work with the project. To start the packager, `cd` into the `example` directory, `cd example`, and then: @@ -73,11 +83,14 @@ We follow the [conventional commits specification](https://www.conventionalcommi - `fix`: bug fixes, e.g. fix crash due to deprecated method. - `feat`: new features, e.g. add new method to the module. - `refactor`: code refactor, e.g. migrate from class components to hooks. -- `docs`: changes into documentation, e.g. add usage example for the module.. +- `docs`: changes into documentation, e.g. add usage example for the module. - `test`: adding or updating tests, e.g. add integration tests using detox. -- `chore`: tooling changes, e.g. change CI config. +- `chore`: tooling changes, e.g. change dependency versions. +- `perf`: performance improvements, e.g. optimize a hot path. +- `ci`: CI/CD pipeline changes, e.g. update a GitHub Actions workflow. +- `build`: build system or external dependency changes, e.g. bump the RN version. -Our pre-commit hooks verify that your commit message matches this format when committing. +Our `commit-msg` hook (via commitlint) verifies that your commit message matches this format when committing. ### Linting and tests @@ -85,7 +98,7 @@ Our pre-commit hooks verify that your commit message matches this format when co We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing. -Our pre-commit hooks verify that the linter and tests pass when committing. +Our `pre-commit` hook (via lefthook) runs ESLint and a TypeScript check on staged files. Tests aren't run automatically on commit, so run `yarn test` yourself before opening a pull request. ### Scripts @@ -95,9 +108,12 @@ The `package.json` file contains various scripts for common tasks: - `yarn typecheck`: type-check files with TypeScript. - `yarn lint`: lint files with ESLint. - `yarn test`: run unit tests with Jest. +- `yarn specs`: regenerate native bindings from the Nitro spec (`src/specs/*.nitro.ts`) with nitrogen. +- `yarn clean`: remove build artifacts for the library and the example app (Android/iOS). - `yarn start`: start the Metro server for the example app. - `yarn android`: run the example app on Android. - `yarn ios`: run the example app on iOS. +- `yarn prepare`: build the library for publishing with `react-native-builder-bob` (runs automatically as Yarn's `prepare` lifecycle hook after install; rarely needed manually). ### Sending a pull request @@ -108,5 +124,4 @@ When you're sending a pull request: - Prefer small pull requests focused on one change. - Verify that linters and tests are passing. - Review the documentation to make sure it looks good. -- Follow the pull request template when opening a pull request. -- For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. +- For pull requests that change the API or implementation, discuss with maintainers first by opening an issue (see the [issue templates](.github/ISSUE_TEMPLATE) for bug reports, build errors, and feature requests). diff --git a/README.md b/README.md index 596329f..59aa9a6 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ [![MIT License][license-shield]][license-url] [![NPM Version][npm-version-shield]][npm-version-url] -A [React Native Vision Camera](https://github.com/mrousavy/react-native-vision-camera) plugin that exposes high-performance [Google ML Kit](https://developers.google.com/ml-kit) [frame output](https://visioncamera.margelo.com/docs/frame-output) features such as text recognition (OCR), face detection, barcode scanning, pose detection, and more. +A [React Native Vision Camera](https://github.com/mrousavy/react-native-vision-camera) plugin that exposes high-performance [Google ML Kit](https://developers.google.com/ml-kit) [frame output](https://visioncamera.margelo.com/docs/frame-output) features. Ships today: text recognition (OCR) and barcode scanning. Face detection, pose detection, and other ML Kit vision features are planned — see the [roadmap](#google-ml-kit-vision-features-roadmap) below. > The example app is intentionally heavy and demo-focused. For integration details, follow the documentation below. @@ -400,6 +400,8 @@ const { textRecognition } = useTextRecognition({ iOS camera sensors are fixed in landscape orientation. The frame buffer stays landscape-shaped even when the UI rotates, so ML Kit needs an explicit orientation hint to rotate text correctly. On iOS, pass `outputOrientation` to `textRecognition(frame, { outputOrientation })` so ML Kit can map the buffer to upright text. Android handles rotation automatically. +## Troubleshooting + ### ⚠️ iOS Simulator (Apple Silicon) – Heads-up On Apple Silicon Macs, building for the **iOS Simulator (arm64)** may fail after installing this package. @@ -407,6 +409,10 @@ On Apple Silicon Macs, building for the **iOS Simulator (arm64)** may fail after This is a **known limitation of Google ML Kit**, which does not currently ship an `arm64-simulator` slice for some iOS frameworks. The library works correctly on **physical iOS devices** and on the **iOS Simulator when running under Rosetta**. +### Xcode 26.4 Build Failures (fmt / consteval) + +If your iOS build fails in Pods with errors referencing `fmt/include/fmt/base.h` or `FMT_USE_CONSTEVAL` after updating to Xcode 26.4, see [docs/xcode-26.4-fmt-consteval-workaround.md](docs/xcode-26.4-fmt-consteval-workaround.md) for the root cause and a `post_install` Podfile patch. This is a React Native / Xcode toolchain issue, not specific to this library. + ## Google ML Kit Vision Features Roadmap | # | Feature | Status | Platform | diff --git a/docs/barcode-scanning.md b/docs/barcode-scanning.md index 88f7e97..7e1d5b2 100644 --- a/docs/barcode-scanning.md +++ b/docs/barcode-scanning.md @@ -104,10 +104,10 @@ const result = await processImageBarcodeScanning(imageUri, { Each barcode includes: -- geometry: `bounds`, `corners` -- identity: `format`, `formatName`, `valueType`, `valueTypeName` +- geometry: `bounds`, `corners` (optional — may be absent for potential barcodes) +- identity: `format`, `formatName`, `valueType`, `valueTypeName` — `format`/`valueType` are the raw ML Kit numeric codes; the `*Name` variants are the human-readable string equivalents and are what you should match against in application code - raw payload: `rawValue`, `displayValue`, `rawBytes` -- `isPotential` (true for potential/undecoded barcode candidates) +- `isPotential` (true for potential/undecoded barcode candidates; when `true`, `rawValue`/`rawBytes` may be `null`) - parsed payload: `value?: BarcodeParsedValue` ### Parsed value typing diff --git a/docs/text-recognition.md b/docs/text-recognition.md index a639433..aa76c77 100644 --- a/docs/text-recognition.md +++ b/docs/text-recognition.md @@ -11,6 +11,7 @@ import { type TextRecognitionOptions, type TextRecognitionArguments, type TextRecognitionImageOptions, + type TextRecognitionResult, } from 'react-native-vision-camera-mlkit'; ``` @@ -77,13 +78,21 @@ const result = await processImageTextRecognition(imageUri, { ## Result shape -Text recognition returns: - -- top-level text content -- blocks -- lines -- elements -- geometry (`bounds`, `corners`) with transformed coordinates +`TextRecognitionResult` is a nested hierarchy: text → blocks → lines → elements +(→ symbols on Android). Every level carries its own `bounds`/`corners` +geometry, already mapped back to full source coordinates (see +[Region of Interest](../README.md#region-of-interest)). + +- `text: string` — the full recognized text. +- `blocks: TextBlock[]` + - `text: string`, `bounds`, `corners`, `languages: string[]` (ISO 639-1/639-2) + - `lines: TextLine[]` + - `text: string`, `bounds`, `corners`, `languages: string[]` + - `confidence: number | null`, `angle: number | null` (Android only) + - `elements: TextElement[]` + - `text: string`, `bounds`, `corners`, `languages: string[]` + - `confidence: number | null`, `angle: number | null` (Android only) + - `symbols: TextSymbol[]` (Android only) — `text`, `bounds`, `corners`, `confidence`, `angle` ## iOS orientation note diff --git a/package.json b/package.json index edd8f36..378d7f1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-vision-camera-mlkit", "version": "2.0.0", - "description": "A powerful React Native Vision Camera plugin delivering high-performance Google ML Kit frame processor features—including text recognition (OCR), face detection, barcode scanning, pose detection, and more. Seamlessly bridges native ML Kit capabilities for real-time, on-device computer vision in your React Native apps.", + "description": "A React Native Vision Camera (v5, Nitro-based) plugin for Google ML Kit vision features. Ships text recognition (OCR) and barcode scanning today, for real-time frame processing and static images, with more ML Kit vision features (face detection, pose detection, and others) on the roadmap.", "main": "./lib/module/index.js", "types": "./lib/typescript/src/index.d.ts", "exports": { @@ -61,9 +61,9 @@ "image-processing", "camera", "text-detection", - "face-detection", + "text-recognition", "barcode-scanner", - "pose-detection" + "barcode-scanning" ], "repository": { "type": "git", From 7b9347fc0666b04ef93713e2c7b97042bc9dd24b Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 26 Jul 2026 22:34:34 -0300 Subject: [PATCH 36/45] chore: replace labels.yml with updated shared taxonomy (#18) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: replace labels.yml with updated shared taxonomy Adopt the new personal cross-repo label taxonomy (type/status/priority/ platform/technology/area/impact/pr/contribution), trimmed to what actually applies to this repo: drops backend/web platforms, Java, Objective-C, Flutter, and Dart (Android impl is Kotlin-only, iOS impl is Swift-only, no Flutter/Dart anywhere), and drops analytics, auth, database, design-system, localization, notifications, offline, synchronization, and the networking-flavored "api" area, plus "impact: data loss" — none of these concepts exist in this camera/OCR frame-processor library. Kept accessibility/navigation/ui-ux since the example/ demo app has real navigation, UI components, and accessibility labels. * chore: align workflow and issue-template label references with new taxonomy Update the stale-bot config (stale/exempt/only labels) and the bug report, build error, and feature request issue templates to reference the new label names instead of the old emoji-prefixed ones. GitHub Actions label matching is exact-string, so leftover references to the old names would have silently stopped working once labels.yml changed. --- .github/ISSUE_TEMPLATE/bug-report.yml | 2 +- .github/ISSUE_TEMPLATE/build-error.yml | 2 +- .github/ISSUE_TEMPLATE/feature-request.yml | 2 +- .github/labels.yml | 302 ++++++++++++++------- .github/workflows/stale.yml | 10 +- 5 files changed, 208 insertions(+), 110 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 8bfb66a..fda4334 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -1,7 +1,7 @@ name: 🐛 Bug Report description: File a bug report for react-native-vision-camera-mlkit title: '🐛 Bug: ' -labels: [🐛 bug] +labels: ["type: bug"] body: - type: dropdown attributes: diff --git a/.github/ISSUE_TEMPLATE/build-error.yml b/.github/ISSUE_TEMPLATE/build-error.yml index c4bf8b4..f79f1f4 100644 --- a/.github/ISSUE_TEMPLATE/build-error.yml +++ b/.github/ISSUE_TEMPLATE/build-error.yml @@ -1,7 +1,7 @@ name: 🔧 Build Error description: Report build issues with react-native-vision-camera-mlkit title: '🔧 Build error: ' -labels: [🔧 build error] +labels: ["type: bug", "area: build"] body: - type: dropdown attributes: diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index b473e8a..c8c6a66 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -1,7 +1,7 @@ name: ✨ Feature Request description: Suggest a new ML Kit feature or enhancement title: '✨ Feature Request: ' -labels: [✨ feature] +labels: ["type: feature"] body: - type: dropdown attributes: diff --git a/.github/labels.yml b/.github/labels.yml index a34ed09..b095bcf 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -1,102 +1,200 @@ -- name: 🐛 bug - description: Something isn't working as expected - color: d73a49 -- name: ✨ feature - description: New feature request - color: a2eeef -- name: 🚀 enhancement - description: Enhancement to existing feature - color: 84B6EB -- name: 🔧 build error - description: Build or compilation issues - color: 008672 -- name: 📚 documentation - description: Documentation improvements or additions - color: 0075ca -- name: 💭 question - description: Questions or clarifications needed - color: d876e3 -- name: 🤖 android - description: Android platform specific - color: 4BC74F -- name: 🍏 ios - description: iOS platform specific - color: 3DDC84 -- name: 📝 text-recognition - description: Text Recognition feature - color: fbca04 -- name: 👤 face-detection - description: Face Detection feature - color: f9d0c4 -- name: 🎭 face-mesh-detection - description: Face Mesh Detection feature - color: e99695 -- name: 🏃 pose-detection - description: Pose Detection feature - color: d4c5f9 -- name: ✂️ selfie-segmentation - description: Selfie Segmentation feature - color: bfc1c6 -- name: 📐 subject-segmentation - description: Subject Segmentation feature - color: 8b4513 -- name: 📄 document-scanner - description: Document Scanner feature - color: 2e8b57 -- name: 📱 barcode-scanning - description: Barcode Scanning feature - color: ff69b4 -- name: 🏷️ image-labeling - description: Image Labeling feature - color: 00ced1 -- name: 🔍 object-detection - description: Object Detection feature - color: dc143c -- name: ✍️ digital-ink-recognition - description: Digital Ink Recognition feature - color: 8a2be2 -- name: ❌ needs-reproduction - description: Issue needs reproduction steps - color: FF0000 -- name: ⌛ awaiting-author - description: Waiting for author feedback or requested information - color: FBCA04 -- name: 🖇 duplicate - description: This issue or pull request already exists - color: cfd3d7 -- name: ❌ invalid - description: This doesn't seem right - color: e4e669 -- name: ☠️ wontfix - description: This will not be worked on - color: ffffff -- name: ⏳ stale - description: Issue has not had recent activity - color: 808080 -- name: 🚧 wip - description: Work in progress - color: fbca04 -- name: 🔴 critical - description: Critical issue affecting core functionality - color: b60205 -- name: 🟠 high - description: High priority issue - color: fbca04 -- name: 🟡 medium - description: Medium priority issue - color: fef2c0 -- name: 🟢 low - description: Low priority issue - color: 0e8a16 -- name: ⚠️ breaking change - description: Breaking change that requires migration - color: d93f0b -- name: 🎈 performance - description: Performance related issue or improvement - color: 006b75 -- name: 📦 dependencies - description: Pull requests that update dependencies - color: 0366d6 -- name: 💄 types - description: TypeScript type definitions or issues - color: 1d76db +# ============================================================================= +# Type +# ============================================================================= +- name: "type: bug" + description: "Something is not working as expected" + color: "D73A4A" +- name: "type: feature" + description: "Introduces new functionality" + color: "1D76DB" +- name: "type: enhancement" + description: "Improves existing functionality or behavior" + color: "A2EEEF" +- name: "type: refactor" + description: "Restructures code without intentionally changing behavior" + color: "5319E7" +- name: "type: maintenance" + description: "Routine maintenance, tooling, configuration, or internal project work" + color: "C2E0C6" +- name: "type: documentation" + description: "Adds, updates, or corrects documentation" + color: "0075CA" +- name: "type: test" + description: "Adds, updates, or fixes automated tests" + color: "BFDADC" +- name: "type: cleanup" + description: "Removes unused code or improves project organization" + color: "C5DEF5" +- name: "type: dependency" + description: "Adds, removes, or updates a project dependency" + color: "0366D6" +- name: "type: breaking change" + description: "Introduces an incompatible change that may require migration" + color: "B60205" +- name: "type: deprecation" + description: "Deprecates functionality, APIs, or implementation paths" + color: "8B0000" +- name: "type: question" + description: "Requests clarification, guidance, or additional information" + color: "D876E3" + +# ============================================================================= +# Status +# ============================================================================= +- name: "status: blocked" + description: "Progress is blocked by a dependency, decision, or external factor" + color: "B60205" +- name: "status: needs information" + description: "Additional information is required before work can continue" + color: "D93F0B" +- name: "status: needs reproduction" + description: "Reproduction steps or a minimal reproducible example are required" + color: "D93F0B" +- name: "status: ready" + description: "Ready to be worked on or reviewed" + color: "0E8A16" +- name: "status: in progress" + description: "Work is currently in progress" + color: "FBCA04" +- name: "status: needs review" + description: "Ready for code, product, design, or technical review" + color: "0052CC" +- name: "status: changes requested" + description: "Changes are required before the work can be approved" + color: "D93F0B" +- name: "status: duplicate" + description: "This issue or pull request duplicates existing work" + color: "CFD3D7" +- name: "status: invalid" + description: "This issue or pull request is incomplete, incorrect, or not actionable" + color: "E4E669" +- name: "status: stale" + description: "This issue or pull request has had no recent activity" + color: "EDEDED" +- name: "status: wontfix" + description: "This issue is acknowledged but will not be addressed" + color: "FFFFFF" + +# ============================================================================= +# Priority +# ============================================================================= +- name: "priority: critical" + description: "Requires immediate attention due to severe user or business impact" + color: "B60205" +- name: "priority: high" + description: "High-impact work that should be addressed soon" + color: "D93F0B" +- name: "priority: medium" + description: "Normal-priority work" + color: "FBCA04" +- name: "priority: low" + description: "Low-impact work that can be handled when capacity allows" + color: "0E8A16" + +# ============================================================================= +# Platform +# ============================================================================= +- name: "platform: android" + description: "Specific to Android" + color: "3DDC84" +- name: "platform: ios" + description: "Specific to iOS" + color: "A2AAAD" +- name: "platform: shared" + description: "Affects shared or cross-platform application code" + color: "6F42C1" + +# ============================================================================= +# Technology +# ============================================================================= +- name: "technology: kotlin" + description: "Specific to Kotlin code or tooling" + color: "7F52FF" +- name: "technology: swift" + description: "Specific to Swift code or tooling" + color: "F05138" +- name: "technology: react native" + description: "Specific to React Native" + color: "61DAFB" +- name: "technology: typescript" + description: "Specific to TypeScript code or tooling" + color: "3178C6" + +# ============================================================================= +# Area +# ============================================================================= +- name: "area: accessibility" + description: "Accessibility behavior, support, or compliance" + color: "D4C5F9" +- name: "area: build" + description: "Build configuration, compilation, signing, packaging, or release builds" + color: "006B75" +- name: "area: ci/cd" + description: "Continuous integration, delivery, automation, or deployment pipelines" + color: "0E8A16" +- name: "area: navigation" + description: "Application navigation, routing, deep links, or screen transitions" + color: "84B6EB" +- name: "area: performance" + description: "Startup time, responsiveness, memory, battery, or resource usage" + color: "F9C513" +- name: "area: permissions" + description: "System permissions, privacy prompts, or permission handling" + color: "D4C5F9" +- name: "area: release" + description: "Versioning, publishing, store submission, rollout, or release management" + color: "0E8A16" +- name: "area: security" + description: "Security vulnerabilities, hardening, data protection, or privacy concerns" + color: "B60205" +- name: "area: testing" + description: "Test infrastructure, fixtures, mocks, coverage, or test reliability" + color: "BFDADC" +- name: "area: ui/ux" + description: "User interface, interaction, layout, or user experience" + color: "E99695" + +# ============================================================================= +# Quality and impact +# ============================================================================= +- name: "impact: crash" + description: "Causes or prevents an application crash" + color: "B60205" +- name: "impact: regression" + description: "Previously working behavior has stopped working" + color: "D93F0B" +- name: "impact: user-facing" + description: "Directly changes or affects the user experience" + color: "1D76DB" +- name: "impact: internal" + description: "Primarily affects internal implementation or developer workflows" + color: "C5DEF5" + +# ============================================================================= +# Pull request +# ============================================================================= +- name: "pr: do not merge" + description: "Must not be merged until the blocking condition is resolved" + color: "B60205" +- name: "pr: ready for review" + description: "The pull request is ready for review" + color: "0E8A16" +- name: "pr: work in progress" + description: "The pull request is still under active development" + color: "FBCA04" +- name: "pr: requires migration" + description: "Requires a data, configuration, API, or consumer migration" + color: "D93F0B" +- name: "pr: requires release notes" + description: "Requires an entry in the changelog or release notes" + color: "0075CA" + +# ============================================================================= +# Contribution +# ============================================================================= +- name: "contribution: good first issue" + description: "Suitable for first-time contributors" + color: "7057FF" +- name: "contribution: help wanted" + description: "Additional assistance or community contributions are welcome" + color: "008672" diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index b155a72..00c05e1 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -32,10 +32,10 @@ jobs: close-pr-message: > This PR was closed because it remained inactive for 7 days after being marked stale. - stale-issue-label: ⏳ stale - stale-pr-label: ⏳ stale + stale-issue-label: "status: stale" + stale-pr-label: "status: stale" - only-labels: ⌛ awaiting-author + only-labels: "status: needs information" days-before-stale-issues: 30 days-before-stale-prs: 30 @@ -51,8 +51,8 @@ jobs: close-pr-message-when-labeled: > This PR was closed because the author did not follow up after 7 days. - exempt-issue-labels: ☠️ wontfix, 🔴 critical - exempt-pr-labels: ☠️ wontfix, 🔴 critical + exempt-issue-labels: "status: wontfix, priority: critical" + exempt-pr-labels: "status: wontfix, priority: critical" exempt-all-milestones: true remove-stale-when-updated: true From 5bf818d81254b231fcc69d5c31103d11c936fb91 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 26 Jul 2026 22:42:39 -0300 Subject: [PATCH 37/45] chore: untrack personal AI tooling state from .claude/.codex (#16) * chore: untrack personal AI tooling state from .claude/.codex Audit of .agents/.claude/.codex (added by an ECC auto-setup process), using the same rationale that got .context/ gitignored: keep genuinely shareable repo-specific tooling tracked (skills, slash-command scaffolds, Codex sub-agent role definitions - none of it contains personal data), but untrack files that are personal/machine-specific state: - .claude/identity.json: user's personal AI verbosity/style/domain preferences, not a project fact. - .claude/ecc-tools.json: ECC tool bookkeeping/state manifest with generation timestamps and readiness scores. - .claude/homunculus/instincts/inherited/*.yaml: personal continuous- learning cache, content overlaps with the tracked SKILL.md docs. - .codex/config.toml: personal sandbox/approval policy and MCP server list - the Codex equivalent of .claude/settings.local.json, which is already untracked. Files stay on disk (not deleted), just removed from git's index, so local AI tooling keeps working. .gitignore gains a new section for these patterns, appended alongside (not replacing) the existing .context/ entry. * chore: gitignore the untracked personal AI tooling state files * chore: untrack .agents/ AI tooling scaffolding as well Not personal data (verified byte-identical to the kept .claude/skills/ copy, plus generic tool-discovery metadata) - untracked by preference, to keep the repo free of visible ECC-generated AI-tooling scaffolding regardless of whether it's personal. Files stay on disk. --- .../react-native-vision-camera-mlkit/SKILL.md | 137 ----- .../agents/openai.yaml | 6 - .claude/ecc-tools.json | 286 --------- ...-native-vision-camera-mlkit-instincts.yaml | 548 ------------------ .claude/identity.json | 15 - .codex/config.toml | 44 -- .gitignore | 10 + 7 files changed, 10 insertions(+), 1036 deletions(-) delete mode 100644 .agents/skills/react-native-vision-camera-mlkit/SKILL.md delete mode 100644 .agents/skills/react-native-vision-camera-mlkit/agents/openai.yaml delete mode 100644 .claude/ecc-tools.json delete mode 100644 .claude/homunculus/instincts/inherited/react-native-vision-camera-mlkit-instincts.yaml delete mode 100644 .claude/identity.json delete mode 100644 .codex/config.toml diff --git a/.agents/skills/react-native-vision-camera-mlkit/SKILL.md b/.agents/skills/react-native-vision-camera-mlkit/SKILL.md deleted file mode 100644 index 1721f1f..0000000 --- a/.agents/skills/react-native-vision-camera-mlkit/SKILL.md +++ /dev/null @@ -1,137 +0,0 @@ -```markdown -# react-native-vision-camera-mlkit Development Patterns - -> Auto-generated skill from repository analysis - -## Overview - -This skill teaches you how to contribute to the `react-native-vision-camera-mlkit` repository, a React Native module that integrates ML Kit features (like barcode scanning and text recognition) into mobile camera applications. You'll learn the project's coding conventions, commit patterns, and the main development workflows for adding features, improving code quality, updating iOS dependencies, and maintaining documentation. - ---- - -## Coding Conventions - -**File Naming** -- Use **PascalCase** for file and directory names. - - Example: `VisionCameraMLKitModule.swift`, `PluginRegistry.kt` - -**Import Style** -- Use **relative imports** in TypeScript/JavaScript. - - Example: - ```ts - import { BarcodeScanner } from './BarcodeScanner' - ``` - -**Export Style** -- Use **named exports**. - - Example: - ```ts - export const BarcodeScanner = { ... } - export type Barcode = { ... } - ``` - -**Commit Messages** -- Follow **Conventional Commits**: `fix:`, `feat:`, `chore:`, `docs:`, `perf:` -- Keep commit messages concise (~52 characters on average). - - Example: `feat: add face detection plugin for iOS` - ---- - -## Workflows - -### Add or Update Native ML Kit Feature -**Trigger:** When you want to add or significantly update a native ML Kit feature (e.g., barcode scanning, text recognition). -**Command:** `/add-mlkit-feature` - -1. **Android:** - - Add/update handlers, plugins, models, factories, adapters, serializers, and registry files under `android/src/main/java/com/visioncameramlkit/bridge/`. - - Update `android/build.gradle` if needed. -2. **iOS:** - - Add/update handlers, plugins, models, factories, adapters, serializers, and registry files under `ios/Bridge/`. - - Update `ios/VisionCameraMLKit.mm` and `ios/Infrastructure/Modules/VisionCameraMLKitModule.swift` as needed. -3. **Cross-platform:** - - Update or add TypeScript feature files: `src/features/*/index.ts`, `src/features/*/types.ts`, and `src/index.ts`. -4. **Example App:** - - Integrate the new or updated feature in `example/src/components/views/CameraView.tsx`, `example/src/screens/`, and `example/src/stores/`. - - Update iOS example project files: `example/ios/Podfile.lock`, `example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj`. -5. **Documentation:** - - Update `README.md` and/or create or edit `docs/feature.md` to document the new feature. - -**Example commit:** -```sh -feat: add text recognition support for Android -``` - ---- - -### Code Quality Fixes (Native) -**Trigger:** When you want to improve code quality, readability, or maintainability in native Android (Kotlin) and/or iOS (Swift) code. -**Command:** `/code-quality-native` - -1. Identify code quality issues in Android and/or iOS native code. -2. Refactor or clean up code in relevant files (handlers, plugins, models, modules, etc.). -3. Commit changes with a `fix:` or `chore:` message. - -**Example commit:** -```sh -chore: refactor plugin registry for clarity -``` - ---- - -### Update iOS Pods and Xcode Project -**Trigger:** When you need to update iOS dependencies (Pods) or adapt to new Xcode versions. -**Command:** `/update-ios-pods` - -1. Update `example/ios/Podfile` and/or `Podfile.lock` as needed. -2. Update `example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj` for project configuration changes. -3. Commit with a `chore(xcode):` or similar message. - -**Example commit:** -```sh -chore(xcode): update Podfile for Xcode 15 compatibility -``` - ---- - -### Documentation Update -**Trigger:** When you want to update documentation for new features, fixes, or developer guidance. -**Command:** `/update-docs` - -1. Edit `README.md` or any file under `docs/` to add, clarify, or reorganize information. -2. Commit with a `docs:` message. - -**Example commit:** -```sh -docs: add usage example for barcode scanning -``` - ---- - -## Testing Patterns - -- **Test File Naming:** Test files use the `*.test.*` pattern (e.g., `BarcodeScanner.test.ts`). -- **Testing Framework:** Not explicitly detected; check existing test files for framework usage. -- **Test Placement:** Tests are typically placed alongside the files they test. - -**Example:** -```ts -// BarcodeScanner.test.ts -import { BarcodeScanner } from './BarcodeScanner' - -test('scans a barcode', () => { - // ...test implementation -}) -``` - ---- - -## Commands - -| Command | Purpose | -|----------------------|----------------------------------------------------------------| -| /add-mlkit-feature | Add or update a native ML Kit feature (Android/iOS/TS/docs) | -| /code-quality-native | Refactor or improve code quality in native codebases | -| /update-ios-pods | Update iOS Podfile, Podfile.lock, or Xcode project files | -| /update-docs | Update documentation (README.md, docs/*.md) | -``` \ No newline at end of file diff --git a/.agents/skills/react-native-vision-camera-mlkit/agents/openai.yaml b/.agents/skills/react-native-vision-camera-mlkit/agents/openai.yaml deleted file mode 100644 index 590a783..0000000 --- a/.agents/skills/react-native-vision-camera-mlkit/agents/openai.yaml +++ /dev/null @@ -1,6 +0,0 @@ -interface: - display_name: "React Native Vision Camera Mlkit" - short_description: "Repo-specific patterns and workflows for react-native-vision-camera-mlkit" - default_prompt: "Use the react-native-vision-camera-mlkit repo skill to follow existing architecture, testing, and workflow conventions." -policy: - allow_implicit_invocation: true \ No newline at end of file diff --git a/.claude/ecc-tools.json b/.claude/ecc-tools.json deleted file mode 100644 index 6165044..0000000 --- a/.claude/ecc-tools.json +++ /dev/null @@ -1,286 +0,0 @@ -{ - "version": "1.3", - "schemaVersion": "1.0", - "generatedBy": "ecc-tools", - "generatedAt": "2026-07-05T23:56:54.166Z", - "repo": "https://github.com/pedrol2b/react-native-vision-camera-mlkit", - "referenceSetReadiness": { - "score": 0, - "present": 0, - "total": 7, - "items": [ - { - "id": "deep-analyzer-corpus", - "label": "Deep analyzer corpus", - "status": "missing", - "evidence": [], - "recommendation": "Add analyzer fixture, golden, benchmark, or reference-set files that can catch analyzer regressions." - }, - { - "id": "rag-evaluator", - "label": "RAG/evaluator comparison", - "status": "missing", - "evidence": [], - "recommendation": "Add retrieval or evaluator reference-set comparison fixtures with expected ranking behavior." - }, - { - "id": "pr-salvage", - "label": "PR salvage/review corpus", - "status": "missing", - "evidence": [], - "recommendation": "Add stale-PR, review-thread, reopen-flow, or salvage reference cases for queue cleanup automation." - }, - { - "id": "discussion-triage", - "label": "Discussion triage corpus", - "status": "missing", - "evidence": [], - "recommendation": "Add public discussion triage fixtures, golden cases, or reference sets for informational, answered, and no-response classifications." - }, - { - "id": "harness-compatibility", - "label": "Harness compatibility", - "status": "missing", - "evidence": [], - "recommendation": "Add cross-harness, adapter-compliance, or harness-audit evidence for Claude, Codex, OpenCode, Zed, dmux, and agent surfaces." - }, - { - "id": "security-evidence", - "label": "Security evidence", - "status": "missing", - "evidence": [], - "recommendation": "Attach security evidence such as SBOMs, SARIF, audit reports, or AgentShield evidence packs." - }, - { - "id": "ci-failure-mode", - "label": "CI failure-mode evidence", - "status": "missing", - "evidence": [], - "recommendation": "Add captured CI failure logs, dry-run fixtures, or troubleshooting docs for common workflow failure modes." - } - ] - }, - "profiles": { - "requested": "developer", - "recommended": "developer", - "effective": "developer", - "requestedAlias": "developer", - "recommendedAlias": "developer", - "effectiveAlias": "developer" - }, - "requestedProfile": "developer", - "profile": "developer", - "recommendedProfile": "developer", - "effectiveProfile": "developer", - "tier": "free", - "requestedComponents": [ - "repo-baseline", - "workflow-automation" - ], - "selectedComponents": [ - "repo-baseline", - "workflow-automation" - ], - "requestedAddComponents": [], - "requestedRemoveComponents": [], - "blockedRemovalComponents": [], - "tierFilteredComponents": [], - "requestedRootPackages": [ - "runtime-core", - "workflow-pack" - ], - "selectedRootPackages": [ - "runtime-core", - "workflow-pack" - ], - "requestedPackages": [ - "runtime-core", - "workflow-pack" - ], - "requestedAddPackages": [], - "requestedRemovePackages": [], - "selectedPackages": [ - "runtime-core", - "workflow-pack" - ], - "packages": [ - "runtime-core", - "workflow-pack" - ], - "blockedRemovalPackages": [], - "tierFilteredRootPackages": [], - "tierFilteredPackages": [], - "conflictingPackages": [], - "dependencyGraph": { - "runtime-core": [], - "workflow-pack": [ - "runtime-core" - ] - }, - "resolutionOrder": [ - "runtime-core", - "workflow-pack" - ], - "requestedModules": [ - "runtime-core", - "workflow-pack" - ], - "selectedModules": [ - "runtime-core", - "workflow-pack" - ], - "modules": [ - "runtime-core", - "workflow-pack" - ], - "managedFiles": [ - ".claude/skills/react-native-vision-camera-mlkit/SKILL.md", - ".agents/skills/react-native-vision-camera-mlkit/SKILL.md", - ".agents/skills/react-native-vision-camera-mlkit/agents/openai.yaml", - ".claude/identity.json", - ".codex/config.toml", - ".codex/AGENTS.md", - ".codex/agents/explorer.toml", - ".codex/agents/reviewer.toml", - ".codex/agents/docs-researcher.toml", - ".claude/homunculus/instincts/inherited/react-native-vision-camera-mlkit-instincts.yaml", - ".claude/commands/feature-development.md", - ".claude/commands/add-or-update-native-mlkit-feature.md", - ".claude/commands/code-quality-fixes-native.md" - ], - "packageFiles": { - "runtime-core": [ - ".claude/skills/react-native-vision-camera-mlkit/SKILL.md", - ".agents/skills/react-native-vision-camera-mlkit/SKILL.md", - ".agents/skills/react-native-vision-camera-mlkit/agents/openai.yaml", - ".claude/identity.json", - ".codex/config.toml", - ".codex/AGENTS.md", - ".codex/agents/explorer.toml", - ".codex/agents/reviewer.toml", - ".codex/agents/docs-researcher.toml", - ".claude/homunculus/instincts/inherited/react-native-vision-camera-mlkit-instincts.yaml" - ], - "workflow-pack": [ - ".claude/commands/feature-development.md", - ".claude/commands/add-or-update-native-mlkit-feature.md", - ".claude/commands/code-quality-fixes-native.md" - ] - }, - "moduleFiles": { - "runtime-core": [ - ".claude/skills/react-native-vision-camera-mlkit/SKILL.md", - ".agents/skills/react-native-vision-camera-mlkit/SKILL.md", - ".agents/skills/react-native-vision-camera-mlkit/agents/openai.yaml", - ".claude/identity.json", - ".codex/config.toml", - ".codex/AGENTS.md", - ".codex/agents/explorer.toml", - ".codex/agents/reviewer.toml", - ".codex/agents/docs-researcher.toml", - ".claude/homunculus/instincts/inherited/react-native-vision-camera-mlkit-instincts.yaml" - ], - "workflow-pack": [ - ".claude/commands/feature-development.md", - ".claude/commands/add-or-update-native-mlkit-feature.md", - ".claude/commands/code-quality-fixes-native.md" - ] - }, - "files": [ - { - "moduleId": "runtime-core", - "path": ".claude/skills/react-native-vision-camera-mlkit/SKILL.md", - "description": "Repository-specific Claude Code skill generated from git history." - }, - { - "moduleId": "runtime-core", - "path": ".agents/skills/react-native-vision-camera-mlkit/SKILL.md", - "description": "Codex-facing copy of the generated repository skill." - }, - { - "moduleId": "runtime-core", - "path": ".agents/skills/react-native-vision-camera-mlkit/agents/openai.yaml", - "description": "Codex skill metadata so the repo skill appears cleanly in the skill interface." - }, - { - "moduleId": "runtime-core", - "path": ".claude/identity.json", - "description": "Suggested identity.json baseline derived from repository conventions." - }, - { - "moduleId": "runtime-core", - "path": ".codex/config.toml", - "description": "Repo-local Codex MCP and multi-agent baseline aligned with ECC defaults." - }, - { - "moduleId": "runtime-core", - "path": ".codex/AGENTS.md", - "description": "Codex usage guide that points at the generated repo skill and workflow bundle." - }, - { - "moduleId": "runtime-core", - "path": ".codex/agents/explorer.toml", - "description": "Read-only explorer role config for Codex multi-agent work." - }, - { - "moduleId": "runtime-core", - "path": ".codex/agents/reviewer.toml", - "description": "Read-only reviewer role config focused on correctness and security." - }, - { - "moduleId": "runtime-core", - "path": ".codex/agents/docs-researcher.toml", - "description": "Read-only docs researcher role config for API verification." - }, - { - "moduleId": "runtime-core", - "path": ".claude/homunculus/instincts/inherited/react-native-vision-camera-mlkit-instincts.yaml", - "description": "Continuous-learning instincts derived from repository patterns." - }, - { - "moduleId": "workflow-pack", - "path": ".claude/commands/feature-development.md", - "description": "Workflow command scaffold for feature-development." - }, - { - "moduleId": "workflow-pack", - "path": ".claude/commands/add-or-update-native-mlkit-feature.md", - "description": "Workflow command scaffold for add-or-update-native-mlkit-feature." - }, - { - "moduleId": "workflow-pack", - "path": ".claude/commands/code-quality-fixes-native.md", - "description": "Workflow command scaffold for code-quality-fixes-native." - } - ], - "workflows": [ - { - "command": "feature-development", - "path": ".claude/commands/feature-development.md" - }, - { - "command": "add-or-update-native-mlkit-feature", - "path": ".claude/commands/add-or-update-native-mlkit-feature.md" - }, - { - "command": "code-quality-fixes-native", - "path": ".claude/commands/code-quality-fixes-native.md" - } - ], - "adapters": { - "claudeCode": { - "skillPath": ".claude/skills/react-native-vision-camera-mlkit/SKILL.md", - "identityPath": ".claude/identity.json", - "commandPaths": [ - ".claude/commands/feature-development.md", - ".claude/commands/add-or-update-native-mlkit-feature.md", - ".claude/commands/code-quality-fixes-native.md" - ] - }, - "codex": { - "configPath": ".codex/config.toml", - "agentsGuidePath": ".codex/AGENTS.md", - "skillPath": ".agents/skills/react-native-vision-camera-mlkit/SKILL.md" - } - } -} \ No newline at end of file diff --git a/.claude/homunculus/instincts/inherited/react-native-vision-camera-mlkit-instincts.yaml b/.claude/homunculus/instincts/inherited/react-native-vision-camera-mlkit-instincts.yaml deleted file mode 100644 index b458d58..0000000 --- a/.claude/homunculus/instincts/inherited/react-native-vision-camera-mlkit-instincts.yaml +++ /dev/null @@ -1,548 +0,0 @@ -# Instincts generated from https://github.com/pedrol2b/react-native-vision-camera-mlkit -# Generated: 2026-07-05T23:57:28.965Z -# Version: 2.0 -# NOTE: This file supplements (does not replace) any existing curated instincts. -# High-confidence manually curated instincts should be preserved alongside these. - ---- -id: react-native-vision-camera-mlkit-commit-conventional -trigger: "when writing a commit message" -confidence: 0.85 -domain: git -source: repo-analysis -source_repo: https://github.com/pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Commit Conventional - -## Action - -Use conventional commit format with prefixes: fix, feat, chore, docs, perf - -## Evidence - -- 17 commits analyzed -- Detected conventional commit pattern -- Examples: chore(barcodeScanning): add native deps for barcodeScanning, docs(README): add quote to md line - ---- -id: react-native-vision-camera-mlkit-commit-length -trigger: "when writing a commit message" -confidence: 0.6 -domain: git -source: repo-analysis -source_repo: https://github.com/pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Commit Length - -## Action - -Write moderate-length commit messages (~52 characters) - -## Evidence - -- Average commit message length: 52 chars -- Based on 17 commits - ---- -id: react-native-vision-camera-mlkit-naming-files -trigger: "when creating a new file" -confidence: 0.8 -domain: code-style -source: repo-analysis -source_repo: https://github.com/pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Naming Files - -## Action - -Use PascalCase naming convention - -## Evidence - -- Analyzed file naming patterns in repository -- Dominant pattern: PascalCase - ---- -id: react-native-vision-camera-mlkit-import-relative -trigger: "when importing modules" -confidence: 0.75 -domain: code-style -source: repo-analysis -source_repo: https://github.com/pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Import Relative - -## Action - -Use relative imports for project files - -## Evidence - -- Import analysis shows relative import pattern -- Example: import { x } from '../lib/x' - ---- -id: react-native-vision-camera-mlkit-export-style -trigger: "when exporting from a module" -confidence: 0.7 -domain: code-style -source: repo-analysis -source_repo: https://github.com/pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Export Style - -## Action - -Prefer named exports - -## Evidence - -- Export pattern analysis -- Dominant style: named - ---- -id: react-native-vision-camera-mlkit-arch-type-based -trigger: "when adding new code" -confidence: 0.8 -domain: architecture -source: repo-analysis -source_repo: https://github.com/pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Arch Type Based - -## Action - -Place code in the appropriate type folder (components/, services/, utils/, etc.) - -## Evidence - -- Type-based module organization detected -- Folders: core, features - ---- -id: react-native-vision-camera-mlkit-test-separate -trigger: "when writing tests" -confidence: 0.8 -domain: testing -source: repo-analysis -source_repo: https://github.com/pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Test Separate - -## Action - -Place tests in the tests/ or __tests__/ directory, mirroring src structure - -## Evidence - -- Separate test directory pattern detected -- Tests live in dedicated test folders - ---- -id: react-native-vision-camera-mlkit-workflow-feature-development -trigger: "when implementing a new feature" -confidence: 0.9 -domain: workflow -source: repo-analysis -source_repo: https://github.com/pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Workflow Feature Development - -## Action - -Follow the feature-development workflow: -1. Add feature implementation -2. Add tests for feature -3. Update documentation - -## Evidence - -- Workflow detected from commit patterns -- Frequency: ~14x per month -- Files: example/src/screens/*, example/src/components/ui/*, example/src/stores/* - ---- -id: react-native-vision-camera-mlkit-workflow-add-or-update-native-mlkit-feature -trigger: "when doing add or update native mlkit feature" -confidence: 0.55 -domain: workflow -source: repo-analysis -source_repo: https://github.com/pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Workflow Add Or Update Native Mlkit Feature - -## Action - -Follow the add-or-update-native-mlkit-feature workflow: -1. Add or update native Android files (handlers, plugins, models, factories, adapters, serializers, registry, build.gradle). -2. Add or update native iOS files (handlers, plugins, models, factories, adapters, serializers, registry, VisionCameraMLKit.mm). -3. Update or add cross-platform TypeScript files (feature index, types, src/index.ts). -4. Update example app integration (screens, stores, CameraView, Podfile.lock, project.pbxproj). -5. Update documentation (README.md, docs/feature.md). - -## Evidence - -- Workflow detected from commit patterns -- Frequency: ~1x per month -- Files: android/src/main/java/com/visioncameramlkit/bridge/handlers/*, android/src/main/java/com/visioncameramlkit/bridge/plugins/*, android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt - ---- -id: react-native-vision-camera-mlkit-workflow-code-quality-fixes-native -trigger: "when doing code quality fixes native" -confidence: 0.55 -domain: workflow -source: repo-analysis -source_repo: https://github.com/pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Workflow Code Quality Fixes Native - -## Action - -Follow the code-quality-fixes-native workflow: -1. Identify code quality issues in Android and/or iOS native code. -2. Refactor or clean up code in multiple related files (handlers, plugins, models, modules, etc.). -3. Commit changes with a fix or chore message. - -## Evidence - -- Workflow detected from commit patterns -- Frequency: ~1x per month -- Files: android/src/main/java/com/visioncameramlkit/bridge/handlers/*, android/src/main/java/com/visioncameramlkit/bridge/plugins/*, android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt - ---- -id: react-native-vision-camera-mlkit-workflow-update-ios-pods-and-xcode-project -trigger: "when doing update ios pods and xcode project" -confidence: 0.55 -domain: workflow -source: repo-analysis -source_repo: https://github.com/pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Workflow Update Ios Pods And Xcode Project - -## Action - -Follow the update-ios-pods-and-xcode-project workflow: -1. Update example/ios/Podfile and/or Podfile.lock. -2. Update example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj. -3. Commit with chore(xcode) or similar message. - -## Evidence - -- Workflow detected from commit patterns -- Frequency: ~1x per month -- Files: example/ios/Podfile, example/ios/Podfile.lock, example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj - ---- -id: react-native-vision-camera-mlkit-workflow-documentation-update -trigger: "when doing documentation update" -confidence: 0.6 -domain: workflow -source: repo-analysis -source_repo: https://github.com/pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Workflow Documentation Update - -## Action - -Follow the documentation-update workflow: -1. Edit README.md or docs/*.md to add, clarify, or reorganize information. -2. Commit with docs(...) message. - -## Evidence - -- Workflow detected from commit patterns -- Frequency: ~2x per month -- Files: README.md, docs/*.md - ---- -id: react-native-vision-camera-mlkit-instinct-file-naming -trigger: "When creating or renaming a file in the Swift codebase" -confidence: 0.9 -domain: code-style -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct File Naming - -## Action - -Use PascalCase for file names - -## Evidence - -- Pattern in codeStyle.namingConventions.files -- Observed in ios/Bridge/Handlers/*, ios/Domain/Models/* - ---- -id: react-native-vision-camera-mlkit-instinct-function-naming -trigger: "When defining a function in Swift" -confidence: 0.9 -domain: code-style -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Function Naming - -## Action - -Use camelCase for function names - -## Evidence - -- Pattern in codeStyle.namingConventions.functions -- Observed in ios/Infrastructure/MLKit/* - ---- -id: react-native-vision-camera-mlkit-instinct-class-naming -trigger: "When defining a class in Swift" -confidence: 0.9 -domain: code-style -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Class Naming - -## Action - -Use PascalCase for class names - -## Evidence - -- Pattern in codeStyle.namingConventions.classes -- Observed in ios/Domain/Models/* - ---- -id: react-native-vision-camera-mlkit-instinct-constant-naming -trigger: "When declaring a constant in Swift" -confidence: 0.9 -domain: code-style -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Constant Naming - -## Action - -Use SCREAMING_SNAKE_CASE for constant names - -## Evidence - -- Pattern in codeStyle.namingConventions.constants -- Observed in ios/Infrastructure/MLKit/* - ---- -id: react-native-vision-camera-mlkit-instinct-import-style -trigger: "When importing modules in Swift or TypeScript" -confidence: 0.8 -domain: code-style -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Import Style - -## Action - -Use relative import paths - -## Evidence - -- Pattern in codeStyle.importStyle -- Seen in src/features/*/index.ts - ---- -id: react-native-vision-camera-mlkit-instinct-export-style -trigger: "When exporting modules in TypeScript" -confidence: 0.8 -domain: code-style -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Export Style - -## Action - -Use named exports - -## Evidence - -- Pattern in codeStyle.exportStyle -- Seen in src/features/*/index.ts - ---- -id: react-native-vision-camera-mlkit-instinct-test-location -trigger: "When adding or updating tests" -confidence: 0.8 -domain: testing -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Test Location - -## Action - -Place test files in a separate test directory - -## Evidence - -- Pattern in architecture.folderStructure.testLocation -- Files like **/*.test.* - ---- -id: react-native-vision-camera-mlkit-instinct-test-feature-workflow -trigger: "When implementing a new feature" -confidence: 0.85 -domain: testing -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Test Feature Workflow - -## Action - -Add corresponding tests for the feature - -## Evidence - -- Workflow feature-development includes 'Add tests for feature' -- example/src/screens/*, example/src/components/ui/*, **/*.test.* - ---- -id: react-native-vision-camera-mlkit-instinct-git-commit-format -trigger: "When making a commit" -confidence: 0.9 -domain: git -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Git Commit Format - -## Action - -Use conventional commit format: (): - -## Evidence - -- Pattern in commits.type and commits.prefixes -- Examples like 'feat(example/HomeScreen): improve UI by sorting list by status complete' - ---- -id: react-native-vision-camera-mlkit-instinct-git-commit-length -trigger: "When writing a commit message" -confidence: 0.7 -domain: git -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Git Commit Length - -## Action - -Keep the commit message around 52 characters on average - -## Evidence - -- Pattern in commits.averageLength -- Examples in commit history - ---- -id: react-native-vision-camera-mlkit-instinct-add-mlkit-feature -trigger: "When adding or updating a native ML Kit feature (e.g., barcode scanning, text recognition)" -confidence: 0.95 -domain: workflow -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Add Mlkit Feature - -## Action - -Update native Android/iOS files, cross-platform TypeScript files, example integration, and documentation in a coordinated sequence - -## Evidence - -- Workflow add-or-update-native-mlkit-feature -- Files and commit sequences in workflow and example - ---- -id: react-native-vision-camera-mlkit-instinct-code-quality-native -trigger: "When improving code quality in native Android or iOS code" -confidence: 0.9 -domain: workflow -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Code Quality Native - -## Action - -Refactor or clean up code in related native files and commit with fix or chore message - -## Evidence - -- Workflow code-quality-fixes-native -- Commits like 'fix(android): kotlin code quality fixes' - ---- -id: react-native-vision-camera-mlkit-instinct-update-ios-pods -trigger: "When iOS dependencies or Xcode versions change" -confidence: 0.88 -domain: workflow -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Update Ios Pods - -## Action - -Update Podfile, Podfile.lock, and Xcode project files and commit with chore(xcode) or similar message - -## Evidence - -- Workflow update-ios-pods-and-xcode-project -- Files example/ios/Podfile, Podfile.lock, VisionCameraMLKitExample.xcodeproj/project.pbxproj - ---- -id: react-native-vision-camera-mlkit-instinct-documentation-update -trigger: "When features, fixes, or developer guidance change" -confidence: 0.85 -domain: workflow -source: repo-analysis -source_repo: pedrol2b/react-native-vision-camera-mlkit ---- - -# React Native Vision Camera Mlkit Instinct Documentation Update - -## Action - -Edit README.md or docs/*.md and commit with docs(...) message - -## Evidence - -- Workflow documentation-update -- Commits like 'docs(README): add quote to md line' - diff --git a/.claude/identity.json b/.claude/identity.json deleted file mode 100644 index ffb8f52..0000000 --- a/.claude/identity.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": "2.0", - "technicalLevel": "technical", - "preferredStyle": { - "verbosity": "moderate", - "codeComments": true, - "explanations": true - }, - "domains": [ - "swift", - "react" - ], - "suggestedBy": "ecc-tools-repo-analysis", - "createdAt": "2026-07-05T23:57:28.965Z" -} \ No newline at end of file diff --git a/.codex/config.toml b/.codex/config.toml deleted file mode 100644 index 6140da8..0000000 --- a/.codex/config.toml +++ /dev/null @@ -1,44 +0,0 @@ -#:schema https://developers.openai.com/codex/config-schema.json - -# ECC Tools generated Codex baseline -approval_policy = "on-request" -sandbox_mode = "workspace-write" -web_search = "live" - -[mcp_servers.context7] -command = "npx" -args = ["-y", "@upstash/context7-mcp@latest"] - -[mcp_servers.exa] -url = "https://mcp.exa.ai/mcp" - -[mcp_servers.memory] -command = "npx" -args = ["-y", "@modelcontextprotocol/server-memory"] - -[mcp_servers.playwright] -command = "npx" -args = ["-y", "@playwright/mcp@latest", "--extension"] - -[mcp_servers.sequential-thinking] -command = "npx" -args = ["-y", "@modelcontextprotocol/server-sequential-thinking"] - -[features] -multi_agent = true - -[agents] -max_threads = 6 -max_depth = 1 - -[agents.explorer] -description = "Read-only codebase explorer for gathering evidence before changes are proposed." -config_file = "agents/explorer.toml" - -[agents.reviewer] -description = "PR reviewer focused on correctness, security, and missing tests." -config_file = "agents/reviewer.toml" - -[agents.docs_researcher] -description = "Documentation specialist that verifies APIs, framework behavior, and release notes." -config_file = "agents/docs-researcher.toml" \ No newline at end of file diff --git a/.gitignore b/.gitignore index cb931b7..52f84e5 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,16 @@ jsconfig.json # Local AI development context .context/ +# Personal AI tooling state (machine/user-specific, not shared project tooling) +.claude/settings.local.json +.claude/ecc-tools.json +.claude/identity.json +.claude/homunculus/ +.codex/config.toml + +# ECC-generated AI tooling scaffolding (not personal, kept local by preference) +.agents/ + # Xcode # build/ From aa3cacaaceaa956d2e07102ac2ebc681daff4081 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 26 Jul 2026 22:55:00 -0300 Subject: [PATCH 38/45] chore: untrack all remaining .claude/.codex AI tooling from git Removes the last tracked ECC-generated files (.claude/commands/*.md, .claude/skills/.../SKILL.md, .codex/AGENTS.md, .codex/agents/*.toml) - previously kept on a personal-vs-shareable distinction, but going fully local-only for now regardless of that distinction, matching the .agents/ call. Files stay on disk, just removed from git's index. .gitignore simplified to blanket-ignore .agents/, .claude/, and .codex/ instead of itemized file patterns, since nothing under them is tracked anymore. What AI tooling (if any) should be shared via the git remote is left as an open question to revisit later, not decided by this commit. --- .../add-or-update-native-mlkit-feature.md | 42 ------ .claude/commands/code-quality-fixes-native.md | 40 ----- .claude/commands/feature-development.md | 38 ----- .../react-native-vision-camera-mlkit/SKILL.md | 137 ------------------ .codex/AGENTS.md | 28 ---- .codex/agents/docs-researcher.toml | 9 -- .codex/agents/explorer.toml | 9 -- .codex/agents/reviewer.toml | 9 -- 8 files changed, 312 deletions(-) delete mode 100644 .claude/commands/add-or-update-native-mlkit-feature.md delete mode 100644 .claude/commands/code-quality-fixes-native.md delete mode 100644 .claude/commands/feature-development.md delete mode 100644 .claude/skills/react-native-vision-camera-mlkit/SKILL.md delete mode 100644 .codex/AGENTS.md delete mode 100644 .codex/agents/docs-researcher.toml delete mode 100644 .codex/agents/explorer.toml delete mode 100644 .codex/agents/reviewer.toml diff --git a/.claude/commands/add-or-update-native-mlkit-feature.md b/.claude/commands/add-or-update-native-mlkit-feature.md deleted file mode 100644 index 1ae3d8b..0000000 --- a/.claude/commands/add-or-update-native-mlkit-feature.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -name: add-or-update-native-mlkit-feature -description: Workflow command scaffold for add-or-update-native-mlkit-feature in react-native-vision-camera-mlkit. -allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"] ---- - -# /add-or-update-native-mlkit-feature - -Use this workflow when working on **add-or-update-native-mlkit-feature** in `react-native-vision-camera-mlkit`. - -## Goal - -Adds a new ML Kit feature (e.g., barcode scanning, text recognition) or updates an existing one, including native Android/iOS code, plugin registry, and example integration. - -## Common Files - -- `android/src/main/java/com/visioncameramlkit/bridge/handlers/*` -- `android/src/main/java/com/visioncameramlkit/bridge/plugins/*` -- `android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt` -- `android/src/main/java/com/visioncameramlkit/domain/models/*` -- `android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/*` -- `android/build.gradle` - -## Suggested Sequence - -1. Understand the current state and failure mode before editing. -2. Make the smallest coherent change that satisfies the workflow goal. -3. Run the most relevant verification for touched files. -4. Summarize what changed and what still needs review. - -## Typical Commit Signals - -- Add or update native Android files (handlers, plugins, models, factories, adapters, serializers, registry, build.gradle). -- Add or update native iOS files (handlers, plugins, models, factories, adapters, serializers, registry, VisionCameraMLKit.mm). -- Update or add cross-platform TypeScript files (feature index, types, src/index.ts). -- Update example app integration (screens, stores, CameraView, Podfile.lock, project.pbxproj). -- Update documentation (README.md, docs/feature.md). - -## Notes - -- Treat this as a scaffold, not a hard-coded script. -- Update the command if the workflow evolves materially. \ No newline at end of file diff --git a/.claude/commands/code-quality-fixes-native.md b/.claude/commands/code-quality-fixes-native.md deleted file mode 100644 index 93e985c..0000000 --- a/.claude/commands/code-quality-fixes-native.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: code-quality-fixes-native -description: Workflow command scaffold for code-quality-fixes-native in react-native-vision-camera-mlkit. -allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"] ---- - -# /code-quality-fixes-native - -Use this workflow when working on **code-quality-fixes-native** in `react-native-vision-camera-mlkit`. - -## Goal - -Performs code quality improvements or refactoring for native Android (Kotlin) and iOS (Swift) codebases, often in tandem. - -## Common Files - -- `android/src/main/java/com/visioncameramlkit/bridge/handlers/*` -- `android/src/main/java/com/visioncameramlkit/bridge/plugins/*` -- `android/src/main/java/com/visioncameramlkit/bridge/registry/PluginRegistry.kt` -- `android/src/main/java/com/visioncameramlkit/domain/models/*` -- `android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt` -- `ios/Bridge/Handlers/*` - -## Suggested Sequence - -1. Understand the current state and failure mode before editing. -2. Make the smallest coherent change that satisfies the workflow goal. -3. Run the most relevant verification for touched files. -4. Summarize what changed and what still needs review. - -## Typical Commit Signals - -- Identify code quality issues in Android and/or iOS native code. -- Refactor or clean up code in multiple related files (handlers, plugins, models, modules, etc.). -- Commit changes with a fix or chore message. - -## Notes - -- Treat this as a scaffold, not a hard-coded script. -- Update the command if the workflow evolves materially. \ No newline at end of file diff --git a/.claude/commands/feature-development.md b/.claude/commands/feature-development.md deleted file mode 100644 index fad2168..0000000 --- a/.claude/commands/feature-development.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: feature-development -description: Workflow command scaffold for feature-development in react-native-vision-camera-mlkit. -allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"] ---- - -# /feature-development - -Use this workflow when working on **feature-development** in `react-native-vision-camera-mlkit`. - -## Goal - -Standard feature implementation workflow - -## Common Files - -- `example/src/screens/*` -- `example/src/components/ui/*` -- `example/src/stores/*` -- `**/*.test.*` - -## Suggested Sequence - -1. Understand the current state and failure mode before editing. -2. Make the smallest coherent change that satisfies the workflow goal. -3. Run the most relevant verification for touched files. -4. Summarize what changed and what still needs review. - -## Typical Commit Signals - -- Add feature implementation -- Add tests for feature -- Update documentation - -## Notes - -- Treat this as a scaffold, not a hard-coded script. -- Update the command if the workflow evolves materially. \ No newline at end of file diff --git a/.claude/skills/react-native-vision-camera-mlkit/SKILL.md b/.claude/skills/react-native-vision-camera-mlkit/SKILL.md deleted file mode 100644 index 1721f1f..0000000 --- a/.claude/skills/react-native-vision-camera-mlkit/SKILL.md +++ /dev/null @@ -1,137 +0,0 @@ -```markdown -# react-native-vision-camera-mlkit Development Patterns - -> Auto-generated skill from repository analysis - -## Overview - -This skill teaches you how to contribute to the `react-native-vision-camera-mlkit` repository, a React Native module that integrates ML Kit features (like barcode scanning and text recognition) into mobile camera applications. You'll learn the project's coding conventions, commit patterns, and the main development workflows for adding features, improving code quality, updating iOS dependencies, and maintaining documentation. - ---- - -## Coding Conventions - -**File Naming** -- Use **PascalCase** for file and directory names. - - Example: `VisionCameraMLKitModule.swift`, `PluginRegistry.kt` - -**Import Style** -- Use **relative imports** in TypeScript/JavaScript. - - Example: - ```ts - import { BarcodeScanner } from './BarcodeScanner' - ``` - -**Export Style** -- Use **named exports**. - - Example: - ```ts - export const BarcodeScanner = { ... } - export type Barcode = { ... } - ``` - -**Commit Messages** -- Follow **Conventional Commits**: `fix:`, `feat:`, `chore:`, `docs:`, `perf:` -- Keep commit messages concise (~52 characters on average). - - Example: `feat: add face detection plugin for iOS` - ---- - -## Workflows - -### Add or Update Native ML Kit Feature -**Trigger:** When you want to add or significantly update a native ML Kit feature (e.g., barcode scanning, text recognition). -**Command:** `/add-mlkit-feature` - -1. **Android:** - - Add/update handlers, plugins, models, factories, adapters, serializers, and registry files under `android/src/main/java/com/visioncameramlkit/bridge/`. - - Update `android/build.gradle` if needed. -2. **iOS:** - - Add/update handlers, plugins, models, factories, adapters, serializers, and registry files under `ios/Bridge/`. - - Update `ios/VisionCameraMLKit.mm` and `ios/Infrastructure/Modules/VisionCameraMLKitModule.swift` as needed. -3. **Cross-platform:** - - Update or add TypeScript feature files: `src/features/*/index.ts`, `src/features/*/types.ts`, and `src/index.ts`. -4. **Example App:** - - Integrate the new or updated feature in `example/src/components/views/CameraView.tsx`, `example/src/screens/`, and `example/src/stores/`. - - Update iOS example project files: `example/ios/Podfile.lock`, `example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj`. -5. **Documentation:** - - Update `README.md` and/or create or edit `docs/feature.md` to document the new feature. - -**Example commit:** -```sh -feat: add text recognition support for Android -``` - ---- - -### Code Quality Fixes (Native) -**Trigger:** When you want to improve code quality, readability, or maintainability in native Android (Kotlin) and/or iOS (Swift) code. -**Command:** `/code-quality-native` - -1. Identify code quality issues in Android and/or iOS native code. -2. Refactor or clean up code in relevant files (handlers, plugins, models, modules, etc.). -3. Commit changes with a `fix:` or `chore:` message. - -**Example commit:** -```sh -chore: refactor plugin registry for clarity -``` - ---- - -### Update iOS Pods and Xcode Project -**Trigger:** When you need to update iOS dependencies (Pods) or adapt to new Xcode versions. -**Command:** `/update-ios-pods` - -1. Update `example/ios/Podfile` and/or `Podfile.lock` as needed. -2. Update `example/ios/VisionCameraMLKitExample.xcodeproj/project.pbxproj` for project configuration changes. -3. Commit with a `chore(xcode):` or similar message. - -**Example commit:** -```sh -chore(xcode): update Podfile for Xcode 15 compatibility -``` - ---- - -### Documentation Update -**Trigger:** When you want to update documentation for new features, fixes, or developer guidance. -**Command:** `/update-docs` - -1. Edit `README.md` or any file under `docs/` to add, clarify, or reorganize information. -2. Commit with a `docs:` message. - -**Example commit:** -```sh -docs: add usage example for barcode scanning -``` - ---- - -## Testing Patterns - -- **Test File Naming:** Test files use the `*.test.*` pattern (e.g., `BarcodeScanner.test.ts`). -- **Testing Framework:** Not explicitly detected; check existing test files for framework usage. -- **Test Placement:** Tests are typically placed alongside the files they test. - -**Example:** -```ts -// BarcodeScanner.test.ts -import { BarcodeScanner } from './BarcodeScanner' - -test('scans a barcode', () => { - // ...test implementation -}) -``` - ---- - -## Commands - -| Command | Purpose | -|----------------------|----------------------------------------------------------------| -| /add-mlkit-feature | Add or update a native ML Kit feature (Android/iOS/TS/docs) | -| /code-quality-native | Refactor or improve code quality in native codebases | -| /update-ios-pods | Update iOS Podfile, Podfile.lock, or Xcode project files | -| /update-docs | Update documentation (README.md, docs/*.md) | -``` \ No newline at end of file diff --git a/.codex/AGENTS.md b/.codex/AGENTS.md deleted file mode 100644 index 0ddc597..0000000 --- a/.codex/AGENTS.md +++ /dev/null @@ -1,28 +0,0 @@ -# ECC for Codex CLI - -This supplements the root `AGENTS.md` with a repo-local ECC baseline. - -## Repo Skill - -- Repo-generated Codex skill: `.agents/skills/react-native-vision-camera-mlkit/SKILL.md` -- Claude-facing companion skill: `.claude/skills/react-native-vision-camera-mlkit/SKILL.md` -- Keep user-specific credentials and private MCPs in `~/.codex/config.toml`, not in this repo. - -## MCP Baseline - -Treat `.codex/config.toml` as the default ECC-safe baseline for work in this repository. -The generated baseline enables GitHub, Context7, Exa, Memory, Playwright, and Sequential Thinking. - -## Multi-Agent Support - -- Explorer: read-only evidence gathering -- Reviewer: correctness, security, and regression review -- Docs researcher: API and release-note verification - -## Workflow Files - -- `.claude/commands/feature-development.md` -- `.claude/commands/add-or-update-native-mlkit-feature.md` -- `.claude/commands/code-quality-fixes-native.md` - -Use these workflow files as reusable task scaffolds when the detected repository workflows recur. \ No newline at end of file diff --git a/.codex/agents/docs-researcher.toml b/.codex/agents/docs-researcher.toml deleted file mode 100644 index 0daae57..0000000 --- a/.codex/agents/docs-researcher.toml +++ /dev/null @@ -1,9 +0,0 @@ -model = "gpt-5.4" -model_reasoning_effort = "medium" -sandbox_mode = "read-only" - -developer_instructions = """ -Verify APIs, framework behavior, and release-note claims against primary documentation before changes land. -Cite the exact docs or file paths that support each claim. -Do not invent undocumented behavior. -""" \ No newline at end of file diff --git a/.codex/agents/explorer.toml b/.codex/agents/explorer.toml deleted file mode 100644 index 732df7a..0000000 --- a/.codex/agents/explorer.toml +++ /dev/null @@ -1,9 +0,0 @@ -model = "gpt-5.4" -model_reasoning_effort = "medium" -sandbox_mode = "read-only" - -developer_instructions = """ -Stay in exploration mode. -Trace the real execution path, cite files and symbols, and avoid proposing fixes unless the parent agent asks for them. -Prefer targeted search and file reads over broad scans. -""" \ No newline at end of file diff --git a/.codex/agents/reviewer.toml b/.codex/agents/reviewer.toml deleted file mode 100644 index b13ed9c..0000000 --- a/.codex/agents/reviewer.toml +++ /dev/null @@ -1,9 +0,0 @@ -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" - -developer_instructions = """ -Review like an owner. -Prioritize correctness, security, behavioral regressions, and missing tests. -Lead with concrete findings and avoid style-only feedback unless it hides a real bug. -""" \ No newline at end of file From 1eacf62b388d20bff5df42e13550e832ec3cb6a4 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 26 Jul 2026 22:55:19 -0300 Subject: [PATCH 39/45] chore: gitignore the newly untracked .claude/.codex files --- .gitignore | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 52f84e5..7b8793a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,15 +12,11 @@ jsconfig.json # Local AI development context .context/ -# Personal AI tooling state (machine/user-specific, not shared project tooling) -.claude/settings.local.json -.claude/ecc-tools.json -.claude/identity.json -.claude/homunculus/ -.codex/config.toml - -# ECC-generated AI tooling scaffolding (not personal, kept local by preference) +# AI tooling (local-only for now; revisit what, if anything, should be +# deployed to the git remote) .agents/ +.claude/ +.codex/ # Xcode # From 328569c2950429b9480781f83b08a88daa87d326 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 26 Jul 2026 23:18:26 -0300 Subject: [PATCH 40/45] feat: add Expo config plugin for selective ML Kit dependencies (#19) Delivers Phase 4 of the roadmap: an app.plugin.js so Expo prebuild users don't have to hand-edit android/build.gradle / ios/Podfile to selectively enable ML Kit models, mirroring the existing manual instructions in the README exactly (same flag names, same defaults left untouched for any key the consumer omits). - plugin/mlkitConfig.js - the 16 documented feature keys, fail-fast validation (throws on unknown keys instead of silently ignoring typos, matching this repo's validateRegionOfInterest.ts precedent). - plugin/withAndroid.js - prepends the `ext["react-native-vision-camera-mlkit"]` Groovy block to the project-level android/build.gradle via withProjectBuildGradle; throws on Kotlin DSL (.kts), which the native config doesn't support either. - plugin/withIOS.js - inserts the `$VisionCameraMLKit` Ruby hash before the first `target` block in ios/Podfile via withPodfile, dropping the three Android-only keys (faceMeshDetection, subjectSegmentation, documentScanner) that don't exist in the podspec's config. - Both mods are idempotent (marker-checked, not just createRunOncePlugin's own dedup, since a real `expo prebuild` invocation builds a fresh config object each run) - verified end-to-end against real files via compileModsAsync, not just the pure string-transform unit tests. @expo/config-plugins added as an optional peer dependency (peerDependenciesMeta) - Expo projects already have it transitively via `expo` itself; non-Expo consumers never need it, matching this session's earlier peer-dependency cleanup (#15). README: new "Expo (config plugin)" subsection under ML Kit Models Installation, and the old blanket "follow the Vision Camera guide" note now clarifies that's for Vision Camera's own Expo setup, not this plugin. --- README.md | 35 ++++- app.plugin.js | 1 + package.json | 10 ++ plugin/__tests__/mlkitConfig.test.js | 39 +++++ plugin/__tests__/withAndroid.test.js | 54 +++++++ plugin/__tests__/withIOS.test.js | 43 ++++++ plugin/index.js | 22 +++ plugin/mlkitConfig.js | 57 +++++++ plugin/withAndroid.js | 54 +++++++ plugin/withIOS.js | 60 ++++++++ yarn.lock | 215 ++++++++++++++++++++++++++- 11 files changed, 586 insertions(+), 4 deletions(-) create mode 100644 app.plugin.js create mode 100644 plugin/__tests__/mlkitConfig.test.js create mode 100644 plugin/__tests__/withAndroid.test.js create mode 100644 plugin/__tests__/withIOS.test.js create mode 100644 plugin/index.js create mode 100644 plugin/mlkitConfig.js create mode 100644 plugin/withAndroid.js create mode 100644 plugin/withIOS.js diff --git a/README.md b/README.md index 59aa9a6..f6881d6 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,10 @@ The **static-image APIs** (`processImageTextRecognition`, `processImageBarcodeScanning`) don't use frame processors and don't need Vision Camera, Worklets, or a camera at all. -> For Expo, follow the Vision Camera guide: [visioncamera.margelo.com/docs](https://visioncamera.margelo.com/docs) +> For Expo, follow the Vision Camera guide for camera permissions/setup: +> [visioncamera.margelo.com/docs](https://visioncamera.margelo.com/docs). +> This library's own Expo config plugin (selective ML Kit dependencies) is +> documented below. ## Installation @@ -124,6 +127,36 @@ $VisionCameraMLKit = { Android-only keys: `faceMeshDetection`, `subjectSegmentation`, `documentScanner`. +### Expo (config plugin) + +If you're using an Expo prebuild workflow (`expo prebuild`, EAS Build, or a +custom dev client), a config plugin mirrors the same selective ML Kit flags +into the generated `android/build.gradle` and `ios/Podfile` automatically, so +you don't have to hand-edit native config. Add it to your `app.json`/ +`app.config.js` `plugins` array with the same keys as above: + +```json +{ + "expo": { + "plugins": [ + [ + "react-native-vision-camera-mlkit", + { + "textRecognition": true, + "barcodeScanning": true, + "faceDetection": false + } + ] + ] + } +} +``` + +Any key you omit keeps the library's own default (see the Android/iOS +sections above). Passing an unrecognized key throws at prebuild time instead +of silently being ignored. Requires `@expo/config-plugins` (already a +dependency of `expo` itself, so Expo projects have it for free). + ## Usage ### API Docs diff --git a/app.plugin.js b/app.plugin.js new file mode 100644 index 0000000..7590f2e --- /dev/null +++ b/app.plugin.js @@ -0,0 +1 @@ +module.exports = require('./plugin'); diff --git a/package.json b/package.json index 378d7f1..e0def84 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "types": "./lib/typescript/src/index.d.ts", "default": "./lib/module/index.js" }, + "./app.plugin.js": "./app.plugin.js", "./package.json": "./package.json" }, "files": [ @@ -21,6 +22,8 @@ "nitrogen", "nitro.json", "*.podspec", + "app.plugin.js", + "plugin", "react-native.config.js", "!ios/build", "!android/build", @@ -84,6 +87,7 @@ "@eslint/compat": "^1.3.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.35.0", + "@expo/config-plugins": "57.0.6", "@react-native-community/cli": "20.1.0", "@react-native/babel-preset": "0.86.0", "@react-native/eslint-config": "^0.86.0", @@ -110,11 +114,17 @@ "typescript": "^5.9.2" }, "peerDependencies": { + "@expo/config-plugins": "^57.0.0", "react": "*", "react-native": "*", "react-native-nitro-modules": "*", "react-native-vision-camera": ">=5" }, + "peerDependenciesMeta": { + "@expo/config-plugins": { + "optional": true + } + }, "workspaces": [ "example" ], diff --git a/plugin/__tests__/mlkitConfig.test.js b/plugin/__tests__/mlkitConfig.test.js new file mode 100644 index 0000000..50043b9 --- /dev/null +++ b/plugin/__tests__/mlkitConfig.test.js @@ -0,0 +1,39 @@ +const { parseMLKitConfig, MLKIT_FEATURE_KEYS } = require('../mlkitConfig'); + +describe('parseMLKitConfig', () => { + test('keeps only recognized keys, coerced to booleans', () => { + const result = parseMLKitConfig({ + textRecognition: true, + barcodeScanning: 1, + faceDetection: false, + }); + + expect(result).toEqual({ + textRecognition: true, + barcodeScanning: true, + faceDetection: false, + }); + }); + + test('omits keys the caller did not set', () => { + const result = parseMLKitConfig({ textRecognition: true }); + expect(Object.keys(result)).toEqual(['textRecognition']); + }); + + test('defaults to an empty config when no props are given', () => { + expect(parseMLKitConfig()).toEqual({}); + }); + + test('throws on unknown option names', () => { + expect(() => parseMLKitConfig({ faceDeteciton: true })).toThrow( + /unknown option\(s\) "faceDeteciton"/ + ); + }); + + test('every documented feature key round-trips', () => { + const allTrue = Object.fromEntries( + MLKIT_FEATURE_KEYS.map((k) => [k, true]) + ); + expect(parseMLKitConfig(allTrue)).toEqual(allTrue); + }); +}); diff --git a/plugin/__tests__/withAndroid.test.js b/plugin/__tests__/withAndroid.test.js new file mode 100644 index 0000000..3048a3c --- /dev/null +++ b/plugin/__tests__/withAndroid.test.js @@ -0,0 +1,54 @@ +const { insertAndroidConfig } = require('../withAndroid'); + +const BASE_GRADLE = `buildscript {\n ext {\n kotlinVersion = "1.9.0"\n }\n}\n\napply plugin: "com.android.application"\n`; + +describe('insertAndroidConfig', () => { + test('prepends the ext block with the given config', () => { + const result = insertAndroidConfig( + BASE_GRADLE, + { textRecognition: true, faceDetection: false }, + { language: 'groovy' } + ); + + expect(result).toContain('ext["react-native-vision-camera-mlkit"] = ['); + expect(result).toContain('textRecognition: true,'); + expect(result).toContain('faceDetection: false,'); + expect( + result.indexOf('ext["react-native-vision-camera-mlkit"]') + ).toBeLessThan(result.indexOf('buildscript')); + }); + + test('is idempotent - running twice does not duplicate the block', () => { + const once = insertAndroidConfig( + BASE_GRADLE, + { textRecognition: true }, + { + language: 'groovy', + } + ); + const twice = insertAndroidConfig( + once, + { textRecognition: true }, + { + language: 'groovy', + } + ); + + expect(twice).toBe(once); + expect( + once.split( + 'react-native-vision-camera-mlkit selective ML Kit dependencies' + ).length - 1 + ).toBe(1); + }); + + test('throws for Kotlin DSL build.gradle.kts', () => { + expect(() => + insertAndroidConfig( + BASE_GRADLE, + { textRecognition: true }, + { language: 'kt' } + ) + ).toThrow(/Kotlin DSL/); + }); +}); diff --git a/plugin/__tests__/withIOS.test.js b/plugin/__tests__/withIOS.test.js new file mode 100644 index 0000000..00be0cf --- /dev/null +++ b/plugin/__tests__/withIOS.test.js @@ -0,0 +1,43 @@ +const { insertIOSConfig } = require('../withIOS'); + +const BASE_PODFILE = `require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking")\n\nplatform :ios, '15.1'\n\ntarget 'ExampleApp' do\n use_expo_modules!\nend\n`; + +describe('insertIOSConfig', () => { + test('inserts the hash before the target block, dropping Android-only keys', () => { + const result = insertIOSConfig(BASE_PODFILE, { + textRecognition: true, + faceMeshDetection: true, + subjectSegmentation: true, + documentScanner: true, + barcodeScanning: false, + }); + + expect(result).toContain('$VisionCameraMLKit = {'); + expect(result).toContain("'textRecognition' => true,"); + expect(result).toContain("'barcodeScanning' => false,"); + expect(result).not.toContain('faceMeshDetection'); + expect(result).not.toContain('subjectSegmentation'); + expect(result).not.toContain('documentScanner'); + expect(result.indexOf('$VisionCameraMLKit')).toBeLessThan( + result.indexOf("target 'ExampleApp'") + ); + }); + + test('is idempotent - running twice does not duplicate the block', () => { + const once = insertIOSConfig(BASE_PODFILE, { textRecognition: true }); + const twice = insertIOSConfig(once, { textRecognition: true }); + + expect(twice).toBe(once); + expect( + once.split( + 'react-native-vision-camera-mlkit selective ML Kit dependencies' + ).length - 1 + ).toBe(1); + }); + + test('throws when no target block is found', () => { + expect(() => insertIOSConfig("platform :ios, '15.1'\n", {})).toThrow( + /could not find a `target` block/ + ); + }); +}); diff --git a/plugin/index.js b/plugin/index.js new file mode 100644 index 0000000..2e80c96 --- /dev/null +++ b/plugin/index.js @@ -0,0 +1,22 @@ +const { createRunOncePlugin } = require('@expo/config-plugins'); +const { name, version } = require('../package.json'); +const { parseMLKitConfig } = require('./mlkitConfig'); +const { withVisionCameraMLKitAndroid } = require('./withAndroid'); +const { withVisionCameraMLKitIOS } = require('./withIOS'); + +/** + * Expo config plugin mirroring this library's selective ML Kit dependency + * flags (see README "ML Kit Models Installation (Selective)") into the + * generated `android/build.gradle` and `ios/Podfile` during `expo prebuild`, + * so Expo users don't have to hand-edit native config. + * @throws {Error} If `props` contains an option outside the documented + * ML Kit feature flags. + */ +const withVisionCameraMLKit = (config, props = {}) => { + const mlkitConfig = parseMLKitConfig(props); + config = withVisionCameraMLKitAndroid(config, mlkitConfig); + config = withVisionCameraMLKitIOS(config, mlkitConfig); + return config; +}; + +module.exports = createRunOncePlugin(withVisionCameraMLKit, name, version); diff --git a/plugin/mlkitConfig.js b/plugin/mlkitConfig.js new file mode 100644 index 0000000..e3b130f --- /dev/null +++ b/plugin/mlkitConfig.js @@ -0,0 +1,57 @@ +/** + * Shared feature-flag list for the selective ML Kit dependency config, + * mirroring the Android `rootProject.ext["react-native-vision-camera-mlkit"]` + * and iOS `$VisionCameraMLKit` keys documented in the README. + */ +const MLKIT_FEATURE_KEYS = [ + 'textRecognition', + 'textRecognitionChinese', + 'textRecognitionDevanagari', + 'textRecognitionJapanese', + 'textRecognitionKorean', + 'faceDetection', + 'faceMeshDetection', + 'poseDetection', + 'poseDetectionAccurate', + 'selfieSegmentation', + 'subjectSegmentation', + 'documentScanner', + 'barcodeScanning', + 'imageLabeling', + 'objectDetection', + 'digitalInkRecognition', +]; + +/** Keys with no iOS ML Kit equivalent - dropped before writing the Podfile hash. */ +const ANDROID_ONLY_KEYS = new Set([ + 'faceMeshDetection', + 'subjectSegmentation', + 'documentScanner', +]); + +/** + * Validates and normalizes the plugin's `props` into a flat `{ key: boolean }` + * map, throwing on unknown keys instead of silently ignoring typos. + * @throws {Error} If `props` contains a key outside {@link MLKIT_FEATURE_KEYS}. + */ +const parseMLKitConfig = (props = {}) => { + const unknownKeys = Object.keys(props).filter( + (key) => !MLKIT_FEATURE_KEYS.includes(key) + ); + + if (unknownKeys.length > 0) { + throw new Error( + `react-native-vision-camera-mlkit config plugin: unknown option(s) ${unknownKeys + .map((key) => `"${key}"`) + .join(', ')}. Valid options: ${MLKIT_FEATURE_KEYS.join(', ')}.` + ); + } + + const config = {}; + for (const key of MLKIT_FEATURE_KEYS) { + if (props[key] !== undefined) config[key] = Boolean(props[key]); + } + return config; +}; + +module.exports = { MLKIT_FEATURE_KEYS, ANDROID_ONLY_KEYS, parseMLKitConfig }; diff --git a/plugin/withAndroid.js b/plugin/withAndroid.js new file mode 100644 index 0000000..bb2ccf5 --- /dev/null +++ b/plugin/withAndroid.js @@ -0,0 +1,54 @@ +const { withProjectBuildGradle } = require('@expo/config-plugins'); + +const MARKER = + '// react-native-vision-camera-mlkit selective ML Kit dependencies'; + +/** + * Renders `mlkitConfig` as the Groovy block consumers are otherwise told to + * hand-write at the top of `android/build.gradle` (see README "ML Kit Models + * Installation (Selective)"). + */ +const toGroovyBlock = (mlkitConfig) => { + const entries = Object.entries(mlkitConfig) + .map(([key, value]) => ` ${key}: ${value},`) + .join('\n'); + + return `${MARKER}\next["react-native-vision-camera-mlkit"] = [\n mlkit: [\n${entries}\n ]\n]\n`; +}; + +/** + * Prepends the selective ML Kit dependency block to `contents` (the + * project-level `android/build.gradle` source), unless already present. + * Pure string transform, kept separate from the Expo mod plumbing so it can + * be unit tested directly. + * @throws {Error} If `language` isn't Groovy (`.kts` is not supported by this + * plugin, matching the library's own native config). + */ +const insertAndroidConfig = (contents, mlkitConfig, { language }) => { + if (language !== 'groovy') { + throw new Error( + 'react-native-vision-camera-mlkit config plugin only supports Groovy android/build.gradle files, not Kotlin DSL (.kts).' + ); + } + + if (contents.includes(MARKER)) return contents; + + return `${toGroovyBlock(mlkitConfig)}\n${contents}`; +}; + +/** + * Applies {@link insertAndroidConfig} to the project-level + * `android/build.gradle`, mirroring what `getMLKitConfig()` in the library's + * own `android/build.gradle` reads via `rootProject.ext`. + */ +const withVisionCameraMLKitAndroid = (config, mlkitConfig) => + withProjectBuildGradle(config, (modConfig) => { + modConfig.modResults.contents = insertAndroidConfig( + modConfig.modResults.contents, + mlkitConfig, + { language: modConfig.modResults.language } + ); + return modConfig; + }); + +module.exports = { withVisionCameraMLKitAndroid, insertAndroidConfig }; diff --git a/plugin/withIOS.js b/plugin/withIOS.js new file mode 100644 index 0000000..c5ef374 --- /dev/null +++ b/plugin/withIOS.js @@ -0,0 +1,60 @@ +const { withPodfile } = require('@expo/config-plugins'); +const { ANDROID_ONLY_KEYS } = require('./mlkitConfig'); + +const MARKER = + '# react-native-vision-camera-mlkit selective ML Kit dependencies'; + +/** + * Renders the iOS subset of `mlkitConfig` (Android-only keys dropped) as the + * Ruby hash consumers are otherwise told to hand-write before `target` in + * `ios/Podfile` (see README "ML Kit Models Installation (Selective)"). + */ +const toRubyBlock = (mlkitConfig) => { + const entries = Object.entries(mlkitConfig) + .filter(([key]) => !ANDROID_ONLY_KEYS.has(key)) + .map(([key, value]) => ` '${key}' => ${value},`) + .join('\n'); + + return `${MARKER}\n$VisionCameraMLKit = {\n${entries}\n}\n`; +}; + +/** + * Inserts the selective ML Kit dependency hash into `contents` (the + * `ios/Podfile` source) before the first `target` block, unless already + * present. Pure string transform, kept separate from the Expo mod plumbing + * so it can be unit tested directly. + * @throws {Error} If no `target` block is found to insert before. + */ +const insertIOSConfig = (contents, mlkitConfig) => { + if (contents.includes(MARKER)) return contents; + + const targetIndex = contents.search(/^target\s/m); + if (targetIndex === -1) { + throw new Error( + 'react-native-vision-camera-mlkit config plugin: could not find a `target` block in ios/Podfile to insert the ML Kit configuration before.' + ); + } + + return ( + contents.slice(0, targetIndex) + + toRubyBlock(mlkitConfig) + + '\n' + + contents.slice(targetIndex) + ); +}; + +/** + * Applies {@link insertIOSConfig} to `ios/Podfile`, mirroring what + * `VisionCameraMLKit.podspec` reads via the global `$VisionCameraMLKit` + * variable. + */ +const withVisionCameraMLKitIOS = (config, mlkitConfig) => + withPodfile(config, (modConfig) => { + modConfig.modResults.contents = insertIOSConfig( + modConfig.modResults.contents, + mlkitConfig + ); + return modConfig; + }); + +module.exports = { withVisionCameraMLKitIOS, insertIOSConfig }; diff --git a/yarn.lock b/yarn.lock index 33faa2a..f898472 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32,7 +32,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.29.0, @babel/code-frame@npm:^7.29.7": +"@babel/code-frame@npm:^7.20.0, @babel/code-frame@npm:^7.29.0, @babel/code-frame@npm:^7.29.7": version: 7.29.7 resolution: "@babel/code-frame@npm:7.29.7" dependencies: @@ -2200,6 +2200,78 @@ __metadata: languageName: node linkType: hard +"@expo/config-plugins@npm:57.0.6": + version: 57.0.6 + resolution: "@expo/config-plugins@npm:57.0.6" + dependencies: + "@expo/config-types": "npm:^57.0.2" + "@expo/json-file": "npm:~11.0.1" + "@expo/plist": "npm:^0.8.1" + "@expo/require-utils": "npm:^57.0.4" + "@expo/sdk-runtime-versions": "npm:^1.0.0" + chalk: "npm:^4.1.2" + debug: "npm:^4.3.5" + getenv: "npm:^2.0.0" + glob: "npm:^13.0.0" + semver: "npm:^7.5.4" + slugify: "npm:^1.6.6" + xcode: "npm:^3.0.1" + xml2js: "npm:0.6.0" + checksum: 10c0/98ff17edf2bf90d9fa56dde28d0d13b3c11e4767dd0e21c4cf5187990526eb21949718a9dcc5f062a8903ab56c9382ccea75d871d4a9f72f1ab1a91e001a1826 + languageName: node + linkType: hard + +"@expo/config-types@npm:^57.0.2": + version: 57.0.2 + resolution: "@expo/config-types@npm:57.0.2" + checksum: 10c0/3e3a8abcab03791dfccc758bde5ce85f0fa4b14c22df072e6cc72c670945fc65433239bf2ce39f86bbda422b338d19323612d61eabb2442f988713214837a6dc + languageName: node + linkType: hard + +"@expo/json-file@npm:~11.0.1": + version: 11.0.1 + resolution: "@expo/json-file@npm:11.0.1" + dependencies: + "@babel/code-frame": "npm:^7.20.0" + json5: "npm:^2.2.3" + checksum: 10c0/b93e8bf0654c7d417bf00a418932a3f676bc24bed75ff63a8c683229e2d1f3b7a48206fc3af7db307ed93a776adab51cb62ebb6a0ed77f86067c7ae85cc2f74d + languageName: node + linkType: hard + +"@expo/plist@npm:^0.8.1": + version: 0.8.1 + resolution: "@expo/plist@npm:0.8.1" + dependencies: + "@xmldom/xmldom": "npm:^0.8.8" + base64-js: "npm:^1.5.1" + xmlbuilder: "npm:^15.1.1" + checksum: 10c0/45f130f3d5155d46dc415ab61b97449d5cc7bf47e41ed37965f12e50cc9728aa7c4da83fc958a8d7113e31e4342baebc1b2517de7e20186f3e64d53be378a9ce + languageName: node + linkType: hard + +"@expo/require-utils@npm:^57.0.4": + version: 57.0.4 + resolution: "@expo/require-utils@npm:57.0.4" + dependencies: + "@babel/code-frame": "npm:^7.20.0" + "@babel/core": "npm:^7.25.2" + "@babel/plugin-transform-modules-commonjs": "npm:^7.24.8" + peerDependencies: + typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/63ebc03ff9930af4c1680365b524ebe8aedab430273ad39a64c9afd5842e26fbfd7e38f10d5d2d2c6fc3849065ad1c9288a555596a1fd6fddb97c12e6844694f + languageName: node + linkType: hard + +"@expo/sdk-runtime-versions@npm:^1.0.0": + version: 1.0.0 + resolution: "@expo/sdk-runtime-versions@npm:1.0.0" + checksum: 10c0/f80ae78a294daf396f3eff2eb412948ced5501395a6d3b88058866da9c5135dbacbb2804f8d062222e7452159a61eebefd2f548a2939f539f0f0efe8145588a2 + languageName: node + linkType: hard + "@hapi/hoek@npm:^9.0.0, @hapi/hoek@npm:^9.3.0": version: 9.3.0 resolution: "@hapi/hoek@npm:9.3.0" @@ -4037,6 +4109,20 @@ __metadata: languageName: node linkType: hard +"@xmldom/xmldom@npm:^0.8.8": + version: 0.8.13 + resolution: "@xmldom/xmldom@npm:0.8.13" + checksum: 10c0/06405ee6fffba631abf715a305ace338420ebcea8baf1317f19f2752f5c505952b7df45159908e7be8451a42faa54326b780616ab4d08242b20477b2973da24b + languageName: node + linkType: hard + +"@xmldom/xmldom@npm:^0.9.10": + version: 0.9.10 + resolution: "@xmldom/xmldom@npm:0.9.10" + checksum: 10c0/38a9c9b95450d7fccebc61371c8e0b90ceaae992886484108333d29ccbd2640e3555b6af012f15ce1beb5067aeb40486659b6183fad38af179e82d28028bfc5d + languageName: node + linkType: hard + "JSONStream@npm:^1.3.5": version: 1.3.5 resolution: "JSONStream@npm:1.3.5" @@ -4631,6 +4717,13 @@ __metadata: languageName: node linkType: hard +"big-integer@npm:1.6.x": + version: 1.6.52 + resolution: "big-integer@npm:1.6.52" + checksum: 10c0/9604224b4c2ab3c43c075d92da15863077a9f59e5d4205f4e7e76acd0cd47e8d469ec5e5dba8d9b32aa233951893b29329ca56ac80c20ce094b4a647a66abae0 + languageName: node + linkType: hard + "bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" @@ -4669,6 +4762,24 @@ __metadata: languageName: node linkType: hard +"bplist-creator@npm:0.1.1": + version: 0.1.1 + resolution: "bplist-creator@npm:0.1.1" + dependencies: + stream-buffers: "npm:2.2.x" + checksum: 10c0/427ec37263ce0e8c68a83f595fc9889a9cbf2e6fda2de18e1f8ef7f0c6ce68c0cdbb7c9c1f3bb3f2d217407af8cffbdf254bf0f71c99f2186175d07752f08a47 + languageName: node + linkType: hard + +"bplist-parser@npm:0.3.2": + version: 0.3.2 + resolution: "bplist-parser@npm:0.3.2" + dependencies: + big-integer: "npm:1.6.x" + checksum: 10c0/4dc307c11d2511a01255e87e370d4ab6f1962b35fdc27605fd4ce9a557a259c2dc9f87822617ddb1f7aa062a71e30ef20d6103329ac7ce235628f637fb0ed763 + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.12 resolution: "brace-expansion@npm:1.1.12" @@ -4867,7 +4978,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.1.0": +"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -5566,7 +5677,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.4.0, debug@npm:^4.4.1, debug@npm:^4.4.3": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.4.0, debug@npm:^4.4.1, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -7050,6 +7161,13 @@ __metadata: languageName: node linkType: hard +"getenv@npm:^2.0.0": + version: 2.0.0 + resolution: "getenv@npm:2.0.0" + checksum: 10c0/397ff641dd70cd78e414430258651e9a2228d3c5553a8cf15ae7840f75d3f10dfcb83f668f84829e84ea665b0fce2f08a9eddda3c9dcd7faa2d3da1c182c1854 + languageName: node + linkType: hard + "giget@npm:^2.0.0": version: 2.0.0 resolution: "giget@npm:2.0.0" @@ -10549,6 +10667,17 @@ __metadata: languageName: node linkType: hard +"plist@npm:^3.0.5": + version: 3.1.1 + resolution: "plist@npm:3.1.1" + dependencies: + "@xmldom/xmldom": "npm:^0.9.10" + base64-js: "npm:^1.5.1" + xmlbuilder: "npm:^15.1.1" + checksum: 10c0/af681277c2e541c6aab0ddee57f59459c43beeea1ee7aa2d4218aa39595fb41c068e2d2be3e4abb17e3252eaf2c1b806ecfb1d402fa5a3f4a6e1a7a32c880c9a + languageName: node + linkType: hard + "possible-typed-array-names@npm:^1.0.0": version: 1.1.0 resolution: "possible-typed-array-names@npm:1.1.0" @@ -11026,6 +11155,7 @@ __metadata: "@eslint/compat": "npm:^1.3.2" "@eslint/eslintrc": "npm:^3.3.1" "@eslint/js": "npm:^9.35.0" + "@expo/config-plugins": "npm:57.0.6" "@react-native-community/cli": "npm:20.1.0" "@react-native/babel-preset": "npm:0.86.0" "@react-native/eslint-config": "npm:^0.86.0" @@ -11051,10 +11181,14 @@ __metadata: turbo: "npm:^2.5.6" typescript: "npm:^5.9.2" peerDependencies: + "@expo/config-plugins": ^57.0.0 react: "*" react-native: "*" react-native-nitro-modules: "*" react-native-vision-camera: ">=5" + peerDependenciesMeta: + "@expo/config-plugins": + optional: true languageName: unknown linkType: soft @@ -11544,6 +11678,13 @@ __metadata: languageName: node linkType: hard +"sax@npm:>=0.6.0": + version: 1.6.1 + resolution: "sax@npm:1.6.1" + checksum: 10c0/c91a71043d60da50fdf1e2cee936fc7ad4c9376afb8d1022aef5655f279433640859ec06b3b49abfcbe578fc7da2828cc1661e45aaedf835f5393496193437fa + languageName: node + linkType: hard + "scheduler@npm:0.27.0": version: 0.27.0 resolution: "scheduler@npm:0.27.0" @@ -11761,6 +11902,17 @@ __metadata: languageName: node linkType: hard +"simple-plist@npm:^1.1.0": + version: 1.4.0 + resolution: "simple-plist@npm:1.4.0" + dependencies: + bplist-creator: "npm:0.1.1" + bplist-parser: "npm:0.3.2" + plist: "npm:^3.0.5" + checksum: 10c0/226c283492d8518d715e4133d94bdbd15c0619561bcde583b4807b36cde106c0078c615b9b4e25c0e8758a4ae4e79ed5dd76e57cd528d8b7001ecab5ad35e343 + languageName: node + linkType: hard + "simple-swizzle@npm:^0.2.2": version: 0.2.4 resolution: "simple-swizzle@npm:0.2.4" @@ -11802,6 +11954,13 @@ __metadata: languageName: node linkType: hard +"slugify@npm:^1.6.6": + version: 1.6.9 + resolution: "slugify@npm:1.6.9" + checksum: 10c0/8473c566ae00c5db26bfbf6182a4a9478ab3c912a9c926e1fdb410736a77228bfca4fdc5c755b46fafa7d2d9900de482fd7a9bd5e5c91128c4743c2c072d88da + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -11984,6 +12143,13 @@ __metadata: languageName: node linkType: hard +"stream-buffers@npm:2.2.x": + version: 2.2.0 + resolution: "stream-buffers@npm:2.2.0" + checksum: 10c0/14a351f0a066eaa08c8c64a74f4aedd87dd7a8e59d4be224703da33dca3eb370828ee6c0ae3fff59a9c743e8098728fc95c5f052ae7741672a31e6b1430ba50a + languageName: node + linkType: hard + "strict-uri-encode@npm:^2.0.0": version: 2.0.0 resolution: "strict-uri-encode@npm:2.0.0" @@ -12760,6 +12926,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^7.0.3": + version: 7.0.3 + resolution: "uuid@npm:7.0.3" + bin: + uuid: dist/bin/uuid + checksum: 10c0/2eee5723b0fcce8256f5bfd3112af6c453b5471db00af9c3533e3d5a6e57de83513f9a145a570890457bd7abf2c2aa05797291d950ac666e5a074895dc63168b + languageName: node + linkType: hard + "v8-to-istanbul@npm:^9.0.1": version: 9.3.0 resolution: "v8-to-istanbul@npm:9.3.0" @@ -13048,6 +13223,40 @@ __metadata: languageName: node linkType: hard +"xcode@npm:^3.0.1": + version: 3.0.1 + resolution: "xcode@npm:3.0.1" + dependencies: + simple-plist: "npm:^1.1.0" + uuid: "npm:^7.0.3" + checksum: 10c0/51bf35cee52909aeb18f868ecf9828f93b8042fadf968159320f9f11e757a52e43f6563a53b586986cfe5a34d576f3300c4c0cf1e14300084344ae206eaa53c3 + languageName: node + linkType: hard + +"xml2js@npm:0.6.0": + version: 0.6.0 + resolution: "xml2js@npm:0.6.0" + dependencies: + sax: "npm:>=0.6.0" + xmlbuilder: "npm:~11.0.0" + checksum: 10c0/db1ad659210eda4b77929aa692271308ec7e04830112161b8c707f3bcc7138947409c8461ae5c8bcb36b378d62594a8d1cb78770ff5c3dc46a68c67a0838b486 + languageName: node + linkType: hard + +"xmlbuilder@npm:^15.1.1": + version: 15.1.1 + resolution: "xmlbuilder@npm:15.1.1" + checksum: 10c0/665266a8916498ff8d82b3d46d3993913477a254b98149ff7cff060d9b7cc0db7cf5a3dae99aed92355254a808c0e2e3ec74ad1b04aa1061bdb8dfbea26c18b8 + languageName: node + linkType: hard + +"xmlbuilder@npm:~11.0.0": + version: 11.0.1 + resolution: "xmlbuilder@npm:11.0.1" + checksum: 10c0/74b979f89a0a129926bc786b913459bdbcefa809afaa551c5ab83f89b1915bdaea14c11c759284bb9b931e3b53004dbc2181e21d3ca9553eeb0b2a7b4e40c35b + languageName: node + linkType: hard + "y18n@npm:^4.0.0": version: 4.0.3 resolution: "y18n@npm:4.0.3" From 050c00567715645e4e30a7374ad0e69ddf05a6a8 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 2 Aug 2026 15:53:20 -0300 Subject: [PATCH 41/45] fix: v2 release hardening (Nitro migration + static-image fix + review fixes) (#20) * feat: harden v2 Nitro release architecture * fix(ios): downsample full-resolution static images * fix: address v2 release-hardening review findings - android: recycle intermediate bitmaps in ImagePreprocessor, guarded by reference-equality checks since Bitmap.createBitmap/scale return the source instance when no transform is actually applied - android: serialize access to the shared ML Kit detector between the synchronous frame-processor path and the async static-image path - ios: skip the redundant re-render pass in normalized(to:) when the target orientation is already .up - ci: validate the release tag before building the package in npm-release.yml, so a mismatch fails fast - chore: drop native-tests/ios/StaticImageDecodePlan.test.swift, the test:ios-static script, and its CI wiring; the hardcoded /private/tmp paths and hand-rolled swiftc compile step weren't worth keeping without a proper SPM/XCTest target * fix(ci): stop pinning a hardcoded Xcode version in build-ios.yml The XCODE_VERSION: 16.3 pin predates this branch and no longer exists on GitHub's macos-latest runner image (only 26.0.1-26.6.0 are installed now, per Apple's year-aligned Xcode versioning). Switch to latest-stable so this doesn't silently break again next time GitHub rotates old Xcode versions out of the image. * fix(ci): unpin stale Bundler version that crashes on Ruby 3.4 example/Gemfile.lock's BUNDLED WITH 2.1.4 forces bundle install to download and switch to that ancient Bundler on every run, but its vendored thor lib references DidYouMean::SPELL_CHECKERS, which no longer exists on Ruby 3.4 (the default on current macOS runners) - crashing before pod install ever runs. Bump to 4.0.17, the version already installed and working on the runner before the forced downgrade kicked in. * fix(android): scope RNGP codegen resolution to this library's package android/build.gradle sits at the repo root and is linked into the example app by local path, unlike every other native dependency here (all node_modules-nested). Without jsRootDir set, RNGP appears to resolve the JS root against this package's own package.json, which also declares "workspaces": ["example"], causing it to aggregate codegen output for every native module in the workspace - including @swmansion/react-native-bottom-sheet's - duplicating a class that module already builds for itself and failing app:mergeLibDexDebug. * fix(ci): forward IOS_DESTINATION into the build:ios turbo task turbo.json's build:ios task didn't allowlist IOS_DESTINATION, so Turborepo stripped it from the task's environment before running "react-native build-ios --destination \"\${IOS_DESTINATION:-...}\"". The script's fallback (generic/platform=iOS Simulator) then hit xcodebuild's own destination handling and failed instantly - "xcodebuild exited with error code '64'" with no compile output, less than 2ms after starting. Allowlisting it lets the simulator destination computed by build-ios.yml actually reach the build command. * fix(ci): stop duplicating the platform key in the iOS build destination react-native build-ios unconditionally prepends its own "generic/platform=iOS Simulator," to whatever --destination value is passed - it never replaces it. Our computed IOS_DESTINATION already included "platform=iOS Simulator", so the combined string ended up with two "platform=" keys ("generic/platform=iOS Simulator,platform=iOS Simulator,OS=...,name=..."), which xcodebuild rejected instantly with exit code 64 and zero compile output. Drop the redundant prefix from both the computed value and the local-dev fallback. Also revert the earlier jsRootDir addition to android/build.gradle: two CI runs confirmed it had no effect on the BottomSheetSurfaceViewManagerDelegate duplicate-class error, and the yarn-hoisting theory behind it doesn't hold (the package isn't hoisted to the repo root node_modules), so it wasn't worth keeping as unverified guesswork. * fix(android): restore the package's own react-native.config.js Every other native dependency in the tree (nitro-image, vision-camera, vision-camera-worklets) ships an identical self-descriptor at its package root declaring empty ios/android platforms. Ours was missing entirely, even though package.json's files list expects it to exist. Without it, example/react-native.config.js worked around the gap by inlining an empty platforms override directly on the manual dependencies[pkg.name] entry. That's a different code path than a proper self-descriptor, and correlates with the one concrete anomaly in CI: every sibling library's generateCodegenSchemaFromJavaScript task logs "No modules to process" - ours didn't, and our own module ended up compiling com.facebook.react.viewmanagers.BottomSheetSurface- ViewManagerDelegate into itself, colliding with bottom-sheet's own copy at dex-merge time. Restoring the root config and dropping the now-redundant inline override lets the CLI resolve our package the same way it resolves every other properly-behaved dependency. * fix(android): scope codegen to our own src/specs directory Root cause found via a stray local build artifact (android/build/generated/source/codegen/schema.json): our library's generateCodegenSchemaFromJavaScript task was producing a 158-module schema covering the ENTIRE example app's dependency tree (BlurView, RNGestureHandler, RNSVG, BottomSheetSurfaceView, etc.), not just our own package. That's why our module compiled its own copy of com.facebook.react.viewmanagers.BottomSheetSurfaceViewManagerDelegate, colliding with bottom-sheet's own copy at D8 dex-merge time. Without a codegenConfig, RNGP has nothing to scope the schema combiner to just our package, so it fell back to scanning our whole package root - which, unlike sibling npm packages, has the entire example/ workspace nested inside it. Verified locally with combine-js-to-schema-cli: - scanning "." (previous behavior): 158 modules, includes BottomSheetSurfaceView - scanning "src/specs" (this fix): 0 modules, "No modules to process" - matching every other Nitro library in the tree src/specs only contains .nitro.ts files, which never match the old architecture's codegenNativeComponent/TurboModuleRegistry patterns, so scoping codegen there is safe and produces an empty schema by design. * fix(android): drop unused react-native-nitro-image CMake dependency The dex-merge duplicate that failed earlier builds was masking a second issue: android/build.gradle never declares a Gradle dependency on react-native-nitro-image, so its prefab package was never on CMAKE_FIND_ROOT_PATH, and find_package(react-native-nitro-image REQUIRED) failed with "Findreact-native-nitro-image.cmake not found". Verified react-native-nitro-image is never referenced anywhere in cpp/, nitrogen/, or src/ - it's unused leftover from a template, not an actual dependency. Removing the find_package call and its target_link_libraries entry fixes the CMake configure step without touching anything the library actually uses. --- .editorconfig | 4 + .gitattributes | 5 +- .github/workflows/build-android.yml | 20 +- .github/workflows/build-ios.yml | 18 +- .github/workflows/build-package.yml | 14 +- .github/workflows/ktlint-format.yml | 4 +- .github/workflows/lint-typecheck.yml | 4 +- .github/workflows/npm-release.yml | 67 +- .github/workflows/swift-format.yml | 4 +- .github/workflows/unit-tests.yml | 4 +- .gitignore | 3 - README.md | 122 +- android/CMakeLists.txt | 2 - android/build.gradle | 15 +- .../BarcodeParsedValue+toNitroAnyMap.kt | 13 + .../BarcodeScannerOptions+toDomain.kt | 1 + .../BarcodeScanningResult+toNitro.kt | 4 +- .../visioncameramlkit/HybridBarcodeScanner.kt | 21 + .../visioncameramlkit/HybridTextRecognizer.kt | 21 + .../visioncameramlkit/Orientation+toDomain.kt | 12 + .../visioncameramlkit/ResolvedStaticImage.kt | 16 + .../StaticImageUriResolver.kt | 90 + .../TextRecognitionLanguage+toDomain.kt | 4 + .../TextRecognizerOptions+toDomain.kt | 1 + .../VisionCameraMLKitPackage.kt | 24 +- .../usecases/RecognizeBarcodesUseCase.kt | 32 - .../usecases/RecognizeTextUseCase.kt | 32 - .../bridge/constants/MLKitFeatureKeys.kt | 16 - .../bridge/handlers/IStaticImageHandler.kt | 12 - .../handlers/StaticBarcodeScanningHandler.kt | 171 -- .../handlers/StaticTextRecognitionHandler.kt | 159 -- .../parsers/BarcodeScanningOptionParser.kt | 41 - .../parsers/RegionOfInterestOptionParser.kt | 32 - .../domain/models/StaticImageLimits.kt | 7 + .../domain/services/IRecognitionService.kt | 2 +- .../infrastructure/image/ImagePreprocessor.kt | 55 +- .../mlkit/MLKitBarcodeScanningService.kt | 20 +- .../mlkit/MLKitTextRecognitionService.kt | 20 +- .../modules/VisionCameraMLKitModule.kt | 61 - .../serializers/BarcodeScanningSerializer.kt | 190 -- .../serializers/TextRecognitionSerializer.kt | 116 -- docs/barcode-scanning.md | 33 +- docs/migration-v1-to-v2.md | 72 + docs/text-recognition.md | 33 +- example/Gemfile.lock | 2 +- example/android/build.gradle | 15 +- example/ios/Podfile.lock | 54 +- example/package.json | 2 +- example/react-native.config.js | 6 - .../UseCases/RecognizeBarcodesUseCase.swift | 81 - .../UseCases/RecognizeTextUseCase.swift | 76 - ios/Bridge/Constants/MLKitFeatureKeys.swift | 15 - ios/Bridge/Handlers/IStaticImageHandler.swift | 13 - .../StaticBarcodeScanningHandler.swift | 150 -- .../StaticTextRecognitionHandler.swift | 161 -- .../Hybrid/BarcodeParsedValue+toAnyMap.swift | 34 + .../BarcodeScannerOptions+toDomain.swift | 1 + .../BarcodeScanningResult+toNitro.swift | 45 +- ios/Bridge/Hybrid/HybridBarcodeScanner.swift | 46 +- ios/Bridge/Hybrid/HybridTextRecognizer.swift | 44 +- ios/Bridge/Hybrid/Orientation+toDomain.swift | 19 + .../TextRecognizerOptions+toDomain.swift | 1 + .../Parsers/BarcodeScanningOptionParser.swift | 21 - ...cessingOptions+withOutputOrientation.swift | 11 + ios/Domain/Models/StaticImageDecodePlan.swift | 80 + ios/Domain/Models/StaticImageLimits.swift | 29 + .../Image/ImagePreprocessor.swift | 148 +- .../Image/StaticImageURLResolver.swift | 49 + .../Image/UIImage+normalizedOrientation.swift | 36 + .../MLKit/Adapters/MLKitBarcodeAdapter.swift | 115 +- .../Modules/VisionCameraMLKitModule.swift | 45 - .../Modules/VisionCameraMLKitModuleBridge.m | 11 - .../BarcodeScanningSerializer.swift | 89 - .../TextRecognitionSerializer.swift | 112 - nitrogen/generated/.gitattributes | 1 + .../VisionCameraMLKit+autolinking.cmake | 86 + .../VisionCameraMLKit+autolinking.gradle | 27 + .../android/VisionCameraMLKitOnLoad.cpp | 58 + .../android/VisionCameraMLKitOnLoad.hpp | 34 + .../generated/android/c++/JBarcodeFormat.hpp | 97 + .../android/c++/JBarcodeScannerBarcode.hpp | 137 ++ .../android/c++/JBarcodeScannerOptions.hpp | 108 + .../android/c++/JBarcodeScannerResult.hpp | 89 + .../android/c++/JBarcodeValueTypeName.hpp | 91 + .../generated/android/c++/JBoundingBox.hpp | 93 + nitrogen/generated/android/c++/JCorner.hpp | 61 + .../android/c++/JHybridBarcodeScannerSpec.cpp | 111 + .../android/c++/JHybridBarcodeScannerSpec.hpp | 64 + .../android/c++/JHybridTextRecognizerSpec.cpp | 117 ++ .../android/c++/JHybridTextRecognizerSpec.hpp | 64 + .../c++/JHybridVisionCameraMLKitSpec.cpp | 104 + .../c++/JHybridVisionCameraMLKitSpec.hpp | 65 + .../android/c++/JMLKitBaseArguments.hpp | 59 + .../generated/android/c++/JMLKitFeature.hpp | 85 + .../generated/android/c++/JOrientation.hpp | 64 + .../android/c++/JRegionOfInterest.hpp | 75 + .../android/c++/JRegionOfInterestUnit.hpp | 58 + nitrogen/generated/android/c++/JTextBlock.hpp | 143 ++ .../generated/android/c++/JTextElement.hpp | 147 ++ nitrogen/generated/android/c++/JTextLine.hpp | 149 ++ .../android/c++/JTextRecognitionLanguage.hpp | 67 + .../android/c++/JTextRecognitionResult.hpp | 97 + .../android/c++/JTextRecognizerOptions.hpp | 85 + .../generated/android/c++/JTextSymbol.hpp | 118 ++ .../android/c++/JVariant_NullType_Double.cpp | 26 + .../android/c++/JVariant_NullType_Double.hpp | 69 + .../nitro/visioncameramlkit/BarcodeFormat.kt | 36 + .../BarcodeScannerBarcode.kt | 101 + .../BarcodeScannerOptions.kt | 81 + .../visioncameramlkit/BarcodeScannerResult.kt | 51 + .../visioncameramlkit/BarcodeValueTypeName.kt | 34 + .../nitro/visioncameramlkit/BoundingBox.kt | 96 + .../margelo/nitro/visioncameramlkit/Corner.kt | 56 + .../HybridBarcodeScannerSpec.kt | 60 + .../HybridTextRecognizerSpec.kt | 60 + .../HybridVisionCameraMLKitSpec.kt | 62 + .../visioncameramlkit/MLKitBaseArguments.kt | 51 + .../nitro/visioncameramlkit/MLKitFeature.kt | 32 + .../nitro/visioncameramlkit/Orientation.kt | 25 + .../visioncameramlkit/RegionOfInterest.kt | 71 + .../visioncameramlkit/RegionOfInterestUnit.kt | 23 + .../nitro/visioncameramlkit/TextBlock.kt | 71 + .../nitro/visioncameramlkit/TextElement.kt | 81 + .../nitro/visioncameramlkit/TextLine.kt | 81 + .../TextRecognitionLanguage.kt | 26 + .../TextRecognitionResult.kt | 56 + .../TextRecognizerOptions.kt | 76 + .../nitro/visioncameramlkit/TextSymbol.kt | 76 + .../Variant_NullType_Double.kt | 62 + .../VisionCameraMLKitOnLoad.kt | 35 + .../ios/VisionCameraMLKit+autolinking.rb | 62 + .../VisionCameraMLKit-Swift-Cxx-Bridge.cpp | 102 + .../VisionCameraMLKit-Swift-Cxx-Bridge.hpp | 641 ++++++ .../VisionCameraMLKit-Swift-Cxx-Umbrella.hpp | 122 ++ .../ios/VisionCameraMLKitAutolinking.mm | 33 + .../ios/VisionCameraMLKitAutolinking.swift | 26 + .../ios/c++/HybridBarcodeScannerSpecSwift.cpp | 11 + .../ios/c++/HybridBarcodeScannerSpecSwift.hpp | 121 ++ .../ios/c++/HybridTextRecognizerSpecSwift.cpp | 11 + .../ios/c++/HybridTextRecognizerSpecSwift.hpp | 125 ++ .../c++/HybridVisionCameraMLKitSpecSwift.cpp | 11 + .../c++/HybridVisionCameraMLKitSpecSwift.hpp | 129 ++ .../generated/ios/swift/BarcodeFormat.swift | 92 + .../ios/swift/BarcodeScannerBarcode.swift | 162 ++ .../ios/swift/BarcodeScannerOptions.swift | 142 ++ .../ios/swift/BarcodeScannerResult.swift | 35 + .../ios/swift/BarcodeValueTypeName.swift | 84 + .../generated/ios/swift/BoundingBox.swift | 74 + nitrogen/generated/ios/swift/Corner.swift | 34 + .../Func_void_BarcodeScannerResult.swift | 46 + .../Func_void_TextRecognitionResult.swift | 46 + .../swift/Func_void_std__exception_ptr.swift | 46 + .../ios/swift/HybridBarcodeScannerSpec.swift | 57 + .../swift/HybridBarcodeScannerSpec_cxx.swift | 162 ++ .../ios/swift/HybridTextRecognizerSpec.swift | 57 + .../swift/HybridTextRecognizerSpec_cxx.swift | 162 ++ .../swift/HybridVisionCameraMLKitSpec.swift | 57 + .../HybridVisionCameraMLKitSpec_cxx.swift | 168 ++ .../ios/swift/MLKitBaseArguments.swift | 35 + .../generated/ios/swift/MLKitFeature.swift | 76 + .../generated/ios/swift/Orientation.swift | 48 + .../ios/swift/RegionOfInterest.swift | 55 + .../ios/swift/RegionOfInterestUnit.swift | 40 + nitrogen/generated/ios/swift/TextBlock.swift | 67 + .../generated/ios/swift/TextElement.swift | 141 ++ nitrogen/generated/ios/swift/TextLine.swift | 141 ++ .../ios/swift/TextRecognitionLanguage.swift | 52 + .../ios/swift/TextRecognitionResult.swift | 40 + .../ios/swift/TextRecognizerOptions.swift | 111 + nitrogen/generated/ios/swift/TextSymbol.swift | 66 + .../ios/swift/Variant_NullType_Double.swift | 30 + .../generated/shared/c++/BarcodeFormat.hpp | 128 ++ .../shared/c++/BarcodeScannerBarcode.hpp | 137 ++ .../shared/c++/BarcodeScannerOptions.hpp | 116 ++ .../shared/c++/BarcodeScannerResult.hpp | 85 + .../shared/c++/BarcodeValueTypeName.hpp | 120 ++ nitrogen/generated/shared/c++/BoundingBox.hpp | 119 ++ nitrogen/generated/shared/c++/Corner.hpp | 87 + .../shared/c++/HybridBarcodeScannerSpec.cpp | 22 + .../shared/c++/HybridBarcodeScannerSpec.hpp | 74 + .../shared/c++/HybridTextRecognizerSpec.cpp | 22 + .../shared/c++/HybridTextRecognizerSpec.hpp | 74 + .../c++/HybridVisionCameraMLKitSpec.cpp | 23 + .../c++/HybridVisionCameraMLKitSpec.hpp | 78 + .../shared/c++/MLKitBaseArguments.hpp | 85 + .../generated/shared/c++/MLKitFeature.hpp | 112 + nitrogen/generated/shared/c++/Orientation.hpp | 84 + .../generated/shared/c++/RegionOfInterest.hpp | 101 + .../shared/c++/RegionOfInterestUnit.hpp | 76 + nitrogen/generated/shared/c++/TextBlock.hpp | 108 + nitrogen/generated/shared/c++/TextElement.hpp | 119 ++ nitrogen/generated/shared/c++/TextLine.hpp | 119 ++ .../shared/c++/TextRecognitionLanguage.hpp | 88 + .../shared/c++/TextRecognitionResult.hpp | 90 + .../shared/c++/TextRecognizerOptions.hpp | 111 + nitrogen/generated/shared/c++/TextSymbol.hpp | 109 + package.json | 39 +- react-native.config.js | 16 + src/VisionCameraMLKitSpec.ts | 10 - src/__tests__/NativeBridge.test.ts | 79 - src/__tests__/PluginFactory.test.ts | 86 + src/__tests__/frameProcessingHooks.test.ts | 94 + src/__tests__/staticImageNitro.test.ts | 259 +++ src/__tests__/useMLKitPlugin.test.ts | 57 + .../validateRegionOfInterest.test.ts | 54 + src/core/NativeBridge.ts | 74 - src/core/PluginFactory.ts | 133 +- src/core/normalizeImageUri.test.ts | 58 + src/core/normalizeImageUri.ts | 18 + src/core/types.ts | 7 + src/core/validateRegionOfInterest.ts | 55 +- src/features/barcode-scanning/index.ts | 17 +- .../mapStaticBarcodeResult.test.ts | 149 ++ .../mapStaticBarcodeResult.ts | 294 +++ src/features/barcode-scanning/types.ts | 2 - src/features/text-recognition/index.ts | 14 +- src/shared/constants.ts | 10 - src/specs/BarcodeScanner.nitro.ts | 5 + src/specs/BarcodeScannerTypes.ts | 7 + src/specs/MLKitVisionTypes.ts | 7 +- src/specs/TextRecognizer.nitro.ts | 5 + turbo.json | 3 +- yarn.lock | 1794 ++++++++--------- 223 files changed, 13423 insertions(+), 3090 deletions(-) create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeParsedValue+toNitroAnyMap.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/Orientation+toDomain.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/ResolvedStaticImage.kt create mode 100644 android/src/main/java/com/margelo/nitro/visioncameramlkit/StaticImageUriResolver.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeTextUseCase.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/bridge/constants/MLKitFeatureKeys.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/bridge/handlers/IStaticImageHandler.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticBarcodeScanningHandler.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/bridge/parsers/BarcodeScanningOptionParser.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/bridge/parsers/RegionOfInterestOptionParser.kt create mode 100644 android/src/main/java/com/visioncameramlkit/domain/models/StaticImageLimits.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/infrastructure/serializers/BarcodeScanningSerializer.kt delete mode 100644 android/src/main/java/com/visioncameramlkit/infrastructure/serializers/TextRecognitionSerializer.kt create mode 100644 docs/migration-v1-to-v2.md delete mode 100644 ios/Application/UseCases/RecognizeBarcodesUseCase.swift delete mode 100644 ios/Application/UseCases/RecognizeTextUseCase.swift delete mode 100644 ios/Bridge/Constants/MLKitFeatureKeys.swift delete mode 100644 ios/Bridge/Handlers/IStaticImageHandler.swift delete mode 100644 ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift delete mode 100644 ios/Bridge/Handlers/StaticTextRecognitionHandler.swift create mode 100644 ios/Bridge/Hybrid/BarcodeParsedValue+toAnyMap.swift create mode 100644 ios/Bridge/Hybrid/Orientation+toDomain.swift delete mode 100644 ios/Bridge/Parsers/BarcodeScanningOptionParser.swift create mode 100644 ios/Domain/Models/ImagePreprocessingOptions+withOutputOrientation.swift create mode 100644 ios/Domain/Models/StaticImageDecodePlan.swift create mode 100644 ios/Domain/Models/StaticImageLimits.swift create mode 100644 ios/Infrastructure/Image/StaticImageURLResolver.swift create mode 100644 ios/Infrastructure/Image/UIImage+normalizedOrientation.swift delete mode 100644 ios/Infrastructure/Modules/VisionCameraMLKitModule.swift delete mode 100644 ios/Infrastructure/Modules/VisionCameraMLKitModuleBridge.m delete mode 100644 ios/Infrastructure/Serializers/BarcodeScanningSerializer.swift delete mode 100644 ios/Infrastructure/Serializers/TextRecognitionSerializer.swift create mode 100644 nitrogen/generated/.gitattributes create mode 100644 nitrogen/generated/android/VisionCameraMLKit+autolinking.cmake create mode 100644 nitrogen/generated/android/VisionCameraMLKit+autolinking.gradle create mode 100644 nitrogen/generated/android/VisionCameraMLKitOnLoad.cpp create mode 100644 nitrogen/generated/android/VisionCameraMLKitOnLoad.hpp create mode 100644 nitrogen/generated/android/c++/JBarcodeFormat.hpp create mode 100644 nitrogen/generated/android/c++/JBarcodeScannerBarcode.hpp create mode 100644 nitrogen/generated/android/c++/JBarcodeScannerOptions.hpp create mode 100644 nitrogen/generated/android/c++/JBarcodeScannerResult.hpp create mode 100644 nitrogen/generated/android/c++/JBarcodeValueTypeName.hpp create mode 100644 nitrogen/generated/android/c++/JBoundingBox.hpp create mode 100644 nitrogen/generated/android/c++/JCorner.hpp create mode 100644 nitrogen/generated/android/c++/JHybridBarcodeScannerSpec.cpp create mode 100644 nitrogen/generated/android/c++/JHybridBarcodeScannerSpec.hpp create mode 100644 nitrogen/generated/android/c++/JHybridTextRecognizerSpec.cpp create mode 100644 nitrogen/generated/android/c++/JHybridTextRecognizerSpec.hpp create mode 100644 nitrogen/generated/android/c++/JHybridVisionCameraMLKitSpec.cpp create mode 100644 nitrogen/generated/android/c++/JHybridVisionCameraMLKitSpec.hpp create mode 100644 nitrogen/generated/android/c++/JMLKitBaseArguments.hpp create mode 100644 nitrogen/generated/android/c++/JMLKitFeature.hpp create mode 100644 nitrogen/generated/android/c++/JOrientation.hpp create mode 100644 nitrogen/generated/android/c++/JRegionOfInterest.hpp create mode 100644 nitrogen/generated/android/c++/JRegionOfInterestUnit.hpp create mode 100644 nitrogen/generated/android/c++/JTextBlock.hpp create mode 100644 nitrogen/generated/android/c++/JTextElement.hpp create mode 100644 nitrogen/generated/android/c++/JTextLine.hpp create mode 100644 nitrogen/generated/android/c++/JTextRecognitionLanguage.hpp create mode 100644 nitrogen/generated/android/c++/JTextRecognitionResult.hpp create mode 100644 nitrogen/generated/android/c++/JTextRecognizerOptions.hpp create mode 100644 nitrogen/generated/android/c++/JTextSymbol.hpp create mode 100644 nitrogen/generated/android/c++/JVariant_NullType_Double.cpp create mode 100644 nitrogen/generated/android/c++/JVariant_NullType_Double.hpp create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeFormat.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeScannerBarcode.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeScannerResult.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeValueTypeName.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BoundingBox.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/Corner.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/HybridBarcodeScannerSpec.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/HybridTextRecognizerSpec.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/HybridVisionCameraMLKitSpec.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/MLKitBaseArguments.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/MLKitFeature.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/Orientation.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/RegionOfInterest.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/RegionOfInterestUnit.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextBlock.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextElement.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextLine.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextRecognitionLanguage.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextRecognitionResult.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextSymbol.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/Variant_NullType_Double.kt create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/VisionCameraMLKitOnLoad.kt create mode 100644 nitrogen/generated/ios/VisionCameraMLKit+autolinking.rb create mode 100644 nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Bridge.cpp create mode 100644 nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Bridge.hpp create mode 100644 nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Umbrella.hpp create mode 100644 nitrogen/generated/ios/VisionCameraMLKitAutolinking.mm create mode 100644 nitrogen/generated/ios/VisionCameraMLKitAutolinking.swift create mode 100644 nitrogen/generated/ios/c++/HybridBarcodeScannerSpecSwift.cpp create mode 100644 nitrogen/generated/ios/c++/HybridBarcodeScannerSpecSwift.hpp create mode 100644 nitrogen/generated/ios/c++/HybridTextRecognizerSpecSwift.cpp create mode 100644 nitrogen/generated/ios/c++/HybridTextRecognizerSpecSwift.hpp create mode 100644 nitrogen/generated/ios/c++/HybridVisionCameraMLKitSpecSwift.cpp create mode 100644 nitrogen/generated/ios/c++/HybridVisionCameraMLKitSpecSwift.hpp create mode 100644 nitrogen/generated/ios/swift/BarcodeFormat.swift create mode 100644 nitrogen/generated/ios/swift/BarcodeScannerBarcode.swift create mode 100644 nitrogen/generated/ios/swift/BarcodeScannerOptions.swift create mode 100644 nitrogen/generated/ios/swift/BarcodeScannerResult.swift create mode 100644 nitrogen/generated/ios/swift/BarcodeValueTypeName.swift create mode 100644 nitrogen/generated/ios/swift/BoundingBox.swift create mode 100644 nitrogen/generated/ios/swift/Corner.swift create mode 100644 nitrogen/generated/ios/swift/Func_void_BarcodeScannerResult.swift create mode 100644 nitrogen/generated/ios/swift/Func_void_TextRecognitionResult.swift create mode 100644 nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift create mode 100644 nitrogen/generated/ios/swift/HybridBarcodeScannerSpec.swift create mode 100644 nitrogen/generated/ios/swift/HybridBarcodeScannerSpec_cxx.swift create mode 100644 nitrogen/generated/ios/swift/HybridTextRecognizerSpec.swift create mode 100644 nitrogen/generated/ios/swift/HybridTextRecognizerSpec_cxx.swift create mode 100644 nitrogen/generated/ios/swift/HybridVisionCameraMLKitSpec.swift create mode 100644 nitrogen/generated/ios/swift/HybridVisionCameraMLKitSpec_cxx.swift create mode 100644 nitrogen/generated/ios/swift/MLKitBaseArguments.swift create mode 100644 nitrogen/generated/ios/swift/MLKitFeature.swift create mode 100644 nitrogen/generated/ios/swift/Orientation.swift create mode 100644 nitrogen/generated/ios/swift/RegionOfInterest.swift create mode 100644 nitrogen/generated/ios/swift/RegionOfInterestUnit.swift create mode 100644 nitrogen/generated/ios/swift/TextBlock.swift create mode 100644 nitrogen/generated/ios/swift/TextElement.swift create mode 100644 nitrogen/generated/ios/swift/TextLine.swift create mode 100644 nitrogen/generated/ios/swift/TextRecognitionLanguage.swift create mode 100644 nitrogen/generated/ios/swift/TextRecognitionResult.swift create mode 100644 nitrogen/generated/ios/swift/TextRecognizerOptions.swift create mode 100644 nitrogen/generated/ios/swift/TextSymbol.swift create mode 100644 nitrogen/generated/ios/swift/Variant_NullType_Double.swift create mode 100644 nitrogen/generated/shared/c++/BarcodeFormat.hpp create mode 100644 nitrogen/generated/shared/c++/BarcodeScannerBarcode.hpp create mode 100644 nitrogen/generated/shared/c++/BarcodeScannerOptions.hpp create mode 100644 nitrogen/generated/shared/c++/BarcodeScannerResult.hpp create mode 100644 nitrogen/generated/shared/c++/BarcodeValueTypeName.hpp create mode 100644 nitrogen/generated/shared/c++/BoundingBox.hpp create mode 100644 nitrogen/generated/shared/c++/Corner.hpp create mode 100644 nitrogen/generated/shared/c++/HybridBarcodeScannerSpec.cpp create mode 100644 nitrogen/generated/shared/c++/HybridBarcodeScannerSpec.hpp create mode 100644 nitrogen/generated/shared/c++/HybridTextRecognizerSpec.cpp create mode 100644 nitrogen/generated/shared/c++/HybridTextRecognizerSpec.hpp create mode 100644 nitrogen/generated/shared/c++/HybridVisionCameraMLKitSpec.cpp create mode 100644 nitrogen/generated/shared/c++/HybridVisionCameraMLKitSpec.hpp create mode 100644 nitrogen/generated/shared/c++/MLKitBaseArguments.hpp create mode 100644 nitrogen/generated/shared/c++/MLKitFeature.hpp create mode 100644 nitrogen/generated/shared/c++/Orientation.hpp create mode 100644 nitrogen/generated/shared/c++/RegionOfInterest.hpp create mode 100644 nitrogen/generated/shared/c++/RegionOfInterestUnit.hpp create mode 100644 nitrogen/generated/shared/c++/TextBlock.hpp create mode 100644 nitrogen/generated/shared/c++/TextElement.hpp create mode 100644 nitrogen/generated/shared/c++/TextLine.hpp create mode 100644 nitrogen/generated/shared/c++/TextRecognitionLanguage.hpp create mode 100644 nitrogen/generated/shared/c++/TextRecognitionResult.hpp create mode 100644 nitrogen/generated/shared/c++/TextRecognizerOptions.hpp create mode 100644 nitrogen/generated/shared/c++/TextSymbol.hpp create mode 100644 react-native.config.js delete mode 100644 src/VisionCameraMLKitSpec.ts delete mode 100644 src/__tests__/NativeBridge.test.ts create mode 100644 src/__tests__/PluginFactory.test.ts create mode 100644 src/__tests__/frameProcessingHooks.test.ts create mode 100644 src/__tests__/staticImageNitro.test.ts create mode 100644 src/__tests__/useMLKitPlugin.test.ts delete mode 100644 src/core/NativeBridge.ts create mode 100644 src/core/normalizeImageUri.test.ts create mode 100644 src/core/normalizeImageUri.ts create mode 100644 src/features/barcode-scanning/mapStaticBarcodeResult.test.ts create mode 100644 src/features/barcode-scanning/mapStaticBarcodeResult.ts diff --git a/.editorconfig b/.editorconfig index 65365be..8faf7cf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,3 +13,7 @@ end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true + +[*.{kt,kts}] +# Kotlin extension filenames intentionally mirror Swift's Type+operation style. +ktlint_standard_filename = disabled diff --git a/.gitattributes b/.gitattributes index e27f70f..548d575 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,2 @@ -*.pbxproj -text -# specific for windows script files -*.bat text eol=crlf +# Nitrogen owns generated formatting, including intentional trailing spaces. +nitrogen/generated/** -whitespace diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml index d9cb0d6..4d7cbce 100644 --- a/.github/workflows/build-android.yml +++ b/.github/workflows/build-android.yml @@ -2,19 +2,29 @@ name: Build Example (Android) on: pull_request: - branches: [main] + branches: [main, develop] paths: + - '.github/workflows/build-android.yml' - 'android/**' + - 'cpp/**' - 'example/android/**' + - 'nitrogen/**' + - 'nitro.json' + - 'src/specs/**' - 'package.json' - 'yarn.lock' - '*.gradle' - '*.gradle.kts' push: - branches: [main] + branches: [main, develop] paths: + - '.github/workflows/build-android.yml' - 'android/**' + - 'cpp/**' - 'example/android/**' + - 'nitrogen/**' + - 'nitro.json' + - 'src/specs/**' - 'package.json' - 'yarn.lock' - '*.gradle' @@ -79,3 +89,9 @@ jobs: - name: Build Android example run: yarn turbo run build:android + + - name: Compile library with v2 features disabled + working-directory: example/android + env: + VISION_CAMERA_MLKIT_DISABLE_V2_FEATURES: 'true' + run: ./gradlew :react-native-vision-camera-mlkit:compileDebugKotlin --no-daemon diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index 8ed619d..aab7a2a 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -2,18 +2,28 @@ name: Build Example (iOS) on: pull_request: - branches: [main] + branches: [main, develop] paths: + - '.github/workflows/build-ios.yml' + - 'cpp/**' - 'ios/**' - 'example/ios/**' + - 'nitrogen/**' + - 'nitro.json' + - 'src/specs/**' - '*.podspec' - 'package.json' - 'yarn.lock' push: - branches: [main] + branches: [main, develop] paths: + - '.github/workflows/build-ios.yml' + - 'cpp/**' - 'ios/**' - 'example/ios/**' + - 'nitrogen/**' + - 'nitro.json' + - 'src/specs/**' - '*.podspec' - 'package.json' - 'yarn.lock' @@ -32,7 +42,7 @@ jobs: runs-on: macos-latest env: - XCODE_VERSION: 16.3 + XCODE_VERSION: latest-stable RCT_USE_RN_DEP: 1 RCT_USE_PREBUILT_RNCORE: 1 @@ -86,7 +96,7 @@ jobs: exit 1 fi - echo "IOS_DESTINATION=platform=iOS Simulator,OS=${IOS_RUNTIME},name=iPhone 15" >> "$GITHUB_ENV" + echo "IOS_DESTINATION=OS=${IOS_RUNTIME},name=iPhone 15" >> "$GITHUB_ENV" - name: Install CocoaPods run: | diff --git a/.github/workflows/build-package.yml b/.github/workflows/build-package.yml index 110f739..c52bb1c 100644 --- a/.github/workflows/build-package.yml +++ b/.github/workflows/build-package.yml @@ -2,9 +2,9 @@ name: Build Package on: pull_request: - branches: [main] + branches: [main, develop] push: - branches: [main] + branches: [main, develop] permissions: contents: read @@ -23,5 +23,15 @@ jobs: - name: Setup environment uses: ./.github/actions/setup + - name: Verify generated Nitro specs are current + run: | + yarn specs + if [ -n "$(git status --porcelain --untracked-files=all -- nitrogen)" ]; then + echo "Generated Nitro specs are out of date. Run 'yarn specs' and commit the results." + git status --short --untracked-files=all -- nitrogen + git diff -- nitrogen + exit 1 + fi + - name: Build library run: yarn prepare diff --git a/.github/workflows/ktlint-format.yml b/.github/workflows/ktlint-format.yml index 65f867a..3955319 100644 --- a/.github/workflows/ktlint-format.yml +++ b/.github/workflows/ktlint-format.yml @@ -2,14 +2,14 @@ name: Ktlint Format (Android) on: push: - branches: [main] + branches: [main, develop] paths: - '.github/workflows/ktlint-format.yml' - 'android/**/*.kt' - 'android/**/*.kts' - '.editorconfig' pull_request: - branches: [main] + branches: [main, develop] paths: - '.github/workflows/ktlint-format.yml' - 'android/**/*.kt' diff --git a/.github/workflows/lint-typecheck.yml b/.github/workflows/lint-typecheck.yml index f9d24de..1c5b1f3 100644 --- a/.github/workflows/lint-typecheck.yml +++ b/.github/workflows/lint-typecheck.yml @@ -2,9 +2,9 @@ name: Lint & Typecheck on: pull_request: - branches: [main] + branches: [main, develop] push: - branches: [main] + branches: [main, develop] permissions: contents: read diff --git a/.github/workflows/npm-release.yml b/.github/workflows/npm-release.yml index 9f77bb3..c406bcc 100644 --- a/.github/workflows/npm-release.yml +++ b/.github/workflows/npm-release.yml @@ -3,38 +3,55 @@ name: NPM Release on: release: types: [published] - workflow_dispatch: permissions: contents: read - id-token: write jobs: build: name: Build package runs-on: ubuntu-latest + outputs: + tarball: ${{ steps.pack.outputs.tarball }} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup environment uses: ./.github/actions/setup + - name: Validate release tag + run: | + PACKAGE_VERSION="$(node -p "require('./package.json').version")" + EXPECTED_TAG="v${PACKAGE_VERSION}" + + if [[ "$GITHUB_REF_TYPE" != "tag" || "$GITHUB_REF_NAME" != "$EXPECTED_TAG" ]]; then + echo "Release ref must be the package version tag: $EXPECTED_TAG" >&2 + exit 1 + fi + - name: Build package run: yarn prepare - - name: Upload build artifacts - uses: actions/upload-artifact@v4 + - name: Pack package + id: pack + run: | + TARBALL="$(npm pack | tail -n 1)" + + if [[ ! "$TARBALL" =~ ^react-native-vision-camera-mlkit-[0-9A-Za-z._-]+\.tgz$ ]]; then + echo "Unexpected package tarball name: $TARBALL" >&2 + exit 1 + fi + + echo "tarball=$TARBALL" >> "$GITHUB_OUTPUT" + + - name: Upload package tarball + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: lib - path: | - bin - android - ios - package.json - README.md - LICENSE* + name: package-tarball + path: ${{ steps.pack.outputs.tarball }} + if-no-files-found: error publish: name: Publish to npm @@ -46,22 +63,18 @@ jobs: id-token: write steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup environment - uses: ./.github/actions/setup - - name: Setup Node.js for npm - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: - node-version: 20 + # npm trusted publishing requires npm >=11.5.1 and Node >=22.14.0. + # Node 24.7.0 ships npm 11.5.1. + node-version: 24.7.0 registry-url: https://registry.npmjs.org - - name: Download build artifacts (overwrite into repo root) - uses: actions/download-artifact@v4 + - name: Download package tarball + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: - name: lib + name: package-tarball path: . - name: Determine npm tag @@ -80,9 +93,11 @@ jobs: fi - name: Publish package + env: + PACKAGE_TARBALL: ${{ needs.build.outputs.tarball }} run: | if [ "${{ steps.npm-tag.outputs.tag }}" = "latest" ]; then - npm publish --provenance --access public + npm publish "$PACKAGE_TARBALL" --ignore-scripts --provenance --access public else - npm publish --provenance --access public --tag ${{ steps.npm-tag.outputs.tag }} + npm publish "$PACKAGE_TARBALL" --ignore-scripts --provenance --access public --tag ${{ steps.npm-tag.outputs.tag }} fi diff --git a/.github/workflows/swift-format.yml b/.github/workflows/swift-format.yml index 82df3de..26ee93a 100644 --- a/.github/workflows/swift-format.yml +++ b/.github/workflows/swift-format.yml @@ -2,13 +2,13 @@ name: Swift Format (iOS) on: pull_request: - branches: [main] + branches: [main, develop] paths: - '.github/workflows/swift-format.yml' - 'ios/**/*.swift' - '.swift-format' push: - branches: [main] + branches: [main, develop] paths: - '.github/workflows/swift-format.yml' - 'ios/**/*.swift' diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index f5be3ba..240e6ca 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -2,9 +2,9 @@ name: Unit Tests on: pull_request: - branches: [main] + branches: [main, develop] push: - branches: [main] + branches: [main, develop] permissions: contents: read diff --git a/.gitignore b/.gitignore index 7b8793a..512508d 100644 --- a/.gitignore +++ b/.gitignore @@ -92,6 +92,3 @@ lib/ # React Native Codegen ios/generated android/generated - -# React Native Nitro Modules -nitrogen/ diff --git a/README.md b/README.md index f6881d6..3dff1a5 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,14 @@ [![MIT License][license-shield]][license-url] [![NPM Version][npm-version-shield]][npm-version-url] -A [React Native Vision Camera](https://github.com/mrousavy/react-native-vision-camera) plugin that exposes high-performance [Google ML Kit](https://developers.google.com/ml-kit) [frame output](https://visioncamera.margelo.com/docs/frame-output) features. Ships today: text recognition (OCR) and barcode scanning. Face detection, pose detection, and other ML Kit vision features are planned — see the [roadmap](#google-ml-kit-vision-features-roadmap) below. +A Nitro-based [React Native Vision Camera](https://github.com/mrousavy/react-native-vision-camera) v5 plugin that exposes high-performance [Google ML Kit](https://developers.google.com/ml-kit) [frame output](https://visioncamera.margelo.com/docs/frame-output) features. v2 implements text recognition (OCR) and barcode scanning for live frames and static images. Other ML Kit vision features are post-v2 roadmap items. > The example app is intentionally heavy and demo-focused. For integration details, follow the documentation below. ## Requirements -- iOS 12+ and Android SDK 21+ +- React Native 0.86+ +- iOS 15.1+ and Android SDK 24+ - [react-native-vision-camera](https://www.npmjs.com/package/react-native-vision-camera) `>=5` (Nitro-based v5, not v4) - [react-native-nitro-modules](https://www.npmjs.com/package/react-native-nitro-modules) @@ -54,8 +55,9 @@ module.exports = { ``` The **static-image APIs** (`processImageTextRecognition`, -`processImageBarcodeScanning`) don't use frame processors and don't need -Vision Camera, Worklets, or a camera at all. +`processImageBarcodeScanning`) don't use frame outputs, Worklets, or a camera. +They do use the package's configured Nitro recognizers, so VisionCamera v5 and +Nitro Modules remain installation requirements for the native package. > For Expo, follow the Vision Camera guide for camera permissions/setup: > [visioncamera.margelo.com/docs](https://visioncamera.margelo.com/docs). @@ -74,7 +76,17 @@ cd ios && pod install ## ML Kit Models Installation (Selective) -By default, all ML Kit features are enabled. You can selectively include only the models you need to reduce binary size. +v2 implements two feature families: text recognition and barcode scanning. +Both are enabled by default, including all five OCR language models. Disable +OCR languages you do not need to reduce binary size. Configuration keys for +post-v2 roadmap features remain reserved and disabled; enabling one does not +provide a public JS/Nitro API in v2. + +On Android, the Latin OCR and barcode base SDKs remain compile-time +dependencies even when their feature flags are disabled because v2's native +implementation references their API types. Those flags control runtime +availability; the four optional OCR language modules are still omitted when +disabled. ### Android (Gradle) @@ -88,17 +100,7 @@ ext["react-native-vision-camera-mlkit"] = [ textRecognitionDevanagari: false, textRecognitionJapanese: false, textRecognitionKorean: false, - faceDetection: false, - faceMeshDetection: false, - poseDetection: false, - poseDetectionAccurate: false, - selfieSegmentation: false, - subjectSegmentation: false, - documentScanner: false, barcodeScanning: true, - imageLabeling: false, - objectDetection: false, - digitalInkRecognition: false, ] ] ``` @@ -114,19 +116,10 @@ $VisionCameraMLKit = { 'textRecognitionDevanagari' => false, 'textRecognitionJapanese' => false, 'textRecognitionKorean' => false, - 'faceDetection' => false, - 'poseDetection' => false, - 'poseDetectionAccurate' => false, - 'selfieSegmentation' => false, 'barcodeScanning' => true, - 'imageLabeling' => false, - 'objectDetection' => false, - 'digitalInkRecognition' => false, } ``` -Android-only keys: `faceMeshDetection`, `subjectSegmentation`, `documentScanner`. - ### Expo (config plugin) If you're using an Expo prebuild workflow (`expo prebuild`, EAS Build, or a @@ -143,8 +136,7 @@ you don't have to hand-edit native config. Add it to your `app.json`/ "react-native-vision-camera-mlkit", { "textRecognition": true, - "barcodeScanning": true, - "faceDetection": false + "barcodeScanning": true } ] ] @@ -157,12 +149,20 @@ sections above). Passing an unrecognized key throws at prebuild time instead of silently being ignored. Requires `@expo/config-plugins` (already a dependency of `expo` itself, so Expo projects have it for free). +Static-image processing accepts local files up to 25 MB encoded size. iOS +accepts sources up to 25 megapixels and 10,000 pixels on either dimension, then +downsamples them to at most 4 megapixels and 4,096 pixels per dimension during +ImageIO decode. Android currently accepts decoded dimensions up to 4 megapixels +and 4,096 pixels per dimension. Larger inputs are rejected before full decode +to protect application disk, memory, and CPU resources. + ## Usage ### API Docs - [Text Recognition API](docs/text-recognition.md) - [Barcode Scanning API](docs/barcode-scanning.md) +- [Migrating from v1 to v2](docs/migration-v1-to-v2.md) ### Text Recognition (Frame Output) @@ -211,7 +211,7 @@ const frameOutput = useFrameOutput({ - `scaleFactor?: number` (0.9-1.0) - `invertColors?: boolean` - `roi?: RegionOfInterest` (crop processing to a rectangle of the frame; see [Region of Interest](#region-of-interest)) -- `frameProcessInterval?: number` (deprecated, throttle frames manually inside `onFrame` instead) +- `frameProcessInterval?: number` (deprecated compatibility field; v2 does not use it to throttle processing) `TextRecognitionArguments`: @@ -219,7 +219,10 @@ const frameOutput = useFrameOutput({ ### Image Processing (Static Images) -Use `processImageTextRecognition` to analyze a file path or URI without the camera (for example, images picked from the gallery). +Use `processImageTextRecognition` to analyze a local image without the camera +(for example, an image picked from the gallery). Each call creates a Nitro text +recognizer configured with the supplied options, then processes the image +asynchronously. ```ts import { processImageTextRecognition } from 'react-native-vision-camera-mlkit'; @@ -236,11 +239,16 @@ console.log(result.blocks); `TextRecognitionImageOptions`: - `language?: 'LATIN' | 'CHINESE' | 'DEVANAGARI' | 'JAPANESE' | 'KOREAN'` -- `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` +- `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` (overrides EXIF orientation; omit it to use image metadata) - `invertColors?: boolean` +- `scaleFactor?: number` (0.9-1.0) - `roi?: RegionOfInterest` (crop processing to a rectangle of the image; see [Region of Interest](#region-of-interest)) -> The native bridge normalizes URIs (`file://` is removed on iOS and added on Android if missing). Supported formats: JPEG, PNG, WebP. +> Static images must be local and readable. On iOS, the target must be a +> regular, non-symlink file supplied as an absolute path or `file://` URI. +> Android accepts an absolute path, `file://` URI, or +> `content://` URI (copied to a temporary cache file for processing). Remote +> URLs and platform-library schemes such as `ph://` are not supported. ### Barcode Scanning (Frame Output) @@ -301,7 +309,7 @@ const frameOutput = useFrameOutput({ - `scaleFactor?: number` (0.9-1.0) - `invertColors?: boolean` - `roi?: RegionOfInterest` (crop processing to a rectangle of the frame; see [Region of Interest](#region-of-interest)) -- `frameProcessInterval?: number` (deprecated, throttle frames manually inside `onFrame` instead) +- `frameProcessInterval?: number` (deprecated compatibility field; v2 does not use it to throttle processing) Supported `formats` values: @@ -329,7 +337,9 @@ Supported `formats` values: ### Barcode Scanning (Static Images) -Use `processImageBarcodeScanning` to analyze a file path or URI without the camera. +Use `processImageBarcodeScanning` to analyze a local image without the camera. +Each call creates a Nitro barcode scanner configured with the supplied options, +then processes the image asynchronously. ```ts import { processImageBarcodeScanning } from 'react-native-vision-camera-mlkit'; @@ -351,7 +361,7 @@ for (const barcode of result.barcodes) { - `formats?: ('UNKNOWN' | 'ALL_FORMATS' | 'CODE_128' | 'CODE_39' | 'CODE_93' | 'CODABAR' | 'DATA_MATRIX' | 'EAN_13' | 'EAN_8' | 'ITF' | 'QR_CODE' | 'UPC_A' | 'UPC_E' | 'PDF417' | 'AZTEC')[]` - `enableAllPotentialBarcodes?: boolean` (Android only) -- `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` +- `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` (overrides EXIF orientation; omit it to use image metadata) - `invertColors?: boolean` - `scaleFactor?: number` (0.9-1.0) - `roi?: RegionOfInterest` (crop processing to a rectangle of the image; see [Region of Interest](#region-of-interest)) @@ -381,13 +391,20 @@ import { ### Error Handling -Frame processors throw a setup error when the feature is not enabled in Gradle/Podfile. For static image processing, the following error strings are exported: +Recognizer creation throws a setup error when OCR or barcode scanning is not +enabled in Gradle/Podfile. Static Nitro calls reject invalid schemes, missing +or unreadable files, and images the platform decoder cannot read. The package +also exports these compatibility error-message constants: - `IMAGE_NOT_FOUND_ERROR` - `INVALID_URI_ERROR` - `IMAGE_PROCESSING_FAILED_ERROR` - `UNSUPPORTED_IMAGE_FORMAT_ERROR` +These constants are retained for source compatibility and user-facing fallback +messages. Nitro rejection messages include platform-specific file details and +are not guaranteed to equal these strings; do not branch on `error.message`. + Use the feature helpers to provide user-friendly configuration hints: ```ts @@ -425,8 +442,8 @@ const { textRecognition } = useTextRecognition({ ## Performance - Follow the Vision Camera [performance guide](https://visioncamera.margelo.com/docs/performance) -- Always call `frame.dispose()` exactly once per frame inside `onFrame` - the Camera pipeline reuses a small buffer pool and stalls (dropping frames) if buffers aren't released. -- Throttle processing by skipping frames yourself inside `onFrame` (see the `frameProcessInterval` deprecation note above) instead of relying on a native-side interval. +- Always call `frame.dispose()` exactly once for every frame delivered to `onFrame`, including skipped frames. A `try`/`finally` around the whole callback is the safest pattern; unreleased v5 frames can stall the camera buffer pool. +- `frameProcessInterval` is retained only for source compatibility and has no throttling effect in the Nitro recognizers. Throttle in `onFrame` by deciding whether to call the recognizer while still disposing every frame. - `useAsyncRunner()` can offload genuinely independent heavy work to a separate thread, but avoid wrapping a single native plugin call in it - passing a plugin's worklet-callable method through nested worklet closures into a different Worklet Runtime is fragile and can throw at runtime. ## iOS Orientation Notes (Text Recognition) @@ -448,19 +465,22 @@ If your iOS build fails in Pods with errors referencing `fmt/include/fmt/base.h` ## Google ML Kit Vision Features Roadmap -| # | Feature | Status | Platform | -| --- | --------------------------------- | ------------------------------------------ | ------------------------------------------------- | -| 0 | **Text recognition v2** | [![complete][complete]][complete] | [![android][android]][android] [![ios][ios]][ios] | -| 1 | **Barcode scanning** | [![complete][complete]][complete] | [![android][android]][android] [![ios][ios]][ios] | -| 2 | **Face detection** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | -| 3 | **Face mesh detection** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] | -| 4 | **Pose detection** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | -| 5 | **Selfie segmentation** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | -| 6 | **Subject segmentation** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] | -| 7 | **Document scanner** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] | -| 8 | **Image labeling** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | -| 9 | **Object detection and tracking** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | -| 10 | **Digital ink recognition** | [![in-progress][in-progress]][in-progress] | [![android][android]][android] [![ios][ios]][ios] | +| Feature | Release status | Intended platform | +| --------------------------------- | ----------------- | ----------------- | +| **Text recognition v2** | Available in v2 | Android and iOS | +| **Barcode scanning** | Available in v2 | Android and iOS | +| **Face detection** | Post-v2 roadmap | Android and iOS | +| **Face mesh detection** | Post-v2 roadmap | Android | +| **Pose detection** | Post-v2 roadmap | Android and iOS | +| **Selfie segmentation** | Post-v2 roadmap | Android and iOS | +| **Subject segmentation** | Post-v2 roadmap | Android | +| **Document scanner** | Post-v2 roadmap | Android | +| **Image labeling** | Post-v2 roadmap | Android and iOS | +| **Object detection and tracking** | Post-v2 roadmap | Android and iOS | +| **Digital ink recognition** | Post-v2 roadmap | Android and iOS | + +Roadmap entries are not implemented public APIs and are not commitments to a +particular release. ## Sponsor on GitHub @@ -470,10 +490,6 @@ react-native-vision-camera-mlkit is provided as is and maintained in my free tim If you’re integrating this library into a production app, consider funding the project. -[complete]: https://img.shields.io/badge/COMPLETE-5E5CE6 -[in-progress]: https://img.shields.io/badge/IN%20PROGRESS-FFD60A -[android]: https://img.shields.io/badge/ANDROID-3DDC84 -[ios]: https://img.shields.io/badge/IOS-0A84FF [contributors-shield]: https://img.shields.io/github/contributors/pedrol2b/react-native-vision-camera-mlkit.svg?style=for-the-badge [contributors-url]: https://github.com/pedrol2b/react-native-vision-camera-mlkit/graphs/contributors [forks-shield]: https://img.shields.io/github/forks/pedrol2b/react-native-vision-camera-mlkit.svg?style=for-the-badge diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index f71a5c6..d229575 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -34,7 +34,6 @@ target_sources( find_library(LOG_LIB log) find_package(fbjni REQUIRED) find_package(ReactAndroid REQUIRED) -find_package(react-native-nitro-image REQUIRED) find_package(react-native-nitro-modules REQUIRED) find_package(react-native-vision-camera REQUIRED) @@ -44,7 +43,6 @@ target_link_libraries( android fbjni::fbjni ReactAndroid::jsi - react-native-nitro-image::NitroImage react-native-nitro-modules::NitroModules react-native-vision-camera::VisionCamera ) diff --git a/android/build.gradle b/android/build.gradle index 5dccb88..070c506 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -188,15 +188,14 @@ dependencies { implementation "androidx.exifinterface:exifinterface:1.3.7" // ===== GoogleMLKit Vision ===== - // Only include dependencies for enabled features + // Base SDKs for the two v2 features are compile-time dependencies because + // their public types are referenced by unconditional Kotlin sources. Feature + // flags still control runtime availability; optional OCR language models stay + // selective so consumers can trim those larger model artifacts. // Text recognition v2 // https://developers.google.com/ml-kit/vision/text-recognition/v2/android - // Base text-recognition is required whenever any text recognition variant is enabled, - // as it provides shared types (TextRecognizer, TextRecognizerOptions, etc.) - if (anyTextRecognition) { - implementation "com.google.mlkit:text-recognition:16.0.1" - } + implementation "com.google.mlkit:text-recognition:16.0.1" if (mlkitConfig.textRecognitionChinese) { implementation "com.google.mlkit:text-recognition-chinese:16.0.1" } @@ -251,9 +250,7 @@ dependencies { // Barcode scanning // https://developers.google.com/ml-kit/vision/barcode-scanning/android - if (mlkitConfig.barcodeScanning) { - implementation "com.google.mlkit:barcode-scanning:17.3.0" - } + implementation "com.google.mlkit:barcode-scanning:17.3.0" // Image labeling (Base Model) -> Custom models will be added soon // https://developers.google.com/ml-kit/vision/image-labeling/android diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeParsedValue+toNitroAnyMap.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeParsedValue+toNitroAnyMap.kt new file mode 100644 index 0000000..ab2479f --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeParsedValue+toNitroAnyMap.kt @@ -0,0 +1,13 @@ +package com.margelo.nitro.visioncameramlkit + +import com.margelo.nitro.core.AnyMap +import com.visioncameramlkit.domain.models.BarcodeParsedValue + +internal fun BarcodeParsedValue.toNitroAnyMap(): AnyMap = + AnyMap.fromMap( + mapOf( + "type" to type, + "data" to data, + ), + ignoreIncompatible = false, + ) diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions+toDomain.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions+toDomain.kt index 0e1d027..9958ec1 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions+toDomain.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions+toDomain.kt @@ -16,4 +16,5 @@ internal fun BarcodeScannerOptions.toImagePreprocessingOptions(): ImagePreproces invertColors = invertColors ?: false, scaleFactor = scaleFactor?.toFloat() ?: 1.0f, roi = roi.toDomainRegionOfInterest(), + orientation = orientation.toDomainOrientation(), ) diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScanningResult+toNitro.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScanningResult+toNitro.kt index 40717fe..87d2e41 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScanningResult+toNitro.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/BarcodeScanningResult+toNitro.kt @@ -16,11 +16,11 @@ private fun com.visioncameramlkit.domain.models.BarcodeData.toNitroBarcode(): Ba rawValue = rawValue, displayValue = displayValue, rawBytes = rawBytes?.map { it.toDouble() }?.toDoubleArray(), + value = value?.toNitroAnyMap(), isPotential = isPotential, ) -private fun String.toNitroBarcodeFormat(): BarcodeFormat = - runCatching { BarcodeFormat.valueOf(this) }.getOrDefault(BarcodeFormat.UNKNOWN) +private fun String.toNitroBarcodeFormat(): BarcodeFormat = runCatching { BarcodeFormat.valueOf(this) }.getOrDefault(BarcodeFormat.UNKNOWN) private fun String.toNitroBarcodeValueTypeName(): BarcodeValueTypeName = runCatching { BarcodeValueTypeName.valueOf(this) }.getOrDefault(BarcodeValueTypeName.TYPE_UNKNOWN) diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt index 5b72857..b3c19aa 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridBarcodeScanner.kt @@ -1,11 +1,14 @@ package com.margelo.nitro.visioncameramlkit +import android.util.Log import androidx.annotation.Keep import com.facebook.proguard.annotations.DoNotStrip import com.margelo.nitro.camera.HybridFrameSpec +import com.margelo.nitro.core.Promise import com.visioncameramlkit.domain.models.remap import com.visioncameramlkit.infrastructure.image.ImagePreprocessor import com.visioncameramlkit.infrastructure.mlkit.factories.BarcodeScanningServiceFactory +import java.util.concurrent.atomic.AtomicBoolean @DoNotStrip @Keep @@ -16,6 +19,7 @@ class HybridBarcodeScanner( private val barcodeOptions = options.toDomainBarcodeScanningOptions() private val imageOptions = options.toImagePreprocessingOptions() private val recognitionService = BarcodeScanningServiceFactory.create(barcodeOptions) + private val isDisposed = AtomicBoolean(false) override fun recognize( frame: HybridFrameSpec, @@ -25,4 +29,21 @@ class HybridBarcodeScanner( val result = recognitionService.recognize(processedImage) return result.remap(processedImage.metadata).toNitroResult() } + + override fun recognizeImage(uri: String): Promise = + Promise.parallel { + StaticImageUriResolver.resolve(uri).use { resolvedImage -> + val processedImage = imagePreprocessor.preprocessImage(resolvedImage.file, imageOptions) + val result = recognitionService.recognize(processedImage) + result.remap(processedImage.metadata).toNitroResult() + } + } + + override fun dispose() { + if (isDisposed.compareAndSet(false, true)) { + runCatching { recognitionService.close() } + .onFailure { error -> Log.w(TAG, "Failed to close barcode scanner", error) } + } + super.dispose() + } } diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt index 83c9653..bf281f8 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/HybridTextRecognizer.kt @@ -1,11 +1,14 @@ package com.margelo.nitro.visioncameramlkit +import android.util.Log import androidx.annotation.Keep import com.facebook.proguard.annotations.DoNotStrip import com.margelo.nitro.camera.HybridFrameSpec +import com.margelo.nitro.core.Promise import com.visioncameramlkit.domain.models.remap import com.visioncameramlkit.infrastructure.image.ImagePreprocessor import com.visioncameramlkit.infrastructure.mlkit.factories.TextRecognitionServiceFactory +import java.util.concurrent.atomic.AtomicBoolean @DoNotStrip @Keep @@ -16,6 +19,7 @@ class HybridTextRecognizer( private val textOptions = options.toDomainTextRecognitionOptions() private val imageOptions = options.toImagePreprocessingOptions() private val recognitionService = TextRecognitionServiceFactory.create(textOptions.language) + private val isDisposed = AtomicBoolean(false) override fun recognize( frame: HybridFrameSpec, @@ -25,4 +29,21 @@ class HybridTextRecognizer( val result = recognitionService.recognize(processedImage) return result.remap(processedImage.metadata).toNitroResult() } + + override fun recognizeImage(uri: String): Promise = + Promise.parallel { + StaticImageUriResolver.resolve(uri).use { resolvedImage -> + val processedImage = imagePreprocessor.preprocessImage(resolvedImage.file, imageOptions) + val result = recognitionService.recognize(processedImage) + result.remap(processedImage.metadata).toNitroResult() + } + } + + override fun dispose() { + if (isDisposed.compareAndSet(false, true)) { + runCatching { recognitionService.close() } + .onFailure { error -> Log.w(TAG, "Failed to close text recognizer", error) } + } + super.dispose() + } } diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/Orientation+toDomain.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/Orientation+toDomain.kt new file mode 100644 index 0000000..3b16a80 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/Orientation+toDomain.kt @@ -0,0 +1,12 @@ +package com.margelo.nitro.visioncameramlkit + +import com.visioncameramlkit.domain.models.Orientation as DomainOrientation + +internal fun Orientation?.toDomainOrientation(): DomainOrientation? = + when (this) { + Orientation.PORTRAIT -> DomainOrientation.PORTRAIT + Orientation.PORTRAIT_UPSIDE_DOWN -> DomainOrientation.PORTRAIT_UPSIDE_DOWN + Orientation.LANDSCAPE_LEFT -> DomainOrientation.LANDSCAPE_LEFT + Orientation.LANDSCAPE_RIGHT -> DomainOrientation.LANDSCAPE_RIGHT + null -> null + } diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/ResolvedStaticImage.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/ResolvedStaticImage.kt new file mode 100644 index 0000000..a5f4517 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/ResolvedStaticImage.kt @@ -0,0 +1,16 @@ +package com.margelo.nitro.visioncameramlkit + +import java.io.Closeable +import java.io.File +import java.io.IOException + +internal class ResolvedStaticImage( + val file: File, + private val deleteOnClose: Boolean, +) : Closeable { + override fun close() { + if (deleteOnClose && file.exists() && !file.delete()) { + throw IOException("Failed to delete temporary image file.") + } + } +} diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/StaticImageUriResolver.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/StaticImageUriResolver.kt new file mode 100644 index 0000000..227ebb0 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/StaticImageUriResolver.kt @@ -0,0 +1,90 @@ +package com.margelo.nitro.visioncameramlkit + +import android.net.Uri +import com.margelo.nitro.NitroModules +import com.visioncameramlkit.domain.models.StaticImageLimits +import java.io.File +import java.io.FileNotFoundException +import java.io.IOException + +internal object StaticImageUriResolver { + fun resolve(value: String): ResolvedStaticImage { + require(value.isNotBlank()) { "Image URI must not be blank." } + + val uri = Uri.parse(value) + val scheme = uri.scheme + return when { + scheme == null -> resolveAbsolutePath(value) + scheme.equals("content", ignoreCase = true) -> resolveContentUri(uri) + scheme.equals("file", ignoreCase = true) -> resolveFileUri(uri, value) + else -> throw IllegalArgumentException("Unsupported image URI scheme: $scheme") + } + } + + private fun resolveContentUri(uri: Uri): ResolvedStaticImage { + val context = + NitroModules.applicationContext + ?: throw IllegalStateException("NitroModules application context is unavailable.") + val temporaryFile = File.createTempFile("vision-camera-mlkit-", ".image", context.cacheDir) + val resolvedImage = ResolvedStaticImage(temporaryFile, deleteOnClose = true) + + try { + val input = + context.contentResolver.openInputStream(uri) + ?: throw FileNotFoundException("Unable to open content URI: $uri") + input.use { source -> + temporaryFile.outputStream().use { destination -> + val buffer = ByteArray(DEFAULT_BUFFER_SIZE) + var copiedBytes = 0L + while (true) { + val bytesRead = source.read(buffer) + if (bytesRead < 0) break + copiedBytes += bytesRead + require(copiedBytes <= StaticImageLimits.MAX_ENCODED_BYTES) { + "Static image exceeds the 25 MB encoded-size limit." + } + destination.write(buffer, 0, bytesRead) + } + } + } + return resolvedImage + } catch (error: Throwable) { + try { + resolvedImage.close() + } catch (cleanupError: Throwable) { + error.addSuppressed(cleanupError) + } + throw error + } + } + + private fun resolveFileUri( + uri: Uri, + originalValue: String, + ): ResolvedStaticImage { + val path = uri.path ?: throw IllegalArgumentException("File URI has no path: $originalValue") + return resolveExistingFile(File(path), originalValue) + } + + private fun resolveAbsolutePath(value: String): ResolvedStaticImage { + val file = File(value) + require(file.isAbsolute) { "Image path must be absolute: $value" } + return resolveExistingFile(file, value) + } + + private fun resolveExistingFile( + file: File, + originalValue: String, + ): ResolvedStaticImage { + if (!file.isFile) { + throw FileNotFoundException("Image file not found: $originalValue") + } + if (!file.canRead()) { + throw IOException("Image file is not readable: $originalValue") + } + require(file.length() <= StaticImageLimits.MAX_ENCODED_BYTES) { + "Static image exceeds the 25 MB encoded-size limit." + } + return ResolvedStaticImage(file, deleteOnClose = false) + } +} diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognitionLanguage+toDomain.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognitionLanguage+toDomain.kt index d3a0b31..e2e4776 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognitionLanguage+toDomain.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognitionLanguage+toDomain.kt @@ -3,9 +3,13 @@ package com.margelo.nitro.visioncameramlkit internal fun TextRecognitionLanguage?.toDomainLanguage(): com.visioncameramlkit.domain.models.TextRecognitionLanguage = when (this) { TextRecognitionLanguage.CHINESE -> com.visioncameramlkit.domain.models.TextRecognitionLanguage.CHINESE + TextRecognitionLanguage.DEVANAGARI -> com.visioncameramlkit.domain.models.TextRecognitionLanguage.DEVANAGARI + TextRecognitionLanguage.JAPANESE -> com.visioncameramlkit.domain.models.TextRecognitionLanguage.JAPANESE + TextRecognitionLanguage.KOREAN -> com.visioncameramlkit.domain.models.TextRecognitionLanguage.KOREAN + TextRecognitionLanguage.LATIN, null, -> com.visioncameramlkit.domain.models.TextRecognitionLanguage.LATIN diff --git a/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions+toDomain.kt b/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions+toDomain.kt index fdea701..4b1b172 100644 --- a/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions+toDomain.kt +++ b/android/src/main/java/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions+toDomain.kt @@ -15,4 +15,5 @@ internal fun TextRecognizerOptions.toImagePreprocessingOptions(): ImagePreproces invertColors = invertColors ?: false, scaleFactor = scaleFactor?.toFloat() ?: 1.0f, roi = roi.toDomainRegionOfInterest(), + orientation = orientation.toDomainOrientation(), ) diff --git a/android/src/main/java/com/visioncameramlkit/VisionCameraMLKitPackage.kt b/android/src/main/java/com/visioncameramlkit/VisionCameraMLKitPackage.kt index 40ffb2b..16b0765 100644 --- a/android/src/main/java/com/visioncameramlkit/VisionCameraMLKitPackage.kt +++ b/android/src/main/java/com/visioncameramlkit/VisionCameraMLKitPackage.kt @@ -7,36 +7,16 @@ import com.facebook.react.module.model.ReactModuleInfo import com.facebook.react.module.model.ReactModuleInfoProvider import com.facebook.react.uimanager.ViewManager import com.margelo.nitro.visioncameramlkit.VisionCameraMLKitOnLoad -import com.visioncameramlkit.infrastructure.modules.VisionCameraMLKitModule class VisionCameraMLKitPackage : BaseReactPackage() { override fun getModule( name: String, reactContext: ReactApplicationContext, - ): NativeModule? = - when (name) { - VisionCameraMLKitModule.NAME -> { - VisionCameraMLKitModule(reactContext) - } - - else -> { - null - } - } + ): NativeModule? = null override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider { - mapOf( - VisionCameraMLKitModule.NAME to - ReactModuleInfo( - VisionCameraMLKitModule.NAME, - VisionCameraMLKitModule.NAME, - canOverrideExistingModule = false, - needsEagerInit = false, - isCxxModule = false, - isTurboModule = false, - ), - ) + emptyMap() } override fun createViewManagers(reactContext: ReactApplicationContext): List> = emptyList() diff --git a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt b/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt deleted file mode 100644 index d03f17a..0000000 --- a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeBarcodesUseCase.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.visioncameramlkit.application.usecases - -import com.visioncameramlkit.domain.models.BarcodeScanningOptions -import com.visioncameramlkit.domain.models.BarcodeScanningResult -import com.visioncameramlkit.domain.models.ImagePreprocessingOptions -import com.visioncameramlkit.domain.models.remap -import com.visioncameramlkit.domain.services.IImagePreprocessor -import com.visioncameramlkit.domain.services.IRecognitionService -import java.io.File - -class RecognizeBarcodesUseCase( - private val imagePreprocessor: IImagePreprocessor, - private val recognitionService: IRecognitionService, -) { - fun execute( - imageFile: File, - imageOptions: ImagePreprocessingOptions, - @Suppress("unused") barcodeOptions: BarcodeScanningOptions, - ): BarcodeScanningResult { - val preprocessingOptions = - ImagePreprocessingOptions( - invertColors = imageOptions.invertColors, - orientation = imageOptions.orientation, - scaleFactor = imageOptions.scaleFactor, - roi = imageOptions.roi, - ) - - val processedImage = imagePreprocessor.preprocessImage(imageFile, preprocessingOptions) - val result = recognitionService.recognize(processedImage) - return result.remap(processedImage.metadata) - } -} diff --git a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeTextUseCase.kt b/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeTextUseCase.kt deleted file mode 100644 index 46883cd..0000000 --- a/android/src/main/java/com/visioncameramlkit/application/usecases/RecognizeTextUseCase.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.visioncameramlkit.application.usecases - -import com.visioncameramlkit.domain.models.ImagePreprocessingOptions -import com.visioncameramlkit.domain.models.TextRecognitionOptions -import com.visioncameramlkit.domain.models.TextRecognitionResult -import com.visioncameramlkit.domain.models.remap -import com.visioncameramlkit.domain.services.IImagePreprocessor -import com.visioncameramlkit.domain.services.IRecognitionService -import java.io.File - -class RecognizeTextUseCase( - private val imagePreprocessor: IImagePreprocessor, - private val recognitionService: IRecognitionService, -) { - fun execute( - imageFile: File, - imageOptions: ImagePreprocessingOptions, - @Suppress("unused") textOptions: TextRecognitionOptions, - ): TextRecognitionResult { - val preprocessingOptions = - ImagePreprocessingOptions( - invertColors = imageOptions.invertColors, - orientation = imageOptions.orientation, - scaleFactor = imageOptions.scaleFactor, - roi = imageOptions.roi, - ) - - val processedImage = imagePreprocessor.preprocessImage(imageFile, preprocessingOptions) - val result = recognitionService.recognize(processedImage) - return result.remap(processedImage.metadata) - } -} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/constants/MLKitFeatureKeys.kt b/android/src/main/java/com/visioncameramlkit/bridge/constants/MLKitFeatureKeys.kt deleted file mode 100644 index 79ff40b..0000000 --- a/android/src/main/java/com/visioncameramlkit/bridge/constants/MLKitFeatureKeys.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.visioncameramlkit.bridge.constants - -@Suppress("unused") -object MLKitFeatureKeys { - const val TEXT_RECOGNITION = "TextRecognition" - const val FACE_DETECTION = "FaceDetection" - const val FACE_MESH_DETECTION = "FaceMeshDetection" - const val POSE_DETECTION = "PoseDetection" - const val SELFIE_SEGMENTATION = "SelfieSegmentation" - const val SUBJECT_SEGMENTATION = "SubjectSegmentation" - const val DOCUMENT_SCANNER = "DocumentScanner" - const val BARCODE_SCANNING = "BarcodeScanning" - const val IMAGE_LABELING = "ImageLabeling" - const val OBJECT_DETECTION = "ObjectDetection" - const val DIGITAL_INK_RECOGNITION = "DigitalInkRecognition" -} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/handlers/IStaticImageHandler.kt b/android/src/main/java/com/visioncameramlkit/bridge/handlers/IStaticImageHandler.kt deleted file mode 100644 index 8b52d93..0000000 --- a/android/src/main/java/com/visioncameramlkit/bridge/handlers/IStaticImageHandler.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.visioncameramlkit.bridge.handlers - -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReadableMap - -interface IStaticImageHandler { - fun process( - path: String, - options: ReadableMap, - promise: Promise, - ) -} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticBarcodeScanningHandler.kt b/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticBarcodeScanningHandler.kt deleted file mode 100644 index 9fe891a..0000000 --- a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticBarcodeScanningHandler.kt +++ /dev/null @@ -1,171 +0,0 @@ -package com.visioncameramlkit.bridge.handlers - -import android.net.Uri -import androidx.core.net.toUri -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReadableMap -import com.visioncameramlkit.application.usecases.RecognizeBarcodesUseCase -import com.visioncameramlkit.bridge.parsers.BarcodeScanningOptionParser -import com.visioncameramlkit.bridge.parsers.RegionOfInterestOptionParser -import com.visioncameramlkit.domain.models.BarcodeScanningOptions -import com.visioncameramlkit.domain.models.ImagePreprocessingOptions -import com.visioncameramlkit.domain.models.Orientation -import com.visioncameramlkit.infrastructure.image.ImagePreprocessor -import com.visioncameramlkit.infrastructure.mlkit.factories.BarcodeScanningServiceFactory -import com.visioncameramlkit.infrastructure.serializers.BarcodeScanningSerializer -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import java.io.File -import java.io.FileOutputStream - -class StaticBarcodeScanningHandler( - private val reactContext: ReactApplicationContext, -) : IStaticImageHandler { - private val cachedUseCases = mutableMapOf() - - private fun getRecognizeBarcodesUseCase(options: BarcodeScanningOptions): RecognizeBarcodesUseCase { - val cacheKey = - buildString { - append(options.enableAllPotentialBarcodes) - append("|") - append(options.formats.joinToString(separator = ",") { it.name }) - } - - return cachedUseCases.getOrPut(cacheKey) { - RecognizeBarcodesUseCase( - ImagePreprocessor(), - BarcodeScanningServiceFactory.create(options), - ) - } - } - - override fun process( - path: String, - options: ReadableMap, - promise: Promise, - ) { - CoroutineScope(Dispatchers.IO).launch { - var resolvedImage: ResolvedImage? = null - try { - resolvedImage = resolveImage(path) - val imageFile = resolvedImage.file - if (!imageFile.exists()) { - promise.reject("IMAGE_NOT_FOUND_ERROR", "Image file not found at path: $path") - return@launch - } - - val imageOptions = parseImageOptions(options) - val barcodeOptions = parseBarcodeOptions(options) - val recognizeBarcodesUseCase = getRecognizeBarcodesUseCase(barcodeOptions) - - val result = recognizeBarcodesUseCase.execute(imageFile, imageOptions, barcodeOptions) - val serializedResult = BarcodeScanningSerializer.toWritableMap(result) - - promise.resolve(serializedResult) - } catch ( - @Suppress("TooGenericExceptionCaught") e: Exception, - ) { - when (e) { - is IllegalArgumentException -> { - promise.reject("INVALID_URI_ERROR", e.message) - } - - is UnsupportedOperationException -> { - promise.reject( - "UNSUPPORTED_IMAGE_FORMAT_ERROR", - e.message, - ) - } - - else -> { - promise.reject( - "IMAGE_PROCESSING_FAILED_ERROR", - e.message ?: "Barcode scanning failed", - ) - } - } - } finally { - val imageFile = resolvedImage?.file - if (resolvedImage?.shouldDelete == true && imageFile != null && imageFile.exists()) { - imageFile.delete() - } - } - } - } - - private data class ResolvedImage( - val file: File, - val shouldDelete: Boolean, - ) - - private fun resolveImage(path: String): ResolvedImage { - val uri = path.toUri() - return when (uri.scheme) { - "content" -> ResolvedImage(copyContentToCache(uri), true) - "file" -> ResolvedImage(File(uri.path ?: path), false) - null -> ResolvedImage(File(path), false) - else -> ResolvedImage(File(path), false) - } - } - - private fun copyContentToCache(uri: Uri): File { - val cacheFile = File(reactContext.cacheDir, "mlkit_${System.currentTimeMillis()}.tmp") - val inputStream = - reactContext.contentResolver.openInputStream(uri) - ?: throw IllegalArgumentException("Unable to open content URI: $uri") - inputStream.use { input -> - FileOutputStream(cacheFile).use { output -> - input.copyTo(output) - } - } - return cacheFile - } - - private fun parseImageOptions(options: ReadableMap): ImagePreprocessingOptions = - ImagePreprocessingOptions( - invertColors = options.getOrDefault("invertColors", false), - orientation = options.getString("orientation")?.let { parseOrientation(it) }, - scaleFactor = options.getOrDefault("scaleFactor", 1.0f), - roi = RegionOfInterestOptionParser.parse(options), - ) - - private fun parseBarcodeOptions(options: ReadableMap): BarcodeScanningOptions = - BarcodeScanningOptions( - formats = BarcodeScanningOptionParser.parseFormats(options), - enableAllPotentialBarcodes = - BarcodeScanningOptionParser.parseEnableAllPotentialBarcodes( - options, - ), - ) - - private fun parseOrientation(orientation: String): Orientation? = - when (orientation) { - "portrait" -> Orientation.PORTRAIT - "portrait-upside-down" -> Orientation.PORTRAIT_UPSIDE_DOWN - "landscape-left" -> Orientation.LANDSCAPE_LEFT - "landscape-right" -> Orientation.LANDSCAPE_RIGHT - else -> null - } - - private fun ReadableMap.getOrDefault( - key: String, - defaultValue: Boolean, - ): Boolean { - if (!hasKey(key) || isNull(key)) { - return defaultValue - } - return getBoolean(key) - } - - private fun ReadableMap.getOrDefault( - key: String, - defaultValue: Float, - ): Float { - if (!hasKey(key) || isNull(key)) { - return defaultValue - } - return getDouble(key).toFloat() - } -} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt b/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt deleted file mode 100644 index 14ff378..0000000 --- a/android/src/main/java/com/visioncameramlkit/bridge/handlers/StaticTextRecognitionHandler.kt +++ /dev/null @@ -1,159 +0,0 @@ -package com.visioncameramlkit.bridge.handlers - -import android.net.Uri -import androidx.core.net.toUri -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReadableMap -import com.visioncameramlkit.application.usecases.RecognizeTextUseCase -import com.visioncameramlkit.bridge.parsers.RegionOfInterestOptionParser -import com.visioncameramlkit.domain.models.ImagePreprocessingOptions -import com.visioncameramlkit.domain.models.Orientation -import com.visioncameramlkit.domain.models.TextRecognitionLanguage -import com.visioncameramlkit.domain.models.TextRecognitionOptions -import com.visioncameramlkit.infrastructure.image.ImagePreprocessor -import com.visioncameramlkit.infrastructure.mlkit.factories.TextRecognitionServiceFactory -import com.visioncameramlkit.infrastructure.serializers.TextRecognitionSerializer -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import java.io.File -import java.io.FileOutputStream - -class StaticTextRecognitionHandler( - private val reactContext: ReactApplicationContext, -) : IStaticImageHandler { - private var cachedUseCase: RecognizeTextUseCase? = null - - private fun getRecognizeTextUseCase(language: TextRecognitionLanguage): RecognizeTextUseCase { - if (cachedUseCase != null) { - return cachedUseCase!! - } - val recognitionService = TextRecognitionServiceFactory.create(language) - val newUseCase = - RecognizeTextUseCase( - ImagePreprocessor(), - recognitionService, - ) - cachedUseCase = newUseCase - return newUseCase - } - - override fun process( - path: String, - options: ReadableMap, - promise: Promise, - ) { - CoroutineScope(Dispatchers.IO).launch { - var resolvedImage: ResolvedImage? = null - try { - resolvedImage = resolveImage(path) - val imageFile = resolvedImage.file - if (!imageFile.exists()) { - promise.reject("IMAGE_NOT_FOUND_ERROR", "Image file not found at path: $path") - return@launch - } - - val imageOptions = parseImageOptions(options) - - val languageString = options.getString("language") ?: "LATIN" - val language = parseLanguage(languageString) - val textOptions = TextRecognitionOptions(language = language) - - val recognizeTextUseCase = getRecognizeTextUseCase(language) - - val result = - recognizeTextUseCase.execute( - imageFile, - imageOptions, - textOptions, - ) - - val serializedResult = TextRecognitionSerializer.toWritableMap(result) - - promise.resolve(serializedResult) - } catch ( - @Suppress("TooGenericExceptionCaught") e: Exception, - ) { - when (e) { - is IllegalArgumentException -> { - promise.reject("INVALID_URI_ERROR", e.message) - } - - is UnsupportedOperationException -> { - promise.reject( - "UNSUPPORTED_IMAGE_FORMAT_ERROR", - e.message, - ) - } - - else -> { - promise.reject( - "IMAGE_PROCESSING_FAILED_ERROR", - e.message ?: "Text recognition failed", - ) - } - } - } finally { - val imageFile = resolvedImage?.file - if (resolvedImage?.shouldDelete == true && imageFile != null && imageFile.exists()) { - imageFile.delete() - } - } - } - } - - private data class ResolvedImage( - val file: File, - val shouldDelete: Boolean, - ) - - private fun resolveImage(path: String): ResolvedImage { - val uri = path.toUri() - return when (uri.scheme) { - "content" -> ResolvedImage(copyContentToCache(uri), true) - "file" -> ResolvedImage(File(uri.path ?: path), false) - null -> ResolvedImage(File(path), false) - else -> ResolvedImage(File(path), false) - } - } - - private fun copyContentToCache(uri: Uri): File { - val cacheFile = File(reactContext.cacheDir, "mlkit_${System.currentTimeMillis()}.tmp") - val inputStream = - reactContext.contentResolver.openInputStream(uri) - ?: throw IllegalArgumentException("Unable to open content URI: $uri") - inputStream.use { input -> - FileOutputStream(cacheFile).use { output -> - input.copyTo(output) - } - } - return cacheFile - } - - private fun parseImageOptions(options: ReadableMap): ImagePreprocessingOptions = - ImagePreprocessingOptions( - invertColors = options.getBoolean("invertColors"), - orientation = options.getString("orientation")?.let { parseOrientation(it) }, - roi = RegionOfInterestOptionParser.parse(options), - ) - - private fun parseOrientation(orientation: String): Orientation? = - when (orientation) { - "portrait" -> Orientation.PORTRAIT - "portrait-upside-down" -> Orientation.PORTRAIT_UPSIDE_DOWN - "landscape-left" -> Orientation.LANDSCAPE_LEFT - "landscape-right" -> Orientation.LANDSCAPE_RIGHT - else -> null - } - - private fun parseLanguage(language: String): TextRecognitionLanguage = - when (language) { - "LATIN" -> TextRecognitionLanguage.LATIN - "CHINESE" -> TextRecognitionLanguage.CHINESE - "DEVANAGARI" -> TextRecognitionLanguage.DEVANAGARI - "JAPANESE" -> TextRecognitionLanguage.JAPANESE - "KOREAN" -> TextRecognitionLanguage.KOREAN - else -> TextRecognitionLanguage.LATIN - } -} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/parsers/BarcodeScanningOptionParser.kt b/android/src/main/java/com/visioncameramlkit/bridge/parsers/BarcodeScanningOptionParser.kt deleted file mode 100644 index 61c5912..0000000 --- a/android/src/main/java/com/visioncameramlkit/bridge/parsers/BarcodeScanningOptionParser.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.visioncameramlkit.bridge.parsers - -import com.facebook.react.bridge.ReadableArray -import com.facebook.react.bridge.ReadableMap -import com.visioncameramlkit.domain.models.BarcodeFormatOption - -object BarcodeScanningOptionParser { - fun parseFormats(value: Any?): List = - (value as? List<*>) - ?.mapNotNull { item -> parseFormat(item as? String) } - ?: emptyList() - - @Suppress("ReturnCount") - fun parseFormats(readableMap: ReadableMap): List { - if (!readableMap.hasKey("formats") || readableMap.isNull("formats")) { - return emptyList() - } - val readableArray: ReadableArray = readableMap.getArray("formats") ?: return emptyList() - val parsed = mutableListOf() - for (index in 0 until readableArray.size()) { - val formatString = readableArray.getString(index) - parseFormat(formatString)?.let(parsed::add) - } - return parsed - } - - fun parseEnableAllPotentialBarcodes(value: Any?): Boolean = value as? Boolean ?: false - - fun parseEnableAllPotentialBarcodes(readableMap: ReadableMap): Boolean { - if (!readableMap.hasKey("enableAllPotentialBarcodes") || readableMap.isNull("enableAllPotentialBarcodes")) { - return false - } - return readableMap.getBoolean("enableAllPotentialBarcodes") - } - - private fun parseFormat(format: String?): BarcodeFormatOption? = - format - ?.trim() - ?.takeIf { it.isNotEmpty() } - ?.let { name -> runCatching { BarcodeFormatOption.valueOf(name) }.getOrNull() } -} diff --git a/android/src/main/java/com/visioncameramlkit/bridge/parsers/RegionOfInterestOptionParser.kt b/android/src/main/java/com/visioncameramlkit/bridge/parsers/RegionOfInterestOptionParser.kt deleted file mode 100644 index 68303ce..0000000 --- a/android/src/main/java/com/visioncameramlkit/bridge/parsers/RegionOfInterestOptionParser.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.visioncameramlkit.bridge.parsers - -import com.facebook.react.bridge.ReadableMap -import com.visioncameramlkit.domain.models.RegionOfInterest -import com.visioncameramlkit.domain.models.RegionOfInterestUnit - -object RegionOfInterestOptionParser { - /** - * Parses an optional `roi` key from [readableMap] into a domain [RegionOfInterest]. - * Returns `null` when the key is absent, matching the "no ROI" default. - */ - fun parse(readableMap: ReadableMap): RegionOfInterest? { - if (!readableMap.hasKey("roi") || readableMap.isNull("roi")) { - return null - } - val roiMap = readableMap.getMap("roi") ?: return null - - return RegionOfInterest( - x = roiMap.getDouble("x").toFloat(), - y = roiMap.getDouble("y").toFloat(), - width = roiMap.getDouble("width").toFloat(), - height = roiMap.getDouble("height").toFloat(), - unit = parseUnit(roiMap.getString("unit")), - ) - } - - private fun parseUnit(unit: String?): RegionOfInterestUnit = - when (unit) { - "pixel" -> RegionOfInterestUnit.PIXEL - else -> RegionOfInterestUnit.NORMALIZED - } -} diff --git a/android/src/main/java/com/visioncameramlkit/domain/models/StaticImageLimits.kt b/android/src/main/java/com/visioncameramlkit/domain/models/StaticImageLimits.kt new file mode 100644 index 0000000..e9eb53e --- /dev/null +++ b/android/src/main/java/com/visioncameramlkit/domain/models/StaticImageLimits.kt @@ -0,0 +1,7 @@ +package com.visioncameramlkit.domain.models + +object StaticImageLimits { + const val MAX_ENCODED_BYTES: Long = 25L * 1024L * 1024L + const val MAX_PIXEL_COUNT: Long = 4_000_000L + const val MAX_DIMENSION: Int = 4_096 +} diff --git a/android/src/main/java/com/visioncameramlkit/domain/services/IRecognitionService.kt b/android/src/main/java/com/visioncameramlkit/domain/services/IRecognitionService.kt index 9f7f406..18b5cbf 100644 --- a/android/src/main/java/com/visioncameramlkit/domain/services/IRecognitionService.kt +++ b/android/src/main/java/com/visioncameramlkit/domain/services/IRecognitionService.kt @@ -2,6 +2,6 @@ package com.visioncameramlkit.domain.services import com.visioncameramlkit.domain.models.ProcessedImage -interface IRecognitionService { +interface IRecognitionService : AutoCloseable { fun recognize(image: ProcessedImage): T } diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt index 070a9b4..231a944 100644 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/image/ImagePreprocessor.kt @@ -17,6 +17,7 @@ import com.visioncameramlkit.domain.models.ImagePreprocessingOptions import com.visioncameramlkit.domain.models.Orientation import com.visioncameramlkit.domain.models.ProcessedImage import com.visioncameramlkit.domain.models.RegionOfInterest +import com.visioncameramlkit.domain.models.StaticImageLimits import com.visioncameramlkit.domain.models.resolveToPixelRect import com.visioncameramlkit.domain.services.IImagePreprocessor import java.io.File @@ -91,6 +92,19 @@ class ImagePreprocessor : IImagePreprocessor { imageFile: File, options: ImagePreprocessingOptions, ): ProcessedImage { + val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true } + BitmapFactory.decodeFile(imageFile.absolutePath, bounds) + require(bounds.outWidth > 0 && bounds.outHeight > 0) { + "Failed to read static image dimensions" + } + require( + bounds.outWidth <= StaticImageLimits.MAX_DIMENSION && + bounds.outHeight <= StaticImageLimits.MAX_DIMENSION && + bounds.outWidth.toLong() * bounds.outHeight.toLong() <= StaticImageLimits.MAX_PIXEL_COUNT, + ) { + "Static image exceeds the 4 MP or 4,096 px dimension limit." + } + val bitmap = BitmapFactory.decodeFile(imageFile.absolutePath) ?: throw UnsupportedOperationException("Failed to decode image file") @@ -100,33 +114,34 @@ class ImagePreprocessor : IImagePreprocessor { val effectiveOrientation = options.orientation ?: readExifOrientation(imageFile) val rotatedBitmap = rotateBitmap(bitmap, effectiveOrientation) + // Bitmap.createBitmap returns the source instance itself when no transform is + // actually applied (e.g. no rotation needed), so only recycle on a genuine copy. + if (rotatedBitmap !== bitmap) bitmap.recycle() + val effectiveScale = clampScale(options.scaleFactor) val (croppedBitmap, cropRect) = cropToRegionOfInterest(rotatedBitmap, options.roi) + if (croppedBitmap !== rotatedBitmap) rotatedBitmap.recycle() + + val scaledBitmap = + if (effectiveScale < 1.0f) { + croppedBitmap.scale( + (croppedBitmap.width * effectiveScale).toInt(), + (croppedBitmap.height * effectiveScale).toInt(), + false, + ) + } else { + croppedBitmap + } + if (scaledBitmap !== croppedBitmap) croppedBitmap.recycle() val processedBitmap = if (options.invertColors) { - val scaledBitmap = - if (effectiveScale < 1.0f) { - croppedBitmap.scale( - (croppedBitmap.width * effectiveScale).toInt(), - (croppedBitmap.height * effectiveScale).toInt(), - false, - ) - } else { - croppedBitmap - } - invertBitmap(scaledBitmap) + val invertedBitmap = invertBitmap(scaledBitmap) + scaledBitmap.recycle() + invertedBitmap } else { - if (effectiveScale < 1.0f) { - croppedBitmap.scale( - (croppedBitmap.width * effectiveScale).toInt(), - (croppedBitmap.height * effectiveScale).toInt(), - false, - ) - } else { - croppedBitmap - } + scaledBitmap } val inputImage = InputImage.fromBitmap(processedBitmap, 0) diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt index 6c93181..b38a87b 100644 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt @@ -11,9 +11,21 @@ import com.visioncameramlkit.infrastructure.mlkit.adapters.MLKitBarcodeAdapter class MLKitBarcodeScanningService( private val barcodeScanner: BarcodeScanner, ) : IRecognitionService { - override fun recognize(image: ProcessedImage): BarcodeScanningResult { - val task = barcodeScanner.process(image.image) - val barcodes: List = Tasks.await(task) - return MLKitBarcodeAdapter.toDomain(barcodes) + // ML Kit's on-device detectors are not documented as safe for concurrent use. + // This instance is shared between the synchronous frame-processor path and the + // async static-image path, so calls (and disposal) must be serialized. + private val lock = Any() + + override fun recognize(image: ProcessedImage): BarcodeScanningResult = + synchronized(lock) { + val task = barcodeScanner.process(image.image) + val barcodes: List = Tasks.await(task) + MLKitBarcodeAdapter.toDomain(barcodes) + } + + override fun close() { + synchronized(lock) { + barcodeScanner.close() + } } } diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitTextRecognitionService.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitTextRecognitionService.kt index 08f7a00..1c3722e 100644 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitTextRecognitionService.kt +++ b/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitTextRecognitionService.kt @@ -11,9 +11,21 @@ import com.visioncameramlkit.infrastructure.mlkit.adapters.MLKitTextAdapter class MLKitTextRecognitionService( private val textRecognizer: TextRecognizer, ) : IRecognitionService { - override fun recognize(image: ProcessedImage): TextRecognitionResult { - val task = textRecognizer.process(image.image) - val mlkitText: Text? = Tasks.await(task) - return MLKitTextAdapter.toDomain(mlkitText) + // ML Kit's on-device detectors are not documented as safe for concurrent use. + // This instance is shared between the synchronous frame-processor path and the + // async static-image path, so calls (and disposal) must be serialized. + private val lock = Any() + + override fun recognize(image: ProcessedImage): TextRecognitionResult = + synchronized(lock) { + val task = textRecognizer.process(image.image) + val mlkitText: Text? = Tasks.await(task) + MLKitTextAdapter.toDomain(mlkitText) + } + + override fun close() { + synchronized(lock) { + textRecognizer.close() + } } } diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt deleted file mode 100644 index 8222645..0000000 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/modules/VisionCameraMLKitModule.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.visioncameramlkit.infrastructure.modules - -import com.facebook.react.bridge.Arguments -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactContextBaseJavaModule -import com.facebook.react.bridge.ReactMethod -import com.facebook.react.bridge.ReadableMap -import com.facebook.react.module.annotations.ReactModule -import com.visioncameramlkit.BuildConfig -import com.visioncameramlkit.bridge.constants.MLKitFeatureKeys -import com.visioncameramlkit.bridge.handlers.IStaticImageHandler -import com.visioncameramlkit.bridge.handlers.StaticBarcodeScanningHandler -import com.visioncameramlkit.bridge.handlers.StaticTextRecognitionHandler - -@ReactModule(name = VisionCameraMLKitModule.NAME) -class VisionCameraMLKitModule( - reactContext: ReactApplicationContext, -) : ReactContextBaseJavaModule(reactContext) { - companion object { - const val NAME = "VisionCameraMLKitModule" - } - - private val handlers by lazy { - val handlers = mutableMapOf() - - @Suppress("KotlinConstantConditions") - if (BuildConfig.MLKIT_TEXT_RECOGNITION_ANY) { - handlers[MLKitFeatureKeys.TEXT_RECOGNITION] = - StaticTextRecognitionHandler(reactApplicationContext) - } - - @Suppress("KotlinConstantConditions") - if (BuildConfig.MLKIT_BARCODE_SCANNING) { - handlers[MLKitFeatureKeys.BARCODE_SCANNING] = - StaticBarcodeScanningHandler(reactApplicationContext) - } - - handlers - } - - override fun getName(): String = NAME - - @Suppress("unused") - @ReactMethod - fun processImage( - feature: String, - path: String, - options: ReadableMap?, - promise: Promise, - ) { - val safeOptions = options ?: Arguments.createMap() - val handler = handlers[feature] - if (handler == null) { - promise.reject("UNSUPPORTED_FEATURE", "Feature $feature is not supported") - return - } - - handler.process(path, safeOptions, promise) - } -} diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/serializers/BarcodeScanningSerializer.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/serializers/BarcodeScanningSerializer.kt deleted file mode 100644 index 8ce3192..0000000 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/serializers/BarcodeScanningSerializer.kt +++ /dev/null @@ -1,190 +0,0 @@ -package com.visioncameramlkit.infrastructure.serializers - -import com.facebook.react.bridge.WritableNativeArray -import com.facebook.react.bridge.WritableNativeMap -import com.visioncameramlkit.domain.models.BarcodeData -import com.visioncameramlkit.domain.models.BarcodeParsedValue -import com.visioncameramlkit.domain.models.BarcodeScanningResult -import com.visioncameramlkit.domain.models.BoundingBox -import com.visioncameramlkit.domain.models.Corner - -@Suppress("TooManyFunctions") -object BarcodeScanningSerializer { - fun toWritableMap(result: BarcodeScanningResult): WritableNativeMap = - WritableNativeMap().apply { - putArray("barcodes", toBarcodesArray(result.barcodes)) - } - - fun toReactNativeMap(result: BarcodeScanningResult): HashMap { - @Suppress("UNCHECKED_CAST") - return toWritableMap(result).toHashMap() as HashMap - } - - private fun toBarcodesArray(barcodes: List): WritableNativeArray = - WritableNativeArray().apply { - barcodes.forEach { barcode -> pushMap(toBarcodeMap(barcode)) } - } - - private fun toBarcodeMap(barcode: BarcodeData): WritableNativeMap = - WritableNativeMap().apply { - barcode.bounds?.let { putMap("bounds", toBoundsMap(it)) } - barcode.corners?.let { putArray("corners", toCornersArray(it)) } - putInt("format", barcode.format) - putString("formatName", barcode.formatName) - putInt("valueType", barcode.valueType) - putString("valueTypeName", barcode.valueTypeName) - putString("rawValue", barcode.rawValue) - putString("displayValue", barcode.displayValue) - putBoolean("isPotential", barcode.isPotential) - barcode.rawBytes?.let { putArray("rawBytes", toByteArray(it)) } - barcode.value?.let { putMap("value", toParsedValueMap(it)) } - } - - private fun toBoundsMap(bounds: BoundingBox): WritableNativeMap = - WritableNativeMap().apply { - putDouble("x", bounds.x) - putDouble("y", bounds.y) - putDouble("centerX", bounds.centerX) - putDouble("centerY", bounds.centerY) - putDouble("width", bounds.width) - putDouble("height", bounds.height) - putDouble("top", bounds.top) - putDouble("left", bounds.left) - putDouble("bottom", bounds.bottom) - putDouble("right", bounds.right) - } - - private fun toCornersArray(corners: List): WritableNativeArray = - WritableNativeArray().apply { - corners.forEach { corner -> - pushMap( - WritableNativeMap().apply { - putDouble("x", corner.x) - putDouble("y", corner.y) - }, - ) - } - } - - private fun toByteArray(bytes: List): WritableNativeArray = - WritableNativeArray().apply { - bytes.forEach { byteValue -> pushInt(byteValue) } - } - - private fun toParsedValueMap(value: BarcodeParsedValue): WritableNativeMap = - WritableNativeMap().apply { - putString("type", value.type) - putMap("data", toDynamicMap(value.data)) - } - - private fun toDynamicMap(map: Map): WritableNativeMap = - WritableNativeMap().apply { - map.forEach { (key, value) -> - putDynamic(this, key, value) - } - } - - private fun toDynamicArray(list: List): WritableNativeArray = - WritableNativeArray().apply { - list.forEach { value -> pushDynamic(this, value) } - } - - private fun putDynamic( - target: WritableNativeMap, - key: String, - value: Any?, - ) { - when (value) { - null -> { - target.putNull(key) - } - - is String -> { - target.putString(key, value) - } - - is Boolean -> { - target.putBoolean(key, value) - } - - is Int -> { - target.putInt(key, value) - } - - is Long -> { - target.putDouble(key, value.toDouble()) - } - - is Float -> { - target.putDouble(key, value.toDouble()) - } - - is Double -> { - target.putDouble(key, value) - } - - is Map<*, *> -> { - @Suppress("UNCHECKED_CAST") - target.putMap(key, toDynamicMap(value as Map)) - } - - is List<*> -> { - @Suppress("UNCHECKED_CAST") - target.putArray(key, toDynamicArray(value)) - } - - else -> { - target.putString(key, value.toString()) - } - } - } - - private fun pushDynamic( - target: WritableNativeArray, - value: Any?, - ) { - when (value) { - null -> { - target.pushNull() - } - - is String -> { - target.pushString(value) - } - - is Boolean -> { - target.pushBoolean(value) - } - - is Int -> { - target.pushInt(value) - } - - is Long -> { - target.pushDouble(value.toDouble()) - } - - is Float -> { - target.pushDouble(value.toDouble()) - } - - is Double -> { - target.pushDouble(value) - } - - is Map<*, *> -> { - @Suppress("UNCHECKED_CAST") - target.pushMap(toDynamicMap(value as Map)) - } - - is List<*> -> { - @Suppress("UNCHECKED_CAST") - target.pushArray(toDynamicArray(value)) - } - - else -> { - target.pushString(value.toString()) - } - } - } -} diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/serializers/TextRecognitionSerializer.kt b/android/src/main/java/com/visioncameramlkit/infrastructure/serializers/TextRecognitionSerializer.kt deleted file mode 100644 index 292c30d..0000000 --- a/android/src/main/java/com/visioncameramlkit/infrastructure/serializers/TextRecognitionSerializer.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.visioncameramlkit.infrastructure.serializers - -import com.facebook.react.bridge.WritableNativeArray -import com.facebook.react.bridge.WritableNativeMap -import com.visioncameramlkit.domain.models.BoundingBox -import com.visioncameramlkit.domain.models.Corner -import com.visioncameramlkit.domain.models.TextBlock -import com.visioncameramlkit.domain.models.TextElement -import com.visioncameramlkit.domain.models.TextLine -import com.visioncameramlkit.domain.models.TextRecognitionResult -import com.visioncameramlkit.domain.models.TextSymbol - -@Suppress("TooManyFunctions") -object TextRecognitionSerializer { - fun toWritableMap(result: TextRecognitionResult): WritableNativeMap = - WritableNativeMap().apply { - putString("text", result.text) - putArray("blocks", toBlocksArray(result.blocks)) - } - - fun toReactNativeMap(result: TextRecognitionResult): HashMap { - @Suppress("UNCHECKED_CAST") - return toWritableMap(result).toHashMap() as HashMap - } - - private fun toBlocksArray(blocks: List): WritableNativeArray = - WritableNativeArray().apply { - blocks.forEach { block -> pushMap(toBlockMap(block)) } - } - - private fun toBlockMap(block: TextBlock): WritableNativeMap = - WritableNativeMap().apply { - putString("text", block.text) - block.bounds?.let { putMap("bounds", toBoundsMap(it)) } - block.corners?.let { putArray("corners", toCornersArray(it)) } - putArray("lines", toLinesArray(block.lines)) - putArray("languages", toLanguagesArray(block.languages)) - } - - private fun toLinesArray(lines: List): WritableNativeArray = - WritableNativeArray().apply { - lines.forEach { line -> pushMap(toLineMap(line)) } - } - - private fun toLineMap(line: TextLine): WritableNativeMap = - WritableNativeMap().apply { - putString("text", line.text) - line.bounds?.let { putMap("bounds", toBoundsMap(it)) } - line.corners?.let { putArray("corners", toCornersArray(it)) } - putArray("elements", toElementsArray(line.elements)) - line.confidence?.let { putDouble("confidence", it) } - line.angle?.let { putDouble("angle", it) } - putArray("languages", toLanguagesArray(line.languages)) - } - - private fun toElementsArray(elements: List): WritableNativeArray = - WritableNativeArray().apply { - elements.forEach { element -> pushMap(toElementMap(element)) } - } - - private fun toElementMap(element: TextElement): WritableNativeMap = - WritableNativeMap().apply { - putString("text", element.text) - element.bounds?.let { putMap("bounds", toBoundsMap(it)) } - element.corners?.let { putArray("corners", toCornersArray(it)) } - putArray("symbols", toSymbolsArray(element.symbols)) - element.confidence?.let { putDouble("confidence", it) } - element.angle?.let { putDouble("angle", it) } - putArray("languages", toLanguagesArray(element.languages)) - } - - private fun toSymbolsArray(symbols: List): WritableNativeArray = - WritableNativeArray().apply { - symbols.forEach { symbol -> pushMap(toSymbolMap(symbol)) } - } - - private fun toSymbolMap(symbol: TextSymbol): WritableNativeMap = - WritableNativeMap().apply { - putString("text", symbol.text) - symbol.bounds?.let { putMap("bounds", toBoundsMap(it)) } - symbol.corners?.let { putArray("corners", toCornersArray(it)) } - symbol.confidence?.let { putDouble("confidence", it) } - symbol.angle?.let { putDouble("angle", it) } - putArray("languages", toLanguagesArray(symbol.languages)) - } - - private fun toBoundsMap(bounds: BoundingBox): WritableNativeMap = - WritableNativeMap().apply { - putDouble("x", bounds.x) - putDouble("y", bounds.y) - putDouble("centerX", bounds.centerX) - putDouble("centerY", bounds.centerY) - putDouble("width", bounds.width) - putDouble("height", bounds.height) - putDouble("top", bounds.top) - putDouble("left", bounds.left) - putDouble("bottom", bounds.bottom) - putDouble("right", bounds.right) - } - - private fun toCornersArray(corners: List): WritableNativeArray = - WritableNativeArray().apply { - corners.forEach { corner -> pushMap(toCornerMap(corner)) } - } - - private fun toCornerMap(corner: Corner): WritableNativeMap = - WritableNativeMap().apply { - putDouble("x", corner.x) - putDouble("y", corner.y) - } - - private fun toLanguagesArray(languages: List): WritableNativeArray = - WritableNativeArray().apply { - languages.forEach { pushString(it) } - } -} diff --git a/docs/barcode-scanning.md b/docs/barcode-scanning.md index 7e1d5b2..c45496f 100644 --- a/docs/barcode-scanning.md +++ b/docs/barcode-scanning.md @@ -2,9 +2,13 @@ This page documents the Barcode Scanning API in more detail than the README quick-start. +Barcode scanning is implemented by configured Nitro recognizers for both live +frames and static images. + ## Imports ```ts +import { useFrameOutput } from 'react-native-vision-camera'; import { useBarcodeScanning, processImageBarcodeScanning, @@ -51,7 +55,7 @@ const frameOutput = useFrameOutput({ - `scaleFactor?: number` (`0.9`-`1.0`) - `invertColors?: boolean` - `roi?: RegionOfInterest` (crop processing to a rectangle of the frame; see the [Region of Interest](../README.md#region-of-interest) section in the main README) -- `frameProcessInterval?: number` (deprecated; throttle frames manually inside `onFrame` instead) +- `frameProcessInterval?: number` (deprecated compatibility field; v2 does not use it to throttle processing) ### Frame arguments @@ -59,7 +63,8 @@ const frameOutput = useFrameOutput({ ## Static Image Processing -Use `processImageBarcodeScanning(uri, options)` for gallery files / local images. +Use `processImageBarcodeScanning(uri, options)` for local images. Each call +creates a Nitro scanner with `options` and runs recognition asynchronously. ```ts const result = await processImageBarcodeScanning(imageUri, { @@ -75,11 +80,18 @@ const result = await processImageBarcodeScanning(imageUri, { - `formats?: BarcodeFormat[]` - `enableAllPotentialBarcodes?: boolean` (Android only) -- `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` +- `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` (overrides EXIF orientation; omit it to use image metadata) - `invertColors?: boolean` - `scaleFactor?: number` (`0.9`-`1.0`) - `roi?: RegionOfInterest` (crop processing to a rectangle of the image; see the [Region of Interest](../README.md#region-of-interest) section in the main README) +Static images must be local and readable. iOS accepts an absolute path or a +`file://` URI. Android also accepts `content://` URIs, which are copied to a +temporary cache file. Remote URLs and platform-library schemes such as +`ph://` are not supported. Static processing does not require Worklets or a +camera, but VisionCamera v5 and Nitro Modules remain native package +dependencies. + ## Supported formats - `ALL_FORMATS` @@ -129,17 +141,24 @@ Use `switch (barcode.value?.type)` for strong type inference in TypeScript. ## Errors -For static image processing, promise rejections can use: +Static Nitro calls reject invalid schemes, missing or unreadable files, and +images the platform decoder cannot read. The package exports these +compatibility error-message constants: - `IMAGE_NOT_FOUND_ERROR` - `INVALID_URI_ERROR` - `IMAGE_PROCESSING_FAILED_ERROR` - `UNSUPPORTED_IMAGE_FORMAT_ERROR` +These constants are retained for source compatibility and user-facing fallback +messages. Nitro rejection messages are platform-specific and are not guaranteed +to equal these strings; do not branch on `error.message`. + ## Performance guidance - Filter `formats` whenever possible for better performance. -- Always call `frame.dispose()` exactly once per frame inside `onFrame`. -- Throttle by skipping frames manually inside `onFrame` instead of relying on - a native-side interval. +- Always call `frame.dispose()` exactly once for every frame, including frames + you skip; keep disposal in a `finally` block around the whole callback. +- `frameProcessInterval` has no throttling effect in v2. Decide whether to call + the scanner inside `onFrame`, while still disposing every frame. - Keep `scaleFactor` as high as possible for decoding reliability. diff --git a/docs/migration-v1-to-v2.md b/docs/migration-v1-to-v2.md new file mode 100644 index 0000000..f1100f7 --- /dev/null +++ b/docs/migration-v1-to-v2.md @@ -0,0 +1,72 @@ +# Migrating from v1 to v2 + +v2 moves the library to React Native 0.86, VisionCamera v5 frame outputs, and +Nitro Modules. It supports iOS 15.1+ and Android SDK 24+. VisionCamera v4 users +must remain on a v1 tag until their camera integration can move to v5. + +## Dependency changes + +Install the v2 native dependencies: + +```sh +npm install react-native-vision-camera-mlkit@^2 \ + react-native-vision-camera@^5 \ + react-native-nitro-modules +cd ios && pod install +``` + +Live-frame recognition also needs VisionCamera's frame-output Worklets +dependencies: + +```sh +npm install react-native-vision-camera-worklets react-native-worklets +``` + +Configure `react-native-worklets/plugin` in `babel.config.js`. Remove the v1 +`react-native-worklets-core` dependency and Babel plugin if nothing else in +your app uses them. Static-image APIs do not need Worklets or a camera, but the +package still requires VisionCamera v5 and Nitro Modules at build time. + +## Live-frame API changes + +Replace the v1 `useFrameProcessor` integration with VisionCamera v5's +`useFrameOutput`. Call `textRecognition(frame, args)` or +`barcodeScanning(frame, args)` directly in its `onFrame` worklet. + +- Remove `runAsync(frame, ...)` and `runAtTargetFps(...)`; those v1 patterns do + not belong to the v5 frame-output API. +- Dispose every delivered frame exactly once, including skipped or failed + frames. Put `frame.dispose()` in a `finally` block around the whole callback. +- Remove `frameProcessInterval`. It remains in the type for compatibility but + has no throttling effect in the v2 Nitro recognizers. Make the call/skip + decision inside `onFrame` and dispose either way. +- Continue passing `outputOrientation` for iOS live frames when required. + +See the complete [text recognition](text-recognition.md) and +[barcode scanning](barcode-scanning.md) examples. + +## Static-image behavior + +The public functions remain `processImageTextRecognition(uri, options)` and +`processImageBarcodeScanning(uri, options)`, but v2 runs them through configured +Nitro recognizers. The supplied language, formats, orientation, scale, color +inversion, and ROI options configure that recognizer. + +Only local, readable images are accepted. iOS supports absolute paths and +`file://` URIs. Android additionally supports `content://` URIs. Remote URLs +and platform-library schemes such as `ph://` are not supported. Omit +`orientation` to use EXIF metadata; pass it only to override missing or wrong +metadata. + +## Feature availability + +v2 implements text recognition and barcode scanning. Both are enabled by +default, including all OCR language models. Other ML Kit feature keys are +reserved, disabled roadmap configuration and do not expose public APIs in v2. + +## v1 maintenance + +Published v1 tags remain available for applications that still use +VisionCamera v4, but v1 is not the active development line. The project does +not currently publish a security or critical-fix support guarantee for v1; +plan to migrate to v2 rather than relying on future v1 releases. diff --git a/docs/text-recognition.md b/docs/text-recognition.md index aa76c77..9ad9b51 100644 --- a/docs/text-recognition.md +++ b/docs/text-recognition.md @@ -2,9 +2,13 @@ This page documents the Text Recognition API in more detail than the README quick-start. +Text recognition is implemented by configured Nitro recognizers for both live +frames and static images. + ## Imports ```ts +import { useFrameOutput } from 'react-native-vision-camera'; import { useTextRecognition, processImageTextRecognition, @@ -49,7 +53,7 @@ const frameOutput = useFrameOutput({ - `scaleFactor?: number` (`0.9`-`1.0`) - `invertColors?: boolean` - `roi?: RegionOfInterest` (crop processing to a rectangle of the frame; see the [Region of Interest](../README.md#region-of-interest) section in the main README) -- `frameProcessInterval?: number` (deprecated; throttle frames manually inside `onFrame` instead) +- `frameProcessInterval?: number` (deprecated compatibility field; v2 does not use it to throttle processing) ### Frame arguments @@ -57,7 +61,8 @@ const frameOutput = useFrameOutput({ ## Static Image Processing -Use `processImageTextRecognition(uri, options)` for gallery files / local images. +Use `processImageTextRecognition(uri, options)` for local images. Each call +creates a Nitro recognizer with `options` and runs recognition asynchronously. ```ts const result = await processImageTextRecognition(imageUri, { @@ -71,11 +76,18 @@ const result = await processImageTextRecognition(imageUri, { ### Image options - `language?: 'LATIN' | 'CHINESE' | 'DEVANAGARI' | 'JAPANESE' | 'KOREAN'` -- `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` +- `orientation?: 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right'` (overrides EXIF orientation; omit it to use image metadata) - `invertColors?: boolean` - `scaleFactor?: number` (`0.9`-`1.0`) - `roi?: RegionOfInterest` (crop processing to a rectangle of the image; see the [Region of Interest](../README.md#region-of-interest) section in the main README) +Static images must be local and readable. iOS accepts an absolute path or a +`file://` URI. Android also accepts `content://` URIs, which are copied to a +temporary cache file. Remote URLs and platform-library schemes such as +`ph://` are not supported. Static processing does not require Worklets or a +camera, but VisionCamera v5 and Nitro Modules remain native package +dependencies. + ## Result shape `TextRecognitionResult` is a nested hierarchy: text → blocks → lines → elements @@ -101,16 +113,23 @@ Android rotation is handled automatically. ## Errors -For static image processing, promise rejections can use: +Static Nitro calls reject invalid schemes, missing or unreadable files, and +images the platform decoder cannot read. The package exports these +compatibility error-message constants: - `IMAGE_NOT_FOUND_ERROR` - `INVALID_URI_ERROR` - `IMAGE_PROCESSING_FAILED_ERROR` - `UNSUPPORTED_IMAGE_FORMAT_ERROR` +These constants are retained for source compatibility and user-facing fallback +messages. Nitro rejection messages are platform-specific and are not guaranteed +to equal these strings; do not branch on `error.message`. + ## Performance guidance -- Always call `frame.dispose()` exactly once per frame inside `onFrame`. -- Throttle by skipping frames manually inside `onFrame` instead of relying on - a native-side interval. +- Always call `frame.dispose()` exactly once for every frame, including frames + you skip; keep disposal in a `finally` block around the whole callback. +- `frameProcessInterval` has no throttling effect in v2. Decide whether to call + the recognizer inside `onFrame`, while still disposing every frame. - Keep `scaleFactor` as high as possible for OCR accuracy. diff --git a/example/Gemfile.lock b/example/Gemfile.lock index bc2bd1b..1f89b08 100644 --- a/example/Gemfile.lock +++ b/example/Gemfile.lock @@ -121,4 +121,4 @@ RUBY VERSION ruby 2.7.4p191 BUNDLED WITH - 2.1.4 + 4.0.17 diff --git a/example/android/build.gradle b/example/android/build.gradle index 2f2de8a..04c24b3 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,5 +1,8 @@ buildscript { ext { + def v2FeaturesEnabled = + !Boolean.parseBoolean(System.getenv("VISION_CAMERA_MLKIT_DISABLE_V2_FEATURES")) + buildToolsVersion = "36.0.0" minSdkVersion = 24 compileSdkVersion = 36 @@ -9,11 +12,11 @@ buildscript { set('react-native-vision-camera-mlkit', [ mlkit: [ - textRecognition : true, - textRecognitionChinese : true, - textRecognitionDevanagari: true, - textRecognitionJapanese : true, - textRecognitionKorean : true, + textRecognition : v2FeaturesEnabled, + textRecognitionChinese : v2FeaturesEnabled, + textRecognitionDevanagari: v2FeaturesEnabled, + textRecognitionJapanese : v2FeaturesEnabled, + textRecognitionKorean : v2FeaturesEnabled, faceDetection : false, faceMeshDetection : false, poseDetection : false, @@ -21,7 +24,7 @@ buildscript { selfieSegmentation : false, subjectSegmentation : false, documentScanner : false, - barcodeScanning : true, + barcodeScanning : v2FeaturesEnabled, imageLabeling : false, objectDetection : false, digitalInkRecognition : false, diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 3570963..dbdeaca 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1605,7 +1605,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - react-native-safe-area-context (5.6.2): + - react-native-safe-area-context (5.8.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1617,8 +1617,8 @@ PODS: - React-graphics - React-ImageManager - React-jsi - - react-native-safe-area-context/common (= 5.6.2) - - react-native-safe-area-context/fabric (= 5.6.2) + - react-native-safe-area-context/common (= 5.8.0) + - react-native-safe-area-context/fabric (= 5.8.0) - React-NativeModulesApple - React-RCTFabric - React-renderercss @@ -1629,7 +1629,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - react-native-safe-area-context/common (5.6.2): + - react-native-safe-area-context/common (5.8.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1651,7 +1651,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - react-native-safe-area-context/fabric (5.6.2): + - react-native-safe-area-context/fabric (5.8.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2138,7 +2138,7 @@ PODS: - ReactNativeDependencies - Yoga - ReactNativeDependencies (0.86.0) - - RNGestureHandler (3.0.2): + - RNGestureHandler (3.1.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2160,7 +2160,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - RNPermissions (5.4.4): + - RNPermissions (5.6.1): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2182,7 +2182,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - RNReanimated (4.5.1): + - RNReanimated (4.5.3): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2204,12 +2204,12 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - - RNReanimated/apple (= 4.5.1) - - RNReanimated/common (= 4.5.1) - - RNReanimated/view (= 4.5.1) + - RNReanimated/apple (= 4.5.3) + - RNReanimated/common (= 4.5.3) + - RNReanimated/view (= 4.5.3) - RNWorklets - Yoga - - RNReanimated/apple (4.5.1): + - RNReanimated/apple (4.5.3): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2233,7 +2233,7 @@ PODS: - ReactNativeDependencies - RNWorklets - Yoga - - RNReanimated/common (4.5.1): + - RNReanimated/common (4.5.3): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2257,7 +2257,7 @@ PODS: - ReactNativeDependencies - RNWorklets - Yoga - - RNReanimated/view (4.5.1): + - RNReanimated/view (4.5.3): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2281,7 +2281,7 @@ PODS: - ReactNativeDependencies - RNWorklets - Yoga - - RNScreens (4.26.0): + - RNScreens (4.26.2): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2303,9 +2303,9 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - - RNScreens/common (= 4.26.0) + - RNScreens/common (= 4.26.2) - Yoga - - RNScreens/common (4.26.0): + - RNScreens/common (4.26.2): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2444,7 +2444,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - VisionCamera (5.1.0): + - VisionCamera (5.2.1): - hermes-engine - NitroImage - NitroModules @@ -2499,7 +2499,7 @@ PODS: - ReactNativeDependencies - VisionCamera - Yoga - - VisionCameraWorklets (5.1.0): + - VisionCameraWorklets (5.2.1): - hermes-engine - NitroModules - RCTRequired @@ -2889,7 +2889,7 @@ SPEC CHECKSUMS: React-mutationobservernativemodule: 2e568e13c98bd185fe83266795233fa7ad2cbc19 react-native-blur: baf5874b4abf3bb8b2012172a3d0905653a1a2b5 react-native-image-picker: 2146157d5d37a34c2063d1d2ed01081b56d0b87b - react-native-safe-area-context: 3cd1f988c96f187e15b49b86b0a2068a6cb19393 + react-native-safe-area-context: c1eb308f4b36372a4de4b3bdaa8ed695ec3dd461 react-native-slider: 73473ab537b86b7ca1b21a28d16019d3cb63e927 React-NativeModulesApple: a4713821c9caae40ae33eef933a6e692a6f4a81d React-networking: b344a1a41a4729ef859123b7a234f3092761cb25 @@ -2927,15 +2927,15 @@ SPEC CHECKSUMS: ReactCommon: 4d01bfb533b93a2103329dfefc74db6a9e272888 ReactNativeBottomSheet: 3551d418c1d10cd49e0907d68300374544fb8d00 ReactNativeDependencies: fa0a54b3f5319ae0e3b9aff32bfee7a424b88e66 - RNGestureHandler: ef19613372e4c87374fe9494862e366d82d1852c - RNPermissions: 38d9cf58be74d148123c93d0e0713092e8e59260 - RNReanimated: 516d842d105966ae05a6a1ad7ad2a0dc1e968d6b - RNScreens: 37cece0483decca1c3300bb1c2225c798039b8fc + RNGestureHandler: 6206fc566272f60a2d57a0b3258c495a73b7dc90 + RNPermissions: 509028712d9e899fcce678c315989a6d439b003c + RNReanimated: 5cc36a25f9e60c523734a60783f84d363e73b4dc + RNScreens: dcd5c85a822d9cbb21fc321bbbbcf918f3d48c23 RNSVG: ffe18ba0e8fbad864bf22d6f0060a4ee88ec9bf3 RNWorklets: 82a5f27dcf3bb69267ec6ce662a756a4d3f61522 - VisionCamera: 73a21a8ed0feecd7c579d79ae25eaf8c3e63c767 - VisionCameraMLKit: c85a989fbaae33532636fdf9cab21b287ddf8a3f - VisionCameraWorklets: 0f70d789dfce3d2efc67db7e106e2f4a32bbe72e + VisionCamera: 4e3e123f4d14c7d3934748c84fe6ddfa5046c5ee + VisionCameraMLKit: 08f7f64b2794cde438ca7ab82a4d904d52d44dd3 + VisionCameraWorklets: 0d8b34820c6aa351fbba9fab3d8ee8e8d98e1990 Yoga: 94172d87dd5d83fc929bf1ee53121b5d4490b5f2 PODFILE CHECKSUM: bfa9c28a161369f7b66f55890deec95fe59f1504 diff --git a/example/package.json b/example/package.json index 04adc3d..adeacbd 100644 --- a/example/package.json +++ b/example/package.json @@ -7,7 +7,7 @@ "ios": "react-native run-ios", "start": "react-native start", "build:android": "react-native build-android --extra-params \"--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a\"", - "build:ios": "react-native build-ios --mode Debug --destination \"${IOS_DESTINATION:-generic/platform=iOS Simulator}\"" + "build:ios": "react-native build-ios --mode Debug --destination \"${IOS_DESTINATION:-OS=latest,name=iPhone 15}\"" }, "dependencies": { "@react-native-community/blur": "^4.4.1", diff --git a/example/react-native.config.js b/example/react-native.config.js index 59d9698..0b9606d 100644 --- a/example/react-native.config.js +++ b/example/react-native.config.js @@ -10,12 +10,6 @@ module.exports = { dependencies: { [pkg.name]: { root: path.join(__dirname, '..'), - platforms: { - // Codegen script incorrectly fails without this - // So we explicitly specify the platforms with empty object - ios: {}, - android: {}, - }, }, }, }; diff --git a/ios/Application/UseCases/RecognizeBarcodesUseCase.swift b/ios/Application/UseCases/RecognizeBarcodesUseCase.swift deleted file mode 100644 index 4a07b3d..0000000 --- a/ios/Application/UseCases/RecognizeBarcodesUseCase.swift +++ /dev/null @@ -1,81 +0,0 @@ -import Foundation - -#if MLKIT_BARCODE_SCANNING - #if canImport(VisionCamera) - import VisionCamera - - class RecognizeBarcodesUseCase { - private let imagePreprocessor: IImagePreprocessor - private let recognitionService: MLKitBarcodeScanningService - - init( - imagePreprocessor: IImagePreprocessor, - recognitionService: MLKitBarcodeScanningService - ) { - self.imagePreprocessor = imagePreprocessor - self.recognitionService = recognitionService - } - - func execute(frame: any HybridFrameSpec, options: BarcodeScanningOptions) throws - -> BarcodeScanningResult - { - return try autoreleasepool { - let preprocessingOptions = ImagePreprocessingOptions( - invertColors: options.invertColors, - outputOrientation: options.outputOrientation, - scaleFactor: options.scaleFactor - ) - - guard - let processedImage = imagePreprocessor.preprocessFrame( - frame: frame, - options: preprocessingOptions - ) - else { - throw NSError( - domain: "RecognizeBarcodesUseCase", - code: -1, - userInfo: [NSLocalizedDescriptionKey: "Failed to preprocess image"] - ) - } - - let result = try recognitionService.recognize(image: processedImage) - return result.remapped(using: processedImage.metadata) - } - } - - func execute( - imageFile: URL, - imageOptions: ImagePreprocessingOptions, - barcodeOptions: BarcodeScanningOptions - ) throws -> BarcodeScanningResult { - return try autoreleasepool { - let preprocessingOptions = ImagePreprocessingOptions( - invertColors: imageOptions.invertColors, - outputOrientation: imageOptions.outputOrientation, - scaleFactor: imageOptions.scaleFactor, - orientation: imageOptions.orientation, - roi: imageOptions.roi - ) - - guard - let processedImage = imagePreprocessor.preprocessImage( - imageFile: imageFile, - options: preprocessingOptions - ) - else { - throw NSError( - domain: "RecognizeBarcodesUseCase", - code: -1, - userInfo: [NSLocalizedDescriptionKey: "Failed to preprocess image"] - ) - } - - _ = barcodeOptions - let result = try recognitionService.recognize(image: processedImage) - return result.remapped(using: processedImage.metadata) - } - } - } - #endif -#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Application/UseCases/RecognizeTextUseCase.swift b/ios/Application/UseCases/RecognizeTextUseCase.swift deleted file mode 100644 index 2f4cafc..0000000 --- a/ios/Application/UseCases/RecognizeTextUseCase.swift +++ /dev/null @@ -1,76 +0,0 @@ -import Foundation - -#if MLKIT_TEXT_RECOGNITION_ANY - #if canImport(VisionCamera) - import VisionCamera - - class RecognizeTextUseCase { - private let imagePreprocessor: IImagePreprocessor - private let recognitionService: MLKitTextRecognitionService - - init( - imagePreprocessor: IImagePreprocessor, - recognitionService: MLKitTextRecognitionService - ) { - self.imagePreprocessor = imagePreprocessor - self.recognitionService = recognitionService - } - - func execute(frame: any HybridFrameSpec, options: TextRecognitionOptions) throws - -> DomainTextRecognitionResult - { - // autoreleasepool at frame boundary to drain autoreleased Obj-C objects - // after each frame is processed, preventing buildup across multiple frames - return try autoreleasepool { - let preprocessingOptions = ImagePreprocessingOptions( - invertColors: options.invertColors, - outputOrientation: options.outputOrientation, - scaleFactor: options.scaleFactor - ) - - guard - let processedImage = imagePreprocessor.preprocessFrame( - frame: frame, - options: preprocessingOptions - ) - else { - throw NSError( - domain: "RecognizeTextUseCase", - code: -1, - userInfo: [NSLocalizedDescriptionKey: "Failed to preprocess image"] - ) - } - - let result = try recognitionService.recognize(image: processedImage) - return result.remapped(using: processedImage.metadata) - } - } - - func execute( - imageFile: URL, - imageOptions: ImagePreprocessingOptions, - textOptions: TextRecognitionOptions - ) throws -> DomainTextRecognitionResult { - // autoreleasepool here ensures predictable memory cleanup, - // especially if this method is called repeatedly or in batches - return try autoreleasepool { - guard - let processedImage = imagePreprocessor.preprocessImage( - imageFile: imageFile, - options: imageOptions - ) - else { - throw NSError( - domain: "RecognizeTextUseCase", - code: -1, - userInfo: [NSLocalizedDescriptionKey: "Failed to preprocess image"] - ) - } - - let result = try recognitionService.recognize(image: processedImage) - return result.remapped(using: processedImage.metadata) - } - } - } - #endif -#endif // MLKIT_TEXT_RECOGNITION_ANY diff --git a/ios/Bridge/Constants/MLKitFeatureKeys.swift b/ios/Bridge/Constants/MLKitFeatureKeys.swift deleted file mode 100644 index 912f061..0000000 --- a/ios/Bridge/Constants/MLKitFeatureKeys.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation - -enum MLKitFeatureKeys { - static let TEXT_RECOGNITION = "TextRecognition" - static let FACE_DETECTION = "FaceDetection" - static let FACE_MESH_DETECTION = "FaceMeshDetection" - static let POSE_DETECTION = "PoseDetection" - static let SELFIE_SEGMENTATION = "SelfieSegmentation" - static let SUBJECT_SEGMENTATION = "SubjectSegmentation" - static let DOCUMENT_SCANNER = "DocumentScanner" - static let BARCODE_SCANNING = "BarcodeScanning" - static let IMAGE_LABELING = "ImageLabeling" - static let OBJECT_DETECTION = "ObjectDetection" - static let DIGITAL_INK_RECOGNITION = "DigitalInkRecognition" -} diff --git a/ios/Bridge/Handlers/IStaticImageHandler.swift b/ios/Bridge/Handlers/IStaticImageHandler.swift deleted file mode 100644 index de5ee0d..0000000 --- a/ios/Bridge/Handlers/IStaticImageHandler.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Foundation - -typealias PromiseResolver = (Any?) -> Void -typealias PromiseRejecter = (String, String, Error?) -> Void - -protocol IStaticImageHandler { - func process( - path: String, - options: [String: Any], - resolver: @escaping PromiseResolver, - rejecter: @escaping PromiseRejecter - ) -} diff --git a/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift b/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift deleted file mode 100644 index 218ae19..0000000 --- a/ios/Bridge/Handlers/StaticBarcodeScanningHandler.swift +++ /dev/null @@ -1,150 +0,0 @@ -import Foundation - -#if MLKIT_BARCODE_SCANNING - class StaticBarcodeScanningHandler: IStaticImageHandler { - private var cachedUseCases: [String: RecognizeBarcodesUseCase] = [:] - - private func getRecognizeBarcodesUseCase(for options: BarcodeScanningOptions) - -> RecognizeBarcodesUseCase - { - let key = - "\(options.enableAllPotentialBarcodes)|\(options.formats.map(\.rawValue).joined(separator: ","))" - - if let useCase = cachedUseCases[key] { - return useCase - } - - let newUseCase = RecognizeBarcodesUseCase( - imagePreprocessor: ImagePreprocessor(), - recognitionService: BarcodeScanningServiceFactory.create(options: options) - ) - cachedUseCases[key] = newUseCase - return newUseCase - } - - func process( - path: String, - options: [String: Any], - resolver: @escaping PromiseResolver, - rejecter: @escaping PromiseRejecter - ) { - Task { - do { - guard let fileURL = resolveFileURL(from: path) else { - rejecter( - "IMAGE_NOT_FOUND_ERROR", - "Unsupported image URI: \(path)", - nil - ) - return - } - - if fileURL.isFileURL - && !FileManager.default.fileExists(atPath: fileURL.path) - { - rejecter( - "IMAGE_NOT_FOUND_ERROR", - "Image file not found at path: \(path)", - nil - ) - return - } - - let imageOptions = parseImageOptions(options) - let barcodeOptions = parseBarcodeOptions(options) - let recognizeBarcodesUseCase = getRecognizeBarcodesUseCase(for: barcodeOptions) - - let result = try recognizeBarcodesUseCase.execute( - imageFile: fileURL, - imageOptions: imageOptions, - barcodeOptions: barcodeOptions - ) - - resolver(BarcodeScanningSerializer.toReactNativeMap(result)) - } catch { - switch error { - case let nsError as NSError: - if nsError.domain == NSCocoaErrorDomain - && nsError.code == NSFileReadNoSuchFileError - { - rejecter("IMAGE_NOT_FOUND_ERROR", "Image file not found", error) - } else { - rejecter( - "IMAGE_PROCESSING_FAILED_ERROR", - error.localizedDescription, - error - ) - } - default: - rejecter( - "IMAGE_PROCESSING_FAILED_ERROR", - error.localizedDescription, - error - ) - } - } - } - } - - private func parseImageOptions(_ options: [String: Any]) - -> ImagePreprocessingOptions - { - let invertColors = options["invertColors"] as? Bool ?? false - let orientation = (options["orientation"] as? String).flatMap { DomainOrientation(string: $0) } - let scaleFactor = CGFloat((options["scaleFactor"] as? NSNumber)?.doubleValue ?? 1.0) - let roi = parseRegionOfInterest(options["roi"]) - - return ImagePreprocessingOptions( - invertColors: invertColors, - scaleFactor: scaleFactor, - orientation: orientation, - roi: roi - ) - } - - private func parseRegionOfInterest(_ value: Any?) -> DomainRegionOfInterest? { - guard let dictionary = value as? [String: Any], - let x = (dictionary["x"] as? NSNumber)?.doubleValue, - let y = (dictionary["y"] as? NSNumber)?.doubleValue, - let width = (dictionary["width"] as? NSNumber)?.doubleValue, - let height = (dictionary["height"] as? NSNumber)?.doubleValue - else { - return nil - } - - let unit: DomainRegionOfInterestUnit = - (dictionary["unit"] as? String) == "pixel" ? .pixel : .normalized - - return DomainRegionOfInterest( - x: CGFloat(x), - y: CGFloat(y), - width: CGFloat(width), - height: CGFloat(height), - unit: unit - ) - } - - private func parseBarcodeOptions(_ options: [String: Any]) - -> BarcodeScanningOptions - { - return BarcodeScanningOptions( - formats: BarcodeScanningOptionParser.parseFormats(options["formats"]), - enableAllPotentialBarcodes: - BarcodeScanningOptionParser - .parseEnableAllPotentialBarcodes(options["enableAllPotentialBarcodes"]) - ) - } - - private func resolveFileURL(from path: String) -> URL? { - if path.hasPrefix("file://") { - return URL(string: path) - } - - if let url = URL(string: path), url.scheme != nil { - return url - } - - return URL(fileURLWithPath: path) - } - } -#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift b/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift deleted file mode 100644 index 3377065..0000000 --- a/ios/Bridge/Handlers/StaticTextRecognitionHandler.swift +++ /dev/null @@ -1,161 +0,0 @@ -import Foundation - -#if MLKIT_TEXT_RECOGNITION_ANY - class StaticTextRecognitionHandler: IStaticImageHandler { - private let textRecognitionSerializer = TextRecognitionSerializer() - private var cachedUseCase: RecognizeTextUseCase? - - private func getRecognizeTextUseCase(for language: DomainTextRecognitionLanguage) - -> RecognizeTextUseCase - { - if let useCase = cachedUseCase { - return useCase - } - let newUseCase = RecognizeTextUseCase( - imagePreprocessor: ImagePreprocessor(), - recognitionService: TextRecognitionServiceFactory.create( - language: language - ) - ) - cachedUseCase = newUseCase - return newUseCase - } - - func process( - path: String, - options: [String: Any], - resolver: @escaping PromiseResolver, - rejecter: @escaping PromiseRejecter - ) { - Task { - do { - guard let fileURL = resolveFileURL(from: path) else { - rejecter( - "IMAGE_NOT_FOUND_ERROR", - "Unsupported image URI: \(path)", - nil - ) - return - } - - if fileURL.isFileURL - && !FileManager.default.fileExists(atPath: fileURL.path) - { - rejecter( - "IMAGE_NOT_FOUND_ERROR", - "Image file not found at path: \(path)", - nil - ) - return - } - - let imageOptions = parseImageOptions(options) - - let languageString = options["language"] as? String ?? "LATIN" - let language = parseLanguage(languageString) - let textOptions = TextRecognitionOptions(language: language) - - let recognizeTextUseCase = getRecognizeTextUseCase(for: language) - - let result = try recognizeTextUseCase.execute( - imageFile: fileURL, - imageOptions: imageOptions, - textOptions: textOptions - ) - - let serializedResult = TextRecognitionSerializer.toReactNativeMap( - result - ) - - resolver(serializedResult) - } catch { - switch error { - case let nsError as NSError: - if nsError.domain == NSCocoaErrorDomain - && nsError.code == NSFileReadNoSuchFileError - { - rejecter("IMAGE_NOT_FOUND_ERROR", "Image file not found", error) - } else { - rejecter( - "IMAGE_PROCESSING_FAILED_ERROR", - error.localizedDescription, - error - ) - } - default: - rejecter( - "IMAGE_PROCESSING_FAILED_ERROR", - error.localizedDescription, - error - ) - } - } - } - } - - private func parseImageOptions(_ options: [String: Any]) - -> ImagePreprocessingOptions - { - let invertColors = options["invertColors"] as? Bool ?? false - let orientation = (options["orientation"] as? String).flatMap { DomainOrientation(string: $0) } - let roi = parseRegionOfInterest(options["roi"]) - - return ImagePreprocessingOptions( - invertColors: invertColors, - orientation: orientation, - roi: roi - ) - } - - private func parseRegionOfInterest(_ value: Any?) -> DomainRegionOfInterest? { - guard let dictionary = value as? [String: Any], - let x = (dictionary["x"] as? NSNumber)?.doubleValue, - let y = (dictionary["y"] as? NSNumber)?.doubleValue, - let width = (dictionary["width"] as? NSNumber)?.doubleValue, - let height = (dictionary["height"] as? NSNumber)?.doubleValue - else { - return nil - } - - let unit: DomainRegionOfInterestUnit = - (dictionary["unit"] as? String) == "pixel" ? .pixel : .normalized - - return DomainRegionOfInterest( - x: CGFloat(x), - y: CGFloat(y), - width: CGFloat(width), - height: CGFloat(height), - unit: unit - ) - } - - private func parseLanguage(_ language: String) -> DomainTextRecognitionLanguage { - switch language { - case "LATIN": - return .latin - case "CHINESE": - return .chinese - case "DEVANAGARI": - return .devanagari - case "JAPANESE": - return .japanese - case "KOREAN": - return .korean - default: - return .latin - } - } - - private func resolveFileURL(from path: String) -> URL? { - if path.hasPrefix("file://") { - return URL(string: path) - } - - if let url = URL(string: path), url.scheme != nil { - return url - } - - return URL(fileURLWithPath: path) - } - } -#endif // MLKIT_TEXT_RECOGNITION_ANY diff --git a/ios/Bridge/Hybrid/BarcodeParsedValue+toAnyMap.swift b/ios/Bridge/Hybrid/BarcodeParsedValue+toAnyMap.swift new file mode 100644 index 0000000..ed3ec08 --- /dev/null +++ b/ios/Bridge/Hybrid/BarcodeParsedValue+toAnyMap.swift @@ -0,0 +1,34 @@ +import Foundation +import NitroModules + +#if MLKIT_BARCODE_SCANNING + extension BarcodeParsedValue { + func toAnyMap() throws -> AnyMap { + let map = AnyMap(withPreallocatedSize: 2) + map.setString(key: "type", value: type) + map.setObject(key: "data", value: try data.mapValues { try toAnyValue($0) }) + return map + } + } + + private func toAnyValue(_ value: Any?) throws -> AnyValue { + switch value { + case nil, is NSNull: + return .null + case let value as Bool: + return .bool(value) + case let value as String: + return .string(value) + case let value as NSNumber: + return .number(value.doubleValue) + case let value as [Any?]: + return .array(try value.map { try toAnyValue($0) }) + case let value as [String: Any?]: + return .object(try value.mapValues { try toAnyValue($0) }) + default: + throw RuntimeError.error( + withMessage: "Barcode value contains an unsupported value: \(String(describing: value))" + ) + } + } +#endif diff --git a/ios/Bridge/Hybrid/BarcodeScannerOptions+toDomain.swift b/ios/Bridge/Hybrid/BarcodeScannerOptions+toDomain.swift index 371d095..f7f174f 100644 --- a/ios/Bridge/Hybrid/BarcodeScannerOptions+toDomain.swift +++ b/ios/Bridge/Hybrid/BarcodeScannerOptions+toDomain.swift @@ -14,6 +14,7 @@ extension BarcodeScannerOptions { return ImagePreprocessingOptions( invertColors: invertColors ?? false, scaleFactor: CGFloat(scaleFactor ?? 1.0), + orientation: orientation?.toDomainOrientation(), roi: roi?.toDomainRegionOfInterest() ) } diff --git a/ios/Bridge/Hybrid/BarcodeScanningResult+toNitro.swift b/ios/Bridge/Hybrid/BarcodeScanningResult+toNitro.swift index 1f7e49c..40fe015 100644 --- a/ios/Bridge/Hybrid/BarcodeScanningResult+toNitro.swift +++ b/ios/Bridge/Hybrid/BarcodeScanningResult+toNitro.swift @@ -1,24 +1,27 @@ -extension BarcodeScanningResult { - func toNitroResult() -> BarcodeScannerResult { - return BarcodeScannerResult( - barcodes: barcodes.map { $0.toNitroBarcode() } - ) +#if MLKIT_BARCODE_SCANNING + extension BarcodeScanningResult { + func toNitroResult() throws -> BarcodeScannerResult { + return BarcodeScannerResult( + barcodes: try barcodes.map { try $0.toNitroBarcode() } + ) + } } -} -private extension BarcodeData { - func toNitroBarcode() -> BarcodeScannerBarcode { - return BarcodeScannerBarcode( - bounds: bounds?.toNitroBoundingBox(), - corners: corners?.map { $0.toNitroCorner() }, - format: Double(format), - formatName: BarcodeFormat(fromString: formatName) ?? .unknown, - valueType: Double(valueType), - valueTypeName: BarcodeValueTypeName(fromString: valueTypeName) ?? .typeUnknown, - rawValue: rawValue, - displayValue: displayValue, - rawBytes: rawBytes?.map { Double($0) }, - isPotential: isPotential - ) + extension BarcodeData { + fileprivate func toNitroBarcode() throws -> BarcodeScannerBarcode { + return BarcodeScannerBarcode( + bounds: bounds?.toNitroBoundingBox(), + corners: corners?.map { $0.toNitroCorner() }, + format: Double(format), + formatName: BarcodeFormat(fromString: formatName) ?? .unknown, + valueType: Double(valueType), + valueTypeName: BarcodeValueTypeName(fromString: valueTypeName) ?? .typeUnknown, + rawValue: rawValue, + displayValue: displayValue, + rawBytes: rawBytes?.map { Double($0) }, + value: try value?.toAnyMap(), + isPotential: isPotential + ) + } } -} +#endif diff --git a/ios/Bridge/Hybrid/HybridBarcodeScanner.swift b/ios/Bridge/Hybrid/HybridBarcodeScanner.swift index 7f17eec..3dfb13c 100644 --- a/ios/Bridge/Hybrid/HybridBarcodeScanner.swift +++ b/ios/Bridge/Hybrid/HybridBarcodeScanner.swift @@ -4,11 +4,15 @@ import VisionCamera final class HybridBarcodeScanner: HybridBarcodeScannerSpec { private let options: BarcodeScannerOptions + private let recognitionQueue = DispatchQueue( + label: "com.visioncameramlkit.barcode-scanner" + ) #if MLKIT_BARCODE_SCANNING private let imagePreprocessor = ImagePreprocessor() private let barcodeOptions: BarcodeScanningOptions private let imageOptions: ImagePreprocessingOptions private let recognitionService: MLKitBarcodeScanningService + private let staticRecognitionService: MLKitBarcodeScanningService #endif init(options: BarcodeScannerOptions) { @@ -20,24 +24,58 @@ final class HybridBarcodeScanner: HybridBarcodeScannerSpec { self.recognitionService = BarcodeScanningServiceFactory.create( options: resolvedBarcodeOptions ) + self.staticRecognitionService = BarcodeScanningServiceFactory.create( + options: resolvedBarcodeOptions + ) #endif } - func recognize(frame: any HybridFrameSpec, args: MLKitBaseArguments?) throws -> BarcodeScannerResult { + func recognize(frame: any HybridFrameSpec, args: MLKitBaseArguments?) throws + -> BarcodeScannerResult + { #if MLKIT_BARCODE_SCANNING + let callImageOptions = imageOptions.withOutputOrientation( + args?.outputOrientation?.toOutputOrientation() + ) guard let processedImage = imagePreprocessor.preprocessFrame( frame: frame, - options: imageOptions + options: callImageOptions ) else { throw RuntimeError.error(withMessage: "Failed to preprocess VisionCamera frame.") } let result = try recognitionService.recognize(image: processedImage) - return result.remapped(using: processedImage.metadata).toNitroResult() + return try result.remapped(using: processedImage.metadata).toNitroResult() + #else + throw RuntimeError.error( + withMessage: "BarcodeScanning is not enabled in the native ML Kit configuration.") + #endif + } + + func recognizeImage(uri: String) throws -> Promise { + #if MLKIT_BARCODE_SCANNING + return Promise.parallel(recognitionQueue) { + let imageFile = try StaticImageURLResolver.resolve(uri) + guard + let processedImage = self.imagePreprocessor.preprocessImage( + imageFile: imageFile, + options: self.imageOptions + ) + else { + throw RuntimeError.error(withMessage: "Failed to preprocess static image: \(uri)") + } + + let result = try self.staticRecognitionService.recognize(image: processedImage) + return try result.remapped(using: processedImage.metadata).toNitroResult() + } #else - throw RuntimeError.error(withMessage: "BarcodeScanning is not enabled in the native ML Kit configuration.") + return Promise.rejected( + withError: RuntimeError.error( + withMessage: "BarcodeScanning is not enabled in the native ML Kit configuration." + ) + ) #endif } } diff --git a/ios/Bridge/Hybrid/HybridTextRecognizer.swift b/ios/Bridge/Hybrid/HybridTextRecognizer.swift index 75bff67..d9b42b0 100644 --- a/ios/Bridge/Hybrid/HybridTextRecognizer.swift +++ b/ios/Bridge/Hybrid/HybridTextRecognizer.swift @@ -4,11 +4,15 @@ import VisionCamera final class HybridTextRecognizer: HybridTextRecognizerSpec { private let options: TextRecognizerOptions + private let recognitionQueue = DispatchQueue( + label: "com.visioncameramlkit.text-recognizer" + ) #if MLKIT_TEXT_RECOGNITION_ANY private let imagePreprocessor = ImagePreprocessor() private let textOptions: TextRecognitionOptions private let imageOptions: ImagePreprocessingOptions private let recognitionService: MLKitTextRecognitionService + private let staticRecognitionService: MLKitTextRecognitionService #endif init(options: TextRecognizerOptions) { @@ -20,15 +24,23 @@ final class HybridTextRecognizer: HybridTextRecognizerSpec { self.recognitionService = TextRecognitionServiceFactory.create( language: resolvedTextOptions.language ) + self.staticRecognitionService = TextRecognitionServiceFactory.create( + language: resolvedTextOptions.language + ) #endif } - func recognize(frame: any HybridFrameSpec, args: MLKitBaseArguments?) throws -> TextRecognitionResult { + func recognize(frame: any HybridFrameSpec, args: MLKitBaseArguments?) throws + -> TextRecognitionResult + { #if MLKIT_TEXT_RECOGNITION_ANY + let callImageOptions = imageOptions.withOutputOrientation( + args?.outputOrientation?.toOutputOrientation() + ) guard let processedImage = imagePreprocessor.preprocessFrame( frame: frame, - options: imageOptions + options: callImageOptions ) else { throw RuntimeError.error(withMessage: "Failed to preprocess VisionCamera frame.") @@ -37,7 +49,33 @@ final class HybridTextRecognizer: HybridTextRecognizerSpec { let result = try recognitionService.recognize(image: processedImage) return result.remapped(using: processedImage.metadata).toNitroResult() #else - throw RuntimeError.error(withMessage: "TextRecognition is not enabled in the native ML Kit configuration.") + throw RuntimeError.error( + withMessage: "TextRecognition is not enabled in the native ML Kit configuration.") + #endif + } + + func recognizeImage(uri: String) throws -> Promise { + #if MLKIT_TEXT_RECOGNITION_ANY + return Promise.parallel(recognitionQueue) { + let imageFile = try StaticImageURLResolver.resolve(uri) + guard + let processedImage = self.imagePreprocessor.preprocessImage( + imageFile: imageFile, + options: self.imageOptions + ) + else { + throw RuntimeError.error(withMessage: "Failed to preprocess static image: \(uri)") + } + + let result = try self.staticRecognitionService.recognize(image: processedImage) + return result.remapped(using: processedImage.metadata).toNitroResult() + } + #else + return Promise.rejected( + withError: RuntimeError.error( + withMessage: "TextRecognition is not enabled in the native ML Kit configuration." + ) + ) #endif } } diff --git a/ios/Bridge/Hybrid/Orientation+toDomain.swift b/ios/Bridge/Hybrid/Orientation+toDomain.swift new file mode 100644 index 0000000..a750271 --- /dev/null +++ b/ios/Bridge/Hybrid/Orientation+toDomain.swift @@ -0,0 +1,19 @@ +extension Orientation { + func toDomainOrientation() -> DomainOrientation { + switch self { + case .portrait: return .portrait + case .portraitUpsideDown: return .portraitUpsideDown + case .landscapeLeft: return .landscapeLeft + case .landscapeRight: return .landscapeRight + } + } + + func toOutputOrientation() -> OutputOrientation { + switch self { + case .portrait: return .portrait + case .portraitUpsideDown: return .portraitUpsideDown + case .landscapeLeft: return .landscapeLeft + case .landscapeRight: return .landscapeRight + } + } +} diff --git a/ios/Bridge/Hybrid/TextRecognizerOptions+toDomain.swift b/ios/Bridge/Hybrid/TextRecognizerOptions+toDomain.swift index dfc3b86..3706487 100644 --- a/ios/Bridge/Hybrid/TextRecognizerOptions+toDomain.swift +++ b/ios/Bridge/Hybrid/TextRecognizerOptions+toDomain.swift @@ -13,6 +13,7 @@ extension TextRecognizerOptions { return ImagePreprocessingOptions( invertColors: invertColors ?? false, scaleFactor: CGFloat(scaleFactor ?? 1.0), + orientation: orientation?.toDomainOrientation(), roi: roi?.toDomainRegionOfInterest() ) } diff --git a/ios/Bridge/Parsers/BarcodeScanningOptionParser.swift b/ios/Bridge/Parsers/BarcodeScanningOptionParser.swift deleted file mode 100644 index 85a17aa..0000000 --- a/ios/Bridge/Parsers/BarcodeScanningOptionParser.swift +++ /dev/null @@ -1,21 +0,0 @@ -import Foundation - -#if MLKIT_BARCODE_SCANNING - enum BarcodeScanningOptionParser { - static func parseFormats(_ value: Any?) -> [BarcodeFormatOption] { - guard let items = value as? [Any] else { - return [] - } - return - items - .compactMap { $0 as? String } - .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } - .compactMap { BarcodeFormatOption(rawValue: $0) } - } - - static func parseEnableAllPotentialBarcodes(_ value: Any?) -> Bool { - return value as? Bool ?? false - } - - } -#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Domain/Models/ImagePreprocessingOptions+withOutputOrientation.swift b/ios/Domain/Models/ImagePreprocessingOptions+withOutputOrientation.swift new file mode 100644 index 0000000..7639c93 --- /dev/null +++ b/ios/Domain/Models/ImagePreprocessingOptions+withOutputOrientation.swift @@ -0,0 +1,11 @@ +extension ImagePreprocessingOptions { + func withOutputOrientation(_ outputOrientation: OutputOrientation?) -> ImagePreprocessingOptions { + return ImagePreprocessingOptions( + invertColors: invertColors, + outputOrientation: outputOrientation, + scaleFactor: scaleFactor, + orientation: orientation, + roi: roi + ) + } +} diff --git a/ios/Domain/Models/StaticImageDecodePlan.swift b/ios/Domain/Models/StaticImageDecodePlan.swift new file mode 100644 index 0000000..f49d17f --- /dev/null +++ b/ios/Domain/Models/StaticImageDecodePlan.swift @@ -0,0 +1,80 @@ +import Foundation + +struct StaticImageDecodePlan: Equatable { + let sourceWidth: Int + let sourceHeight: Int + let outputWidth: Int + let outputHeight: Int + + var thumbnailMaxPixelSize: Int { + max(outputWidth, outputHeight) + } + + var outputPixelCount: Int { + outputWidth * outputHeight + } + + func sourceScaleFactor(decodedWidth: Int, decodedHeight: Int) -> Double { + Double(max(decodedWidth, decodedHeight)) + / Double(max(sourceWidth, sourceHeight)) + } + + static func make(sourceWidth: Int, sourceHeight: Int) -> StaticImageDecodePlan? { + guard + sourceWidth > 0, + sourceHeight > 0, + sourceWidth <= StaticImageLimits.maxSourceDimension, + sourceHeight <= StaticImageLimits.maxSourceDimension, + sourceWidth <= StaticImageLimits.maxSourcePixelCount / sourceHeight + else { + return nil + } + + let pixelScale = sqrt( + Double(StaticImageLimits.maxDecodedPixelCount) + / Double(sourceWidth * sourceHeight) + ) + let dimensionScale = + Double(StaticImageLimits.maxDecodedDimension) + / Double(max(sourceWidth, sourceHeight)) + let scale = min(1, pixelScale, dimensionScale) + + let sourceMaxDimension = max(sourceWidth, sourceHeight) + var maxPixelSize = max(1, Int(floor(Double(sourceMaxDimension) * scale))) + + while maxPixelSize > 0 { + // ImageIO may round the secondary dimension up. Plan with ceil so the actual + // thumbnail still satisfies the decoded pixel-count ceiling. + let outputWidth = max( + 1, + Int( + ceil( + Double(sourceWidth) * Double(maxPixelSize) + / Double(sourceMaxDimension) + ) + ) + ) + let outputHeight = max( + 1, + Int( + ceil( + Double(sourceHeight) * Double(maxPixelSize) + / Double(sourceMaxDimension) + ) + ) + ) + + if StaticImageLimits.acceptsDecoded(width: outputWidth, height: outputHeight) { + return StaticImageDecodePlan( + sourceWidth: sourceWidth, + sourceHeight: sourceHeight, + outputWidth: outputWidth, + outputHeight: outputHeight + ) + } + maxPixelSize -= 1 + } + + return nil + } +} diff --git a/ios/Domain/Models/StaticImageLimits.swift b/ios/Domain/Models/StaticImageLimits.swift new file mode 100644 index 0000000..d3b7b43 --- /dev/null +++ b/ios/Domain/Models/StaticImageLimits.swift @@ -0,0 +1,29 @@ +enum StaticImageLimits { + static let maxEncodedBytes = 25 * 1024 * 1024 + static let maxSourcePixelCount = 25_000_000 + static let maxSourceDimension = 10_000 + static let maxDecodedPixelCount = 4_000_000 + static let maxDecodedDimension = 4_096 + + static func acceptsFile( + isRegularFile: Bool?, + isSymbolicLink: Bool?, + encodedBytes: Int? + ) -> Bool { + guard let encodedBytes else { + return false + } + return isRegularFile == true + && isSymbolicLink == false + && encodedBytes >= 0 + && encodedBytes <= maxEncodedBytes + } + + static func acceptsDecoded(width: Int, height: Int) -> Bool { + width > 0 + && height > 0 + && width <= maxDecodedDimension + && height <= maxDecodedDimension + && width <= maxDecodedPixelCount / height + } +} diff --git a/ios/Infrastructure/Image/ImagePreprocessor.swift b/ios/Infrastructure/Image/ImagePreprocessor.swift index 4a617f8..0ff0756 100644 --- a/ios/Infrastructure/Image/ImagePreprocessor.swift +++ b/ios/Infrastructure/Image/ImagePreprocessor.swift @@ -1,4 +1,5 @@ import Foundation +import ImageIO #if canImport(MLKitVision) && canImport(VisionCamera) import MLKitVision @@ -6,6 +7,11 @@ import Foundation class ImagePreprocessor: IImagePreprocessor { + private struct DecodedStaticImage { + let image: UIImage + let sourceScaleFactor: CGFloat + } + // Reuse CIContext across frames to avoid repeated allocation overhead // CIContext creation is expensive (~10-20ms), reusing reduces per-frame cost significantly private static let sharedContext: CIContext = { @@ -31,8 +37,7 @@ import Foundation private func createScaledVisionImage( from ciImage: CIImage, scaleFactor: CGFloat, - frameOrientation: UIImage.Orientation, - outputOrientation: OutputOrientation?, + visionOrientation: UIImage.Orientation, roi: DomainRegionOfInterest? ) -> VisionImage? { var workingImage = ciImage @@ -75,10 +80,7 @@ import Foundation ) let visionImage = VisionImage(image: uiImage) - visionImage.orientation = getVisionOrientation( - frameOrientation: frameOrientation, - outputOrientation: outputOrientation - ) + visionImage.orientation = visionOrientation return visionImage } @@ -102,6 +104,10 @@ import Foundation let effectiveScale = clampScale(options.scaleFactor) let frameOrientation = frame.orientation.asUIImageOrientation + let visionOrientation = resolveVisionOrientation( + frameOrientation: frameOrientation, + outputOrientation: options.outputOrientation + ) if let roi = options.roi { guard (try? roi.validate()) != nil else { @@ -115,8 +121,7 @@ import Foundation guard let invertedImage = createInvertedVisionImageFromFrame( sampleBuffer: sampleBuffer, - frameOrientation: frameOrientation, - outputOrientation: options.outputOrientation, + visionOrientation: visionOrientation, scaleFactor: effectiveScale, roi: options.roi ) @@ -128,8 +133,7 @@ import Foundation guard let convertedImage = createVisionImageFromFrame( sampleBuffer: sampleBuffer, - frameOrientation: frameOrientation, - outputOrientation: options.outputOrientation, + visionOrientation: visionOrientation, scaleFactor: effectiveScale, roi: options.roi ) @@ -171,7 +175,7 @@ import Foundation let metadata = ImageMetadata( width: width, height: height, - orientation: frameOrientation, + orientation: visionOrientation, isInverted: options.invertColors, offsetX: offsetX, offsetY: offsetY, @@ -183,8 +187,7 @@ import Foundation private func createVisionImageFromFrame( sampleBuffer: CMSampleBuffer, - frameOrientation: UIImage.Orientation, - outputOrientation: OutputOrientation?, + visionOrientation: UIImage.Orientation, scaleFactor: CGFloat, roi: DomainRegionOfInterest? ) -> VisionImage? { @@ -213,8 +216,7 @@ import Foundation guard let convertedImage = convertToSupportedFormat( pixelBuffer: pixelBuffer, - frameOrientation: frameOrientation, - outputOrientation: outputOrientation, + visionOrientation: visionOrientation, scaleFactor: scaleFactor, roi: roi ) @@ -228,17 +230,13 @@ import Foundation // Fallback: Use CMSampleBuffer directly (for BGRA, scaleFactor == 1.0, and no ROI) let image = VisionImage(buffer: sampleBuffer) - image.orientation = getVisionOrientation( - frameOrientation: frameOrientation, - outputOrientation: outputOrientation - ) + image.orientation = visionOrientation return image } private func createInvertedVisionImageFromFrame( sampleBuffer: CMSampleBuffer, - frameOrientation: UIImage.Orientation, - outputOrientation: OutputOrientation?, + visionOrientation: UIImage.Orientation, scaleFactor: CGFloat, roi: DomainRegionOfInterest? ) -> VisionImage? { @@ -256,13 +254,12 @@ import Foundation return createScaledVisionImage( from: invertedCIImage, scaleFactor: scaleFactor, - frameOrientation: frameOrientation, - outputOrientation: outputOrientation, + visionOrientation: visionOrientation, roi: roi ) } - private func getVisionOrientation( + private func resolveVisionOrientation( frameOrientation: UIImage.Orientation, outputOrientation: OutputOrientation? ) @@ -290,8 +287,7 @@ import Foundation private func convertToSupportedFormat( pixelBuffer: CVPixelBuffer, - frameOrientation: UIImage.Orientation, - outputOrientation: OutputOrientation?, + visionOrientation: UIImage.Orientation, scaleFactor: CGFloat, roi: DomainRegionOfInterest? ) -> VisionImage? { @@ -302,8 +298,7 @@ import Foundation return createScaledVisionImage( from: ciImage, scaleFactor: scaleFactor, - frameOrientation: frameOrientation, - outputOrientation: outputOrientation, + visionOrientation: visionOrientation, roi: roi ) } @@ -311,24 +306,29 @@ import Foundation func preprocessImage(imageFile: URL, options: ImagePreprocessingOptions) -> ProcessedImage? { - guard let imageData = try? Data(contentsOf: imageFile), - let uiImage = UIImage(data: imageData) + guard + let decoded = decodeStaticImage( + imageFile, + explicitOrientation: options.orientation?.asUIImageOrientation + ) else { return nil } - // Use user-provided orientation override when available. - // Otherwise use the resulting UIImage orientation after processing. - let requestedOrientation = options.orientation?.asUIImageOrientation + // An explicit orientation describes the source pixels and overrides embedded EXIF. + // Normalize either orientation into pixels before resolving ROI coordinates. + let effectiveOrientation = + options.orientation?.asUIImageOrientation ?? decoded.image.imageOrientation + guard let normalizedImage = decoded.image.normalized(to: effectiveOrientation) else { + return nil + } let effectiveScale = clampScale(options.scaleFactor) - var processedImage = uiImage + var processedImage = normalizedImage var offsetX: CGFloat = 0 var offsetY: CGFloat = 0 - // Apply optional ROI crop, right after decode/orientation-resolution and before - // scale/invert (UIImage.imageOrientation metadata already encodes orientation for the - // static path, so there's no separate rotate step here). + // Apply optional ROI crop in normalized, visually oriented pixel coordinates. if let roi = options.roi { guard (try? roi.validate()) != nil else { return nil @@ -349,12 +349,12 @@ import Foundation return nil } - offsetX = pixelRect.origin.x - offsetY = pixelRect.origin.y + offsetX = pixelRect.origin.x / decoded.sourceScaleFactor + offsetY = pixelRect.origin.y / decoded.sourceScaleFactor processedImage = UIImage( cgImage: croppedCGImage, scale: processedImage.scale, - orientation: processedImage.imageOrientation + orientation: .up ) } @@ -370,8 +370,7 @@ import Foundation // Create VisionImage let visionImage = VisionImage(image: processedImage) - let visionOrientation = requestedOrientation ?? processedImage.imageOrientation - visionImage.orientation = visionOrientation + visionImage.orientation = .up let pixelWidth = processedImage.cgImage?.width @@ -383,16 +382,77 @@ import Foundation let metadata = ImageMetadata( width: pixelWidth, height: pixelHeight, - orientation: visionOrientation, + orientation: .up, isInverted: options.invertColors, offsetX: offsetX, offsetY: offsetY, - scaleFactor: effectiveScale + scaleFactor: decoded.sourceScaleFactor * effectiveScale ) return ProcessedImage(image: visionImage, metadata: metadata) } + private func decodeStaticImage( + _ imageFile: URL, + explicitOrientation: UIImage.Orientation? + ) -> DecodedStaticImage? { + guard + let source = CGImageSourceCreateWithURL(imageFile as CFURL, nil), + let properties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) + as? [CFString: Any], + let width = properties[kCGImagePropertyPixelWidth] as? Int, + let height = properties[kCGImagePropertyPixelHeight] as? Int, + let decodePlan = StaticImageDecodePlan.make( + sourceWidth: width, + sourceHeight: height + ) + else { + return nil + } + + let thumbnailOptions: [CFString: Any] = [ + kCGImageSourceCreateThumbnailFromImageAlways: true, + kCGImageSourceCreateThumbnailWithTransform: explicitOrientation == nil, + kCGImageSourceThumbnailMaxPixelSize: decodePlan.thumbnailMaxPixelSize, + kCGImageSourceShouldCacheImmediately: true, + ] + + guard + let thumbnail = CGImageSourceCreateThumbnailAtIndex( + source, + 0, + thumbnailOptions as CFDictionary + ) + else { + return nil + } + + guard + StaticImageLimits.acceptsDecoded( + width: thumbnail.width, + height: thumbnail.height + ) + else { + return nil + } + + let actualScale = CGFloat( + decodePlan.sourceScaleFactor( + decodedWidth: thumbnail.width, + decodedHeight: thumbnail.height + ) + ) + + return DecodedStaticImage( + image: UIImage( + cgImage: thumbnail, + scale: 1, + orientation: explicitOrientation ?? .up + ), + sourceScaleFactor: actualScale + ) + } + private func scaleUIImage(_ image: UIImage, scaleFactor: CGFloat) -> UIImage { let targetSize = CGSize( width: image.size.width * scaleFactor, diff --git a/ios/Infrastructure/Image/StaticImageURLResolver.swift b/ios/Infrastructure/Image/StaticImageURLResolver.swift new file mode 100644 index 0000000..03eb9a4 --- /dev/null +++ b/ios/Infrastructure/Image/StaticImageURLResolver.swift @@ -0,0 +1,49 @@ +import Foundation +import NitroModules + +enum StaticImageURLResolver { + static func resolve(_ uri: String) throws -> URL { + let fileURL: URL + + if let url = URL(string: uri), url.scheme != nil { + guard url.isFileURL else { + throw RuntimeError.error( + withMessage: "Static image URI must use the file scheme: \(uri)" + ) + } + fileURL = url + } else { + guard uri.hasPrefix("/") else { + throw RuntimeError.error( + withMessage: "Static image URI must be a file URL or absolute path: \(uri)" + ) + } + fileURL = URL(fileURLWithPath: uri) + } + + let path = fileURL.standardizedFileURL.path + guard FileManager.default.fileExists(atPath: path) else { + throw RuntimeError.error(withMessage: "Static image file does not exist: \(path)") + } + guard FileManager.default.isReadableFile(atPath: path) else { + throw RuntimeError.error(withMessage: "Static image file is not readable: \(path)") + } + + let resourceValues = try fileURL.resourceValues( + forKeys: [.fileSizeKey, .isRegularFileKey, .isSymbolicLinkKey] + ) + guard + StaticImageLimits.acceptsFile( + isRegularFile: resourceValues.isRegularFile, + isSymbolicLink: resourceValues.isSymbolicLink, + encodedBytes: resourceValues.fileSize + ) + else { + throw RuntimeError.error( + withMessage: "Static image must be a regular, non-symlink file up to 25 MB." + ) + } + + return URL(fileURLWithPath: path) + } +} diff --git a/ios/Infrastructure/Image/UIImage+normalizedOrientation.swift b/ios/Infrastructure/Image/UIImage+normalizedOrientation.swift new file mode 100644 index 0000000..0b010c7 --- /dev/null +++ b/ios/Infrastructure/Image/UIImage+normalizedOrientation.swift @@ -0,0 +1,36 @@ +import UIKit + +extension UIImage { + func normalized(to orientation: UIImage.Orientation) -> UIImage? { + guard let cgImage else { + return nil + } + + // `.up` means the pixel data already matches the desired visual orientation, + // so there's nothing to bake in — skip the redundant render pass. + if orientation == .up { + return UIImage(cgImage: cgImage, scale: 1, orientation: .up) + } + + let swapsDimensions: Bool + switch orientation { + case .left, .leftMirrored, .right, .rightMirrored: + swapsDimensions = true + default: + swapsDimensions = false + } + + let pixelSize = CGSize( + width: swapsDimensions ? cgImage.height : cgImage.width, + height: swapsDimensions ? cgImage.width : cgImage.height + ) + let orientedImage = UIImage(cgImage: cgImage, scale: 1, orientation: orientation) + let rendererFormat = UIGraphicsImageRendererFormat.default() + rendererFormat.scale = 1 + let renderer = UIGraphicsImageRenderer(size: pixelSize, format: rendererFormat) + + return renderer.image { _ in + orientedImage.draw(in: CGRect(origin: .zero, size: pixelSize)) + } + } +} diff --git a/ios/Infrastructure/MLKit/Adapters/MLKitBarcodeAdapter.swift b/ios/Infrastructure/MLKit/Adapters/MLKitBarcodeAdapter.swift index 6041bf6..ab8d210 100644 --- a/ios/Infrastructure/MLKit/Adapters/MLKitBarcodeAdapter.swift +++ b/ios/Infrastructure/MLKit/Adapters/MLKitBarcodeAdapter.swift @@ -32,8 +32,8 @@ import Foundation data: [ "ssid": wifi.ssid, "password": wifi.password, - "type": String(describing: wifi.type), - "typeName": String(describing: wifi.type), + "encryptionType": Int(wifi.type.rawValue), + "encryptionTypeName": wifiEncryptionName(wifi.type), ] ) } @@ -65,8 +65,8 @@ import Foundation "address": email.address, "subject": email.subject, "body": email.body, - "type": String(describing: email.type), - "typeName": String(describing: email.type), + "type": Int(email.type.rawValue), + "typeName": emailTypeName(email.type), ] ) } @@ -76,8 +76,8 @@ import Foundation type: "TYPE_PHONE", data: [ "number": phone.number, - "type": String(describing: phone.type), - "typeName": String(describing: phone.type), + "type": Int(phone.type.rawValue), + "typeName": phoneTypeName(phone.type), ] ) } @@ -114,29 +114,9 @@ import Foundation "name": contactInfo.name.map { toPersonName($0) }, "organization": contactInfo.organization, "title": contactInfo.jobTitle, - "phones": contactInfo.phones?.map { phone in - [ - "number": phone.number, - "type": String(describing: phone.type), - "typeName": String(describing: phone.type), - ] - }, - "emails": contactInfo.emails?.map { email in - [ - "address": email.address, - "subject": email.subject, - "body": email.body, - "type": String(describing: email.type), - "typeName": String(describing: email.type), - ] - }, - "addresses": contactInfo.addresses?.map { address in - [ - "addressLines": address.addressLines, - "type": String(describing: address.type), - "typeName": String(describing: address.type), - ] - }, + "phones": contactInfo.phones?.map { toPhone($0) }, + "emails": contactInfo.emails?.map { toEmail($0) }, + "addresses": contactInfo.addresses?.map { toAddress($0) }, "urls": contactInfo.urls, ] ) @@ -165,9 +145,18 @@ import Foundation } if let rawValue = barcode.rawValue { + let type = valueTypeName(barcode.valueType) + let dataKey: String + switch type { + case "TYPE_TEXT": dataKey = "text" + case "TYPE_PRODUCT": dataKey = "product" + case "TYPE_ISBN": dataKey = "isbn" + default: dataKey = "rawValue" + } + return BarcodeParsedValue( - type: valueTypeName(barcode.valueType), - data: ["value": rawValue] + type: type, + data: [dataKey: rawValue] ) } @@ -186,6 +175,32 @@ import Foundation ] } + private static func toPhone(_ phone: BarcodePhone) -> [String: Any?] { + return [ + "number": phone.number, + "type": Int(phone.type.rawValue), + "typeName": phoneTypeName(phone.type), + ] + } + + private static func toEmail(_ email: BarcodeEmail) -> [String: Any?] { + return [ + "address": email.address, + "subject": email.subject, + "body": email.body, + "type": Int(email.type.rawValue), + "typeName": emailTypeName(email.type), + ] + } + + private static func toAddress(_ address: BarcodeAddress) -> [String: Any?] { + return [ + "addressLines": address.addressLines, + "type": Int(address.type.rawValue), + "typeName": addressTypeName(address.type), + ] + } + private static func toDateInfo(_ date: Date?) -> [String: Any?]? { guard let date else { return nil @@ -253,7 +268,8 @@ import Foundation } } - private static func valueTypeName(_ valueType: MLKitBarcodeScanning.BarcodeValueType) -> String { + private static func valueTypeName(_ valueType: MLKitBarcodeScanning.BarcodeValueType) -> String + { switch Int(valueType.rawValue) { case 1: return "TYPE_CONTACT_INFO" case 2: return "TYPE_EMAIL" @@ -270,5 +286,40 @@ import Foundation default: return "TYPE_UNKNOWN" } } + + private static func wifiEncryptionName(_ type: BarcodeWiFiEncryptionType) -> String { + switch Int(type.rawValue) { + case 1: return "OPEN" + case 2: return "WPA" + case 3: return "WEP" + default: return "UNKNOWN" + } + } + + private static func phoneTypeName(_ type: BarcodePhoneType) -> String { + switch Int(type.rawValue) { + case 1: return "WORK" + case 2: return "HOME" + case 3: return "FAX" + case 4: return "MOBILE" + default: return "UNKNOWN" + } + } + + private static func emailTypeName(_ type: BarcodeEmailType) -> String { + switch Int(type.rawValue) { + case 1: return "WORK" + case 2: return "HOME" + default: return "UNKNOWN" + } + } + + private static func addressTypeName(_ type: BarcodeAddressType) -> String { + switch Int(type.rawValue) { + case 1: return "WORK" + case 2: return "HOME" + default: return "UNKNOWN" + } + } } #endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift b/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift deleted file mode 100644 index a52bb74..0000000 --- a/ios/Infrastructure/Modules/VisionCameraMLKitModule.swift +++ /dev/null @@ -1,45 +0,0 @@ -import Foundation -import React - -@objc(VisionCameraMLKitModule) -class VisionCameraMLKitModule: NSObject { - private lazy var handlers: [String: IStaticImageHandler] = { - var handlers: [String: IStaticImageHandler] = [:] - - #if MLKIT_TEXT_RECOGNITION_ANY - handlers[MLKitFeatureKeys.TEXT_RECOGNITION] = StaticTextRecognitionHandler() - #endif - - #if MLKIT_BARCODE_SCANNING - handlers[MLKitFeatureKeys.BARCODE_SCANNING] = StaticBarcodeScanningHandler() - #endif - - return handlers - }() - - @objc - func processImage( - _ feature: String, - path: String, - options: Any?, - resolver resolve: @escaping RCTPromiseResolveBlock, - rejecter reject: @escaping RCTPromiseRejectBlock - ) { - let safeOptions = options as? [String: Any] ?? [:] - guard let handler = handlers[feature] else { - reject("UNSUPPORTED_FEATURE", "Feature \(feature) is not supported", nil) - return - } - - handler.process( - path: path, - options: safeOptions, - resolver: { result in resolve(result) }, - rejecter: { code, message, error in reject(code, message, error) } - ) - } - - @objc static func requiresMainQueueSetup() -> Bool { - false - } -} diff --git a/ios/Infrastructure/Modules/VisionCameraMLKitModuleBridge.m b/ios/Infrastructure/Modules/VisionCameraMLKitModuleBridge.m deleted file mode 100644 index 3e5e466..0000000 --- a/ios/Infrastructure/Modules/VisionCameraMLKitModuleBridge.m +++ /dev/null @@ -1,11 +0,0 @@ -#import - -@interface RCT_EXTERN_MODULE(VisionCameraMLKitModule, NSObject) - -RCT_EXTERN_METHOD(processImage:(NSString *)feature - path:(NSString *)path - options:(id)options - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) - -@end diff --git a/ios/Infrastructure/Serializers/BarcodeScanningSerializer.swift b/ios/Infrastructure/Serializers/BarcodeScanningSerializer.swift deleted file mode 100644 index e692f93..0000000 --- a/ios/Infrastructure/Serializers/BarcodeScanningSerializer.swift +++ /dev/null @@ -1,89 +0,0 @@ -import Foundation - -#if MLKIT_BARCODE_SCANNING - class BarcodeScanningSerializer { - - static func toReactNativeMap(_ result: BarcodeScanningResult) -> [String: Any] { - var map = [String: Any]() - map["barcodes"] = result.barcodes.map { toBarcodeMap($0) } - return map - } - - private static func toBarcodeMap(_ barcode: BarcodeData) -> [String: Any] { - var map = [String: Any]() - if let bounds = barcode.bounds { - map["bounds"] = toBoundsMap(bounds) - } - if let corners = barcode.corners { - map["corners"] = corners.map { toCornerMap($0) } - } - map["format"] = barcode.format - map["formatName"] = barcode.formatName - map["valueType"] = barcode.valueType - map["valueTypeName"] = barcode.valueTypeName - map["rawValue"] = barcode.rawValue as Any - map["displayValue"] = barcode.displayValue as Any - map["rawBytes"] = barcode.rawBytes as Any - map["isPotential"] = barcode.isPotential - if let value = barcode.value { - map["value"] = toValueMap(value) - } - return map - } - - private static func toValueMap(_ value: BarcodeParsedValue) -> [String: Any] { - var map = [String: Any]() - map["type"] = value.type - map["data"] = toNonOptionalDictionary(value.data) - return map - } - - private static func toBoundsMap(_ bounds: DomainBoundingBox) -> [String: Double] { - return [ - "x": bounds.x, - "y": bounds.y, - "centerX": bounds.centerX, - "centerY": bounds.centerY, - "width": bounds.width, - "height": bounds.height, - "top": bounds.top, - "left": bounds.left, - "bottom": bounds.bottom, - "right": bounds.right, - ] - } - - private static func toCornerMap(_ corner: DomainCorner) -> [String: Double] { - return [ - "x": corner.x, - "y": corner.y, - ] - } - - private static func toNonOptionalDictionary(_ dictionary: [String: Any?]) - -> [String: Any] - { - var output: [String: Any] = [:] - for (key, value) in dictionary { - output[key] = normalize(value) - } - return output - } - - private static func normalize(_ value: Any?) -> Any { - guard let value = value else { - return NSNull() - } - - if let dictionary = value as? [String: Any?] { - return toNonOptionalDictionary(dictionary) - } - - if let array = value as? [Any?] { - return array.map { normalize($0) } - } - - return value - } - } -#endif // MLKIT_BARCODE_SCANNING diff --git a/ios/Infrastructure/Serializers/TextRecognitionSerializer.swift b/ios/Infrastructure/Serializers/TextRecognitionSerializer.swift deleted file mode 100644 index 47bf703..0000000 --- a/ios/Infrastructure/Serializers/TextRecognitionSerializer.swift +++ /dev/null @@ -1,112 +0,0 @@ -import Foundation - -class TextRecognitionSerializer { - - static func toReactNativeMap(_ result: DomainTextRecognitionResult) -> [String: Any] { - var map = [String: Any]() - map["text"] = result.text - map["blocks"] = toBlocksArray(result.blocks) - return map - } - - private static func toBlocksArray(_ blocks: [DomainTextBlock]) -> [[String: Any]] { - return blocks.map { toBlockMap($0) } - } - - private static func toBlockMap(_ block: DomainTextBlock) -> [String: Any] { - var map = [String: Any]() - map["text"] = block.text - if let bounds = block.bounds { - map["bounds"] = toBoundsMap(bounds) - } - if let corners = block.corners { - map["corners"] = toCornersArray(corners) - } - map["lines"] = toLinesArray(block.lines) - map["languages"] = block.languages - return map - } - - private static func toLinesArray(_ lines: [DomainTextLine]) -> [[String: Any]] { - return lines.map { toLineMap($0) } - } - - private static func toLineMap(_ line: DomainTextLine) -> [String: Any] { - var map = [String: Any]() - map["text"] = line.text - if let bounds = line.bounds { - map["bounds"] = toBoundsMap(bounds) - } - if let corners = line.corners { - map["corners"] = toCornersArray(corners) - } - map["elements"] = toElementsArray(line.elements) - map["confidence"] = line.confidence - map["angle"] = line.angle - map["languages"] = line.languages - return map - } - - private static func toElementsArray(_ elements: [DomainTextElement]) -> [[String: - Any]] - { - return elements.map { toElementMap($0) } - } - - private static func toElementMap(_ element: DomainTextElement) -> [String: Any] { - var map = [String: Any]() - map["text"] = element.text - if let bounds = element.bounds { - map["bounds"] = toBoundsMap(bounds) - } - if let corners = element.corners { - map["corners"] = toCornersArray(corners) - } - map["symbols"] = element.symbols.map { toSymbolMap($0) } - map["confidence"] = element.confidence - map["angle"] = element.angle - map["languages"] = element.languages - return map - } - - private static func toSymbolMap(_ symbol: DomainTextSymbol) -> [String: Any] { - var map = [String: Any]() - map["text"] = symbol.text - if let bounds = symbol.bounds { - map["bounds"] = toBoundsMap(bounds) - } - if let corners = symbol.corners { - map["corners"] = toCornersArray(corners) - } - map["confidence"] = symbol.confidence - map["angle"] = symbol.angle - map["languages"] = symbol.languages - return map - } - - private static func toBoundsMap(_ bounds: DomainBoundingBox) -> [String: Double] { - return [ - "x": bounds.x, - "y": bounds.y, - "centerX": bounds.centerX, - "centerY": bounds.centerY, - "width": bounds.width, - "height": bounds.height, - "top": bounds.top, - "left": bounds.left, - "bottom": bounds.bottom, - "right": bounds.right, - ] - } - - private static func toCornersArray(_ corners: [DomainCorner]) -> [[String: Double]] { - return corners.map { toCornerMap($0) } - } - - private static func toCornerMap(_ corner: DomainCorner) -> [String: Double] { - return [ - "x": corner.x, - "y": corner.y, - ] - } -} diff --git a/nitrogen/generated/.gitattributes b/nitrogen/generated/.gitattributes new file mode 100644 index 0000000..fb7a0d5 --- /dev/null +++ b/nitrogen/generated/.gitattributes @@ -0,0 +1 @@ +** linguist-generated=true diff --git a/nitrogen/generated/android/VisionCameraMLKit+autolinking.cmake b/nitrogen/generated/android/VisionCameraMLKit+autolinking.cmake new file mode 100644 index 0000000..45a377a --- /dev/null +++ b/nitrogen/generated/android/VisionCameraMLKit+autolinking.cmake @@ -0,0 +1,86 @@ +# +# VisionCameraMLKit+autolinking.cmake +# This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +# https://github.com/mrousavy/nitro +# Copyright © Marc Rousavy @ Margelo +# + +# This is a CMake file that adds all files generated by Nitrogen +# to the current CMake project. +# +# To use it, add this to your CMakeLists.txt: +# ```cmake +# include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/VisionCameraMLKit+autolinking.cmake) +# ``` + +# Define a flag to check if we are building properly +add_definitions(-DBUILDING_VISIONCAMERAMLKIT_WITH_GENERATED_CMAKE_PROJECT) + +# Enable Raw Props parsing in react-native (for Nitro Views) +add_definitions(-DRN_SERIALIZABLE_STATE) + +# Add all headers that were generated by Nitrogen +include_directories( + "../nitrogen/generated/shared/c++" + "../nitrogen/generated/android/c++" + "../nitrogen/generated/android/" +) + +# Add all .cpp sources that were generated by Nitrogen +target_sources( + # CMake project name (Android C++ library name) + VisionCameraMLKit PRIVATE + # Autolinking Setup + ../nitrogen/generated/android/VisionCameraMLKitOnLoad.cpp + # Shared Nitrogen C++ sources + ../nitrogen/generated/shared/c++/HybridBarcodeScannerSpec.cpp + ../nitrogen/generated/shared/c++/HybridTextRecognizerSpec.cpp + ../nitrogen/generated/shared/c++/HybridVisionCameraMLKitSpec.cpp + # Android-specific Nitrogen C++ sources + ../nitrogen/generated/android/c++/JHybridBarcodeScannerSpec.cpp + ../nitrogen/generated/android/c++/JHybridTextRecognizerSpec.cpp + ../nitrogen/generated/android/c++/JVariant_NullType_Double.cpp + ../nitrogen/generated/android/c++/JHybridVisionCameraMLKitSpec.cpp +) + +# From node_modules/react-native/ReactAndroid/cmake-utils/folly-flags.cmake +# Used in node_modules/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake +target_compile_definitions( + VisionCameraMLKit PRIVATE + -DFOLLY_NO_CONFIG=1 + -DFOLLY_HAVE_CLOCK_GETTIME=1 + -DFOLLY_USE_LIBCPP=1 + -DFOLLY_CFG_NO_COROUTINES=1 + -DFOLLY_MOBILE=1 + -DFOLLY_HAVE_RECVMMSG=1 + -DFOLLY_HAVE_PTHREAD=1 + # Once we target android-23 above, we can comment + # the following line. NDK uses GNU style stderror_r() after API 23. + -DFOLLY_HAVE_XSI_STRERROR_R=1 +) + +# Add all libraries required by the generated specs +find_package(fbjni REQUIRED) # <-- Used for communication between Java <-> C++ +find_package(ReactAndroid REQUIRED) # <-- Used to set up React Native bindings (e.g. CallInvoker/TurboModule) +find_package(react-native-nitro-modules REQUIRED) # <-- Used to create all HybridObjects and use the Nitro core library + +# Link all libraries together +target_link_libraries( + VisionCameraMLKit + fbjni::fbjni # <-- Facebook C++ JNI helpers + ReactAndroid::jsi # <-- RN: JSI + react-native-nitro-modules::NitroModules # <-- NitroModules Core :) +) + +# Link react-native (different prefab between RN 0.75 and RN 0.76) +if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76) + target_link_libraries( + VisionCameraMLKit + ReactAndroid::reactnative # <-- RN: Native Modules umbrella prefab + ) +else() + target_link_libraries( + VisionCameraMLKit + ReactAndroid::react_nativemodule_core # <-- RN: TurboModules Core + ) +endif() diff --git a/nitrogen/generated/android/VisionCameraMLKit+autolinking.gradle b/nitrogen/generated/android/VisionCameraMLKit+autolinking.gradle new file mode 100644 index 0000000..de2ad60 --- /dev/null +++ b/nitrogen/generated/android/VisionCameraMLKit+autolinking.gradle @@ -0,0 +1,27 @@ +/// +/// VisionCameraMLKit+autolinking.gradle +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +/// This is a Gradle file that adds all files generated by Nitrogen +/// to the current Gradle project. +/// +/// To use it, add this to your build.gradle: +/// ```gradle +/// apply from: '../nitrogen/generated/android/VisionCameraMLKit+autolinking.gradle' +/// ``` + +logger.warn("[NitroModules] 🔥 VisionCameraMLKit is boosted by nitro!") + +android { + sourceSets { + main { + java.srcDirs += [ + // Nitrogen files + "${project.projectDir}/../nitrogen/generated/android/kotlin" + ] + } + } +} diff --git a/nitrogen/generated/android/VisionCameraMLKitOnLoad.cpp b/nitrogen/generated/android/VisionCameraMLKitOnLoad.cpp new file mode 100644 index 0000000..8a5e802 --- /dev/null +++ b/nitrogen/generated/android/VisionCameraMLKitOnLoad.cpp @@ -0,0 +1,58 @@ +/// +/// VisionCameraMLKitOnLoad.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#ifndef BUILDING_VISIONCAMERAMLKIT_WITH_GENERATED_CMAKE_PROJECT +#error VisionCameraMLKitOnLoad.cpp is not being built with the autogenerated CMakeLists.txt project. Is a different CMakeLists.txt building this? +#endif + +#include "VisionCameraMLKitOnLoad.hpp" + +#include +#include +#include + +#include "JHybridBarcodeScannerSpec.hpp" +#include "JHybridTextRecognizerSpec.hpp" +#include "JHybridVisionCameraMLKitSpec.hpp" +#include + +namespace margelo::nitro::visioncameramlkit { + +int initialize(JavaVM* vm) { + return facebook::jni::initialize(vm, []() { + ::margelo::nitro::visioncameramlkit::registerAllNatives(); + }); +} + +struct JHybridVisionCameraMLKitSpecImpl: public jni::JavaClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/HybridVisionCameraMLKit;"; + static std::shared_ptr create() { + static const auto constructorFn = javaClassStatic()->getConstructor(); + jni::local_ref javaPart = javaClassStatic()->newObject(constructorFn); + return javaPart->getJHybridVisionCameraMLKitSpec(); + } +}; + +void registerAllNatives() { + using namespace margelo::nitro; + using namespace margelo::nitro::visioncameramlkit; + + // Register native JNI methods + margelo::nitro::visioncameramlkit::JHybridBarcodeScannerSpec::CxxPart::registerNatives(); + margelo::nitro::visioncameramlkit::JHybridTextRecognizerSpec::CxxPart::registerNatives(); + margelo::nitro::visioncameramlkit::JHybridVisionCameraMLKitSpec::CxxPart::registerNatives(); + + // Register Nitro Hybrid Objects + HybridObjectRegistry::registerHybridObjectConstructor( + "VisionCameraMLKit", + []() -> std::shared_ptr { + return JHybridVisionCameraMLKitSpecImpl::create(); + } + ); +} + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/VisionCameraMLKitOnLoad.hpp b/nitrogen/generated/android/VisionCameraMLKitOnLoad.hpp new file mode 100644 index 0000000..ae3c5bf --- /dev/null +++ b/nitrogen/generated/android/VisionCameraMLKitOnLoad.hpp @@ -0,0 +1,34 @@ +/// +/// VisionCameraMLKitOnLoad.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + [[deprecated("Use registerNatives() instead.")]] + int initialize(JavaVM* vm); + + /** + * Register the native (C++) part of VisionCameraMLKit, and autolinks all Hybrid Objects. + * Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`), + * inside a `facebook::jni::initialize(vm, ...)` call. + * Example: + * ```cpp (cpp-adapter.cpp) + * JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { + * return facebook::jni::initialize(vm, []() { + * // register all VisionCameraMLKit HybridObjects + * margelo::nitro::visioncameramlkit::registerNatives(); + * // any other custom registrations go here. + * }); + * } + * ``` + */ + void registerAllNatives(); + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JBarcodeFormat.hpp b/nitrogen/generated/android/c++/JBarcodeFormat.hpp new file mode 100644 index 0000000..e7bac27 --- /dev/null +++ b/nitrogen/generated/android/c++/JBarcodeFormat.hpp @@ -0,0 +1,97 @@ +/// +/// JBarcodeFormat.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "BarcodeFormat.hpp" + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ enum "BarcodeFormat" and the Kotlin enum "BarcodeFormat". + */ + struct JBarcodeFormat final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/BarcodeFormat;"; + + public: + /** + * Convert this Java/Kotlin-based enum to the C++ enum BarcodeFormat. + */ + [[maybe_unused]] + [[nodiscard]] + BarcodeFormat toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldOrdinal = clazz->getField("value"); + int ordinal = this->getFieldValue(fieldOrdinal); + return static_cast(ordinal); + } + + public: + /** + * Create a Java/Kotlin-based enum with the given C++ enum's value. + */ + [[maybe_unused]] + static jni::alias_ref fromCpp(BarcodeFormat value) { + static const auto clazz = javaClassStatic(); + switch (value) { + case BarcodeFormat::UNKNOWN: + static const auto fieldUNKNOWN = clazz->getStaticField("UNKNOWN"); + return clazz->getStaticFieldValue(fieldUNKNOWN); + case BarcodeFormat::ALL_FORMATS: + static const auto fieldALL_FORMATS = clazz->getStaticField("ALL_FORMATS"); + return clazz->getStaticFieldValue(fieldALL_FORMATS); + case BarcodeFormat::CODE_128: + static const auto fieldCODE_128 = clazz->getStaticField("CODE_128"); + return clazz->getStaticFieldValue(fieldCODE_128); + case BarcodeFormat::CODE_39: + static const auto fieldCODE_39 = clazz->getStaticField("CODE_39"); + return clazz->getStaticFieldValue(fieldCODE_39); + case BarcodeFormat::CODE_93: + static const auto fieldCODE_93 = clazz->getStaticField("CODE_93"); + return clazz->getStaticFieldValue(fieldCODE_93); + case BarcodeFormat::CODABAR: + static const auto fieldCODABAR = clazz->getStaticField("CODABAR"); + return clazz->getStaticFieldValue(fieldCODABAR); + case BarcodeFormat::DATA_MATRIX: + static const auto fieldDATA_MATRIX = clazz->getStaticField("DATA_MATRIX"); + return clazz->getStaticFieldValue(fieldDATA_MATRIX); + case BarcodeFormat::EAN_13: + static const auto fieldEAN_13 = clazz->getStaticField("EAN_13"); + return clazz->getStaticFieldValue(fieldEAN_13); + case BarcodeFormat::EAN_8: + static const auto fieldEAN_8 = clazz->getStaticField("EAN_8"); + return clazz->getStaticFieldValue(fieldEAN_8); + case BarcodeFormat::ITF: + static const auto fieldITF = clazz->getStaticField("ITF"); + return clazz->getStaticFieldValue(fieldITF); + case BarcodeFormat::QR_CODE: + static const auto fieldQR_CODE = clazz->getStaticField("QR_CODE"); + return clazz->getStaticFieldValue(fieldQR_CODE); + case BarcodeFormat::UPC_A: + static const auto fieldUPC_A = clazz->getStaticField("UPC_A"); + return clazz->getStaticFieldValue(fieldUPC_A); + case BarcodeFormat::UPC_E: + static const auto fieldUPC_E = clazz->getStaticField("UPC_E"); + return clazz->getStaticFieldValue(fieldUPC_E); + case BarcodeFormat::PDF417: + static const auto fieldPDF417 = clazz->getStaticField("PDF417"); + return clazz->getStaticFieldValue(fieldPDF417); + case BarcodeFormat::AZTEC: + static const auto fieldAZTEC = clazz->getStaticField("AZTEC"); + return clazz->getStaticFieldValue(fieldAZTEC); + default: + std::string stringValue = std::to_string(static_cast(value)); + throw std::invalid_argument("Invalid enum value (" + stringValue + "!"); + } + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JBarcodeScannerBarcode.hpp b/nitrogen/generated/android/c++/JBarcodeScannerBarcode.hpp new file mode 100644 index 0000000..bc882bf --- /dev/null +++ b/nitrogen/generated/android/c++/JBarcodeScannerBarcode.hpp @@ -0,0 +1,137 @@ +/// +/// JBarcodeScannerBarcode.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "BarcodeScannerBarcode.hpp" + +#include "BarcodeFormat.hpp" +#include "BarcodeValueTypeName.hpp" +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include "JBarcodeFormat.hpp" +#include "JBarcodeValueTypeName.hpp" +#include "JBoundingBox.hpp" +#include "JCorner.hpp" +#include +#include +#include +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "BarcodeScannerBarcode" and the Kotlin data class "BarcodeScannerBarcode". + */ + struct JBarcodeScannerBarcode final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/BarcodeScannerBarcode;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct BarcodeScannerBarcode by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + BarcodeScannerBarcode toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldBounds = clazz->getField("bounds"); + jni::local_ref bounds = this->getFieldValue(fieldBounds); + static const auto fieldCorners = clazz->getField>("corners"); + jni::local_ref> corners = this->getFieldValue(fieldCorners); + static const auto fieldFormat = clazz->getField("format"); + double format = this->getFieldValue(fieldFormat); + static const auto fieldFormatName = clazz->getField("formatName"); + jni::local_ref formatName = this->getFieldValue(fieldFormatName); + static const auto fieldValueType = clazz->getField("valueType"); + double valueType = this->getFieldValue(fieldValueType); + static const auto fieldValueTypeName = clazz->getField("valueTypeName"); + jni::local_ref valueTypeName = this->getFieldValue(fieldValueTypeName); + static const auto fieldRawValue = clazz->getField("rawValue"); + jni::local_ref rawValue = this->getFieldValue(fieldRawValue); + static const auto fieldDisplayValue = clazz->getField("displayValue"); + jni::local_ref displayValue = this->getFieldValue(fieldDisplayValue); + static const auto fieldRawBytes = clazz->getField("rawBytes"); + jni::local_ref rawBytes = this->getFieldValue(fieldRawBytes); + static const auto fieldValue = clazz->getField("value"); + jni::local_ref value = this->getFieldValue(fieldValue); + static const auto fieldIsPotential = clazz->getField("isPotential"); + jboolean isPotential = this->getFieldValue(fieldIsPotential); + return BarcodeScannerBarcode( + bounds != nullptr ? std::make_optional(bounds->toCpp()) : std::nullopt, + corners != nullptr ? std::make_optional([&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(corners)) : std::nullopt, + format, + formatName->toCpp(), + valueType, + valueTypeName->toCpp(), + rawValue != nullptr ? std::make_optional(rawValue->toStdString()) : std::nullopt, + displayValue != nullptr ? std::make_optional(displayValue->toStdString()) : std::nullopt, + rawBytes != nullptr ? std::make_optional([&]() { + size_t __size = rawBytes->size(); + std::vector __vector(__size); + rawBytes->getRegion(0, __size, __vector.data()); + return __vector; + }()) : std::nullopt, + value != nullptr ? std::make_optional(value->cthis()->getMap()) : std::nullopt, + static_cast(isPotential) + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const BarcodeScannerBarcode& value) { + using JSignature = JBarcodeScannerBarcode(jni::alias_ref, jni::alias_ref>, double, jni::alias_ref, double, jni::alias_ref, jni::alias_ref, jni::alias_ref, jni::alias_ref, jni::alias_ref, jboolean); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + value.bounds.has_value() ? JBoundingBox::fromCpp(value.bounds.value()) : nullptr, + value.corners.has_value() ? [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = JCorner::fromCpp(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.corners.value()) : nullptr, + value.format, + JBarcodeFormat::fromCpp(value.formatName), + value.valueType, + JBarcodeValueTypeName::fromCpp(value.valueTypeName), + value.rawValue.has_value() ? jni::make_jstring(value.rawValue.value()) : nullptr, + value.displayValue.has_value() ? jni::make_jstring(value.displayValue.value()) : nullptr, + value.rawBytes.has_value() ? [&]() { + size_t __size = value.rawBytes.value().size(); + jni::local_ref __array = jni::JArrayDouble::newArray(__size); + __array->setRegion(0, __size, value.rawBytes.value().data()); + return __array; + }() : nullptr, + value.value.has_value() ? JAnyMap::create(value.value.value()) : nullptr, + value.isPotential + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JBarcodeScannerOptions.hpp b/nitrogen/generated/android/c++/JBarcodeScannerOptions.hpp new file mode 100644 index 0000000..3fecf07 --- /dev/null +++ b/nitrogen/generated/android/c++/JBarcodeScannerOptions.hpp @@ -0,0 +1,108 @@ +/// +/// JBarcodeScannerOptions.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "BarcodeScannerOptions.hpp" + +#include "BarcodeFormat.hpp" +#include "JBarcodeFormat.hpp" +#include "JOrientation.hpp" +#include "JRegionOfInterest.hpp" +#include "JRegionOfInterestUnit.hpp" +#include "Orientation.hpp" +#include "RegionOfInterest.hpp" +#include "RegionOfInterestUnit.hpp" +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "BarcodeScannerOptions" and the Kotlin data class "BarcodeScannerOptions". + */ + struct JBarcodeScannerOptions final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/BarcodeScannerOptions;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct BarcodeScannerOptions by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + BarcodeScannerOptions toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldFormats = clazz->getField>("formats"); + jni::local_ref> formats = this->getFieldValue(fieldFormats); + static const auto fieldEnableAllPotentialBarcodes = clazz->getField("enableAllPotentialBarcodes"); + jni::local_ref enableAllPotentialBarcodes = this->getFieldValue(fieldEnableAllPotentialBarcodes); + static const auto fieldScaleFactor = clazz->getField("scaleFactor"); + jni::local_ref scaleFactor = this->getFieldValue(fieldScaleFactor); + static const auto fieldInvertColors = clazz->getField("invertColors"); + jni::local_ref invertColors = this->getFieldValue(fieldInvertColors); + static const auto fieldFrameProcessInterval = clazz->getField("frameProcessInterval"); + jni::local_ref frameProcessInterval = this->getFieldValue(fieldFrameProcessInterval); + static const auto fieldRoi = clazz->getField("roi"); + jni::local_ref roi = this->getFieldValue(fieldRoi); + static const auto fieldOrientation = clazz->getField("orientation"); + jni::local_ref orientation = this->getFieldValue(fieldOrientation); + return BarcodeScannerOptions( + formats != nullptr ? std::make_optional([&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(formats)) : std::nullopt, + enableAllPotentialBarcodes != nullptr ? std::make_optional(static_cast(enableAllPotentialBarcodes->value())) : std::nullopt, + scaleFactor != nullptr ? std::make_optional(scaleFactor->value()) : std::nullopt, + invertColors != nullptr ? std::make_optional(static_cast(invertColors->value())) : std::nullopt, + frameProcessInterval != nullptr ? std::make_optional(frameProcessInterval->value()) : std::nullopt, + roi != nullptr ? std::make_optional(roi->toCpp()) : std::nullopt, + orientation != nullptr ? std::make_optional(orientation->toCpp()) : std::nullopt + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const BarcodeScannerOptions& value) { + using JSignature = JBarcodeScannerOptions(jni::alias_ref>, jni::alias_ref, jni::alias_ref, jni::alias_ref, jni::alias_ref, jni::alias_ref, jni::alias_ref); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + value.formats.has_value() ? [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = JBarcodeFormat::fromCpp(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.formats.value()) : nullptr, + value.enableAllPotentialBarcodes.has_value() ? jni::JBoolean::valueOf(value.enableAllPotentialBarcodes.value()) : nullptr, + value.scaleFactor.has_value() ? jni::JDouble::valueOf(value.scaleFactor.value()) : nullptr, + value.invertColors.has_value() ? jni::JBoolean::valueOf(value.invertColors.value()) : nullptr, + value.frameProcessInterval.has_value() ? jni::JDouble::valueOf(value.frameProcessInterval.value()) : nullptr, + value.roi.has_value() ? JRegionOfInterest::fromCpp(value.roi.value()) : nullptr, + value.orientation.has_value() ? JOrientation::fromCpp(value.orientation.value()) : nullptr + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JBarcodeScannerResult.hpp b/nitrogen/generated/android/c++/JBarcodeScannerResult.hpp new file mode 100644 index 0000000..cd0a0c0 --- /dev/null +++ b/nitrogen/generated/android/c++/JBarcodeScannerResult.hpp @@ -0,0 +1,89 @@ +/// +/// JBarcodeScannerResult.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "BarcodeScannerResult.hpp" + +#include "BarcodeFormat.hpp" +#include "BarcodeScannerBarcode.hpp" +#include "BarcodeValueTypeName.hpp" +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include "JBarcodeFormat.hpp" +#include "JBarcodeScannerBarcode.hpp" +#include "JBarcodeValueTypeName.hpp" +#include "JBoundingBox.hpp" +#include "JCorner.hpp" +#include +#include +#include +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "BarcodeScannerResult" and the Kotlin data class "BarcodeScannerResult". + */ + struct JBarcodeScannerResult final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/BarcodeScannerResult;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct BarcodeScannerResult by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + BarcodeScannerResult toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldBarcodes = clazz->getField>("barcodes"); + jni::local_ref> barcodes = this->getFieldValue(fieldBarcodes); + return BarcodeScannerResult( + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(barcodes) + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const BarcodeScannerResult& value) { + using JSignature = JBarcodeScannerResult(jni::alias_ref>); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = JBarcodeScannerBarcode::fromCpp(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.barcodes) + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JBarcodeValueTypeName.hpp b/nitrogen/generated/android/c++/JBarcodeValueTypeName.hpp new file mode 100644 index 0000000..6393072 --- /dev/null +++ b/nitrogen/generated/android/c++/JBarcodeValueTypeName.hpp @@ -0,0 +1,91 @@ +/// +/// JBarcodeValueTypeName.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "BarcodeValueTypeName.hpp" + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ enum "BarcodeValueTypeName" and the Kotlin enum "BarcodeValueTypeName". + */ + struct JBarcodeValueTypeName final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/BarcodeValueTypeName;"; + + public: + /** + * Convert this Java/Kotlin-based enum to the C++ enum BarcodeValueTypeName. + */ + [[maybe_unused]] + [[nodiscard]] + BarcodeValueTypeName toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldOrdinal = clazz->getField("value"); + int ordinal = this->getFieldValue(fieldOrdinal); + return static_cast(ordinal); + } + + public: + /** + * Create a Java/Kotlin-based enum with the given C++ enum's value. + */ + [[maybe_unused]] + static jni::alias_ref fromCpp(BarcodeValueTypeName value) { + static const auto clazz = javaClassStatic(); + switch (value) { + case BarcodeValueTypeName::TYPE_UNKNOWN: + static const auto fieldTYPE_UNKNOWN = clazz->getStaticField("TYPE_UNKNOWN"); + return clazz->getStaticFieldValue(fieldTYPE_UNKNOWN); + case BarcodeValueTypeName::TYPE_CONTACT_INFO: + static const auto fieldTYPE_CONTACT_INFO = clazz->getStaticField("TYPE_CONTACT_INFO"); + return clazz->getStaticFieldValue(fieldTYPE_CONTACT_INFO); + case BarcodeValueTypeName::TYPE_EMAIL: + static const auto fieldTYPE_EMAIL = clazz->getStaticField("TYPE_EMAIL"); + return clazz->getStaticFieldValue(fieldTYPE_EMAIL); + case BarcodeValueTypeName::TYPE_ISBN: + static const auto fieldTYPE_ISBN = clazz->getStaticField("TYPE_ISBN"); + return clazz->getStaticFieldValue(fieldTYPE_ISBN); + case BarcodeValueTypeName::TYPE_PHONE: + static const auto fieldTYPE_PHONE = clazz->getStaticField("TYPE_PHONE"); + return clazz->getStaticFieldValue(fieldTYPE_PHONE); + case BarcodeValueTypeName::TYPE_PRODUCT: + static const auto fieldTYPE_PRODUCT = clazz->getStaticField("TYPE_PRODUCT"); + return clazz->getStaticFieldValue(fieldTYPE_PRODUCT); + case BarcodeValueTypeName::TYPE_SMS: + static const auto fieldTYPE_SMS = clazz->getStaticField("TYPE_SMS"); + return clazz->getStaticFieldValue(fieldTYPE_SMS); + case BarcodeValueTypeName::TYPE_TEXT: + static const auto fieldTYPE_TEXT = clazz->getStaticField("TYPE_TEXT"); + return clazz->getStaticFieldValue(fieldTYPE_TEXT); + case BarcodeValueTypeName::TYPE_URL: + static const auto fieldTYPE_URL = clazz->getStaticField("TYPE_URL"); + return clazz->getStaticFieldValue(fieldTYPE_URL); + case BarcodeValueTypeName::TYPE_WIFI: + static const auto fieldTYPE_WIFI = clazz->getStaticField("TYPE_WIFI"); + return clazz->getStaticFieldValue(fieldTYPE_WIFI); + case BarcodeValueTypeName::TYPE_GEO: + static const auto fieldTYPE_GEO = clazz->getStaticField("TYPE_GEO"); + return clazz->getStaticFieldValue(fieldTYPE_GEO); + case BarcodeValueTypeName::TYPE_CALENDAR_EVENT: + static const auto fieldTYPE_CALENDAR_EVENT = clazz->getStaticField("TYPE_CALENDAR_EVENT"); + return clazz->getStaticFieldValue(fieldTYPE_CALENDAR_EVENT); + case BarcodeValueTypeName::TYPE_DRIVER_LICENSE: + static const auto fieldTYPE_DRIVER_LICENSE = clazz->getStaticField("TYPE_DRIVER_LICENSE"); + return clazz->getStaticFieldValue(fieldTYPE_DRIVER_LICENSE); + default: + std::string stringValue = std::to_string(static_cast(value)); + throw std::invalid_argument("Invalid enum value (" + stringValue + "!"); + } + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JBoundingBox.hpp b/nitrogen/generated/android/c++/JBoundingBox.hpp new file mode 100644 index 0000000..7905314 --- /dev/null +++ b/nitrogen/generated/android/c++/JBoundingBox.hpp @@ -0,0 +1,93 @@ +/// +/// JBoundingBox.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "BoundingBox.hpp" + + + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "BoundingBox" and the Kotlin data class "BoundingBox". + */ + struct JBoundingBox final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/BoundingBox;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct BoundingBox by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + BoundingBox toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldX = clazz->getField("x"); + double x = this->getFieldValue(fieldX); + static const auto fieldY = clazz->getField("y"); + double y = this->getFieldValue(fieldY); + static const auto fieldCenterX = clazz->getField("centerX"); + double centerX = this->getFieldValue(fieldCenterX); + static const auto fieldCenterY = clazz->getField("centerY"); + double centerY = this->getFieldValue(fieldCenterY); + static const auto fieldWidth = clazz->getField("width"); + double width = this->getFieldValue(fieldWidth); + static const auto fieldHeight = clazz->getField("height"); + double height = this->getFieldValue(fieldHeight); + static const auto fieldTop = clazz->getField("top"); + double top = this->getFieldValue(fieldTop); + static const auto fieldLeft = clazz->getField("left"); + double left = this->getFieldValue(fieldLeft); + static const auto fieldBottom = clazz->getField("bottom"); + double bottom = this->getFieldValue(fieldBottom); + static const auto fieldRight = clazz->getField("right"); + double right = this->getFieldValue(fieldRight); + return BoundingBox( + x, + y, + centerX, + centerY, + width, + height, + top, + left, + bottom, + right + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const BoundingBox& value) { + using JSignature = JBoundingBox(double, double, double, double, double, double, double, double, double, double); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + value.x, + value.y, + value.centerX, + value.centerY, + value.width, + value.height, + value.top, + value.left, + value.bottom, + value.right + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JCorner.hpp b/nitrogen/generated/android/c++/JCorner.hpp new file mode 100644 index 0000000..227cf63 --- /dev/null +++ b/nitrogen/generated/android/c++/JCorner.hpp @@ -0,0 +1,61 @@ +/// +/// JCorner.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "Corner.hpp" + + + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "Corner" and the Kotlin data class "Corner". + */ + struct JCorner final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/Corner;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct Corner by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + Corner toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldX = clazz->getField("x"); + double x = this->getFieldValue(fieldX); + static const auto fieldY = clazz->getField("y"); + double y = this->getFieldValue(fieldY); + return Corner( + x, + y + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const Corner& value) { + using JSignature = JCorner(double, double); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + value.x, + value.y + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JHybridBarcodeScannerSpec.cpp b/nitrogen/generated/android/c++/JHybridBarcodeScannerSpec.cpp new file mode 100644 index 0000000..12e2b59 --- /dev/null +++ b/nitrogen/generated/android/c++/JHybridBarcodeScannerSpec.cpp @@ -0,0 +1,111 @@ +/// +/// JHybridBarcodeScannerSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "JHybridBarcodeScannerSpec.hpp" + +// Forward declaration of `BarcodeScannerResult` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerResult; } +// Forward declaration of `BarcodeScannerBarcode` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerBarcode; } +// Forward declaration of `BoundingBox` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BoundingBox; } +// Forward declaration of `Corner` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct Corner; } +// Forward declaration of `BarcodeFormat` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeFormat; } +// Forward declaration of `BarcodeValueTypeName` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeValueTypeName; } +// Forward declaration of `HybridFrameSpec` to properly resolve imports. +namespace margelo::nitro::camera { class HybridFrameSpec; } +// Forward declaration of `MLKitBaseArguments` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct MLKitBaseArguments; } +// Forward declaration of `Orientation` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class Orientation; } + +#include "BarcodeScannerResult.hpp" +#include "JBarcodeScannerResult.hpp" +#include "BarcodeScannerBarcode.hpp" +#include +#include "JBarcodeScannerBarcode.hpp" +#include "BoundingBox.hpp" +#include +#include "JBoundingBox.hpp" +#include "Corner.hpp" +#include "JCorner.hpp" +#include "BarcodeFormat.hpp" +#include "JBarcodeFormat.hpp" +#include "BarcodeValueTypeName.hpp" +#include "JBarcodeValueTypeName.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include "MLKitBaseArguments.hpp" +#include "JMLKitBaseArguments.hpp" +#include "Orientation.hpp" +#include "JOrientation.hpp" + +namespace margelo::nitro::visioncameramlkit { + + std::shared_ptr JHybridBarcodeScannerSpec::JavaPart::getJHybridBarcodeScannerSpec() { + auto hybridObject = JHybridObject::JavaPart::getJHybridObject(); + auto castHybridObject = std::dynamic_pointer_cast(hybridObject); + if (castHybridObject == nullptr) [[unlikely]] { + throw std::runtime_error("Failed to downcast JHybridObject to JHybridBarcodeScannerSpec!"); + } + return castHybridObject; + } + + jni::local_ref JHybridBarcodeScannerSpec::CxxPart::initHybrid(jni::alias_ref jThis) { + return makeCxxInstance(jThis); + } + + std::shared_ptr JHybridBarcodeScannerSpec::CxxPart::createHybridObject(const jni::local_ref& javaPart) { + auto castJavaPart = jni::dynamic_ref_cast(javaPart); + if (castJavaPart == nullptr) [[unlikely]] { + throw std::runtime_error("Failed to cast JHybridObject::JavaPart to JHybridBarcodeScannerSpec::JavaPart!"); + } + return std::make_shared(castJavaPart); + } + + void JHybridBarcodeScannerSpec::CxxPart::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", JHybridBarcodeScannerSpec::CxxPart::initHybrid), + }); + } + + // Properties + + + // Methods + BarcodeScannerResult JHybridBarcodeScannerSpec::recognize(const std::shared_ptr& frame, const std::optional& args) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* frame */, jni::alias_ref /* args */)>("recognize"); + auto __result = method(_javaPart, std::dynamic_pointer_cast(frame)->getJavaPart(), args.has_value() ? JMLKitBaseArguments::fromCpp(args.value()) : nullptr); + return __result->toCpp(); + } + std::shared_ptr> JHybridBarcodeScannerSpec::recognizeImage(const std::string& uri) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* uri */)>("recognizeImage"); + auto __result = method(_javaPart, jni::make_jstring(uri)); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& __boxedResult) { + auto __result = jni::static_ref_cast(__boxedResult); + __promise->resolve(__result->toCpp()); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JHybridBarcodeScannerSpec.hpp b/nitrogen/generated/android/c++/JHybridBarcodeScannerSpec.hpp new file mode 100644 index 0000000..9c21a2a --- /dev/null +++ b/nitrogen/generated/android/c++/JHybridBarcodeScannerSpec.hpp @@ -0,0 +1,64 @@ +/// +/// HybridBarcodeScannerSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include +#include "HybridBarcodeScannerSpec.hpp" + + + + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + class JHybridBarcodeScannerSpec: public virtual HybridBarcodeScannerSpec, public virtual JHybridObject { + public: + struct JavaPart: public jni::JavaClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/HybridBarcodeScannerSpec;"; + std::shared_ptr getJHybridBarcodeScannerSpec(); + }; + struct CxxPart: public jni::HybridClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/HybridBarcodeScannerSpec$CxxPart;"; + static jni::local_ref initHybrid(jni::alias_ref jThis); + static void registerNatives(); + using HybridBase::HybridBase; + protected: + std::shared_ptr createHybridObject(const jni::local_ref& javaPart) override; + }; + + public: + explicit JHybridBarcodeScannerSpec(const jni::local_ref& javaPart): + HybridObject(HybridBarcodeScannerSpec::TAG), + JHybridObject(javaPart), + _javaPart(jni::make_global(javaPart)) {} + ~JHybridBarcodeScannerSpec() override { + // Hermes GC can destroy JS objects on a non-JNI Thread. + jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); }); + } + + public: + inline const jni::global_ref& getJavaPart() const noexcept { + return _javaPart; + } + + public: + // Properties + + + public: + // Methods + BarcodeScannerResult recognize(const std::shared_ptr& frame, const std::optional& args) override; + std::shared_ptr> recognizeImage(const std::string& uri) override; + + private: + jni::global_ref _javaPart; + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JHybridTextRecognizerSpec.cpp b/nitrogen/generated/android/c++/JHybridTextRecognizerSpec.cpp new file mode 100644 index 0000000..551a930 --- /dev/null +++ b/nitrogen/generated/android/c++/JHybridTextRecognizerSpec.cpp @@ -0,0 +1,117 @@ +/// +/// JHybridTextRecognizerSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "JHybridTextRecognizerSpec.hpp" + +// Forward declaration of `TextRecognitionResult` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextRecognitionResult; } +// Forward declaration of `TextBlock` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextBlock; } +// Forward declaration of `BoundingBox` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BoundingBox; } +// Forward declaration of `Corner` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct Corner; } +// Forward declaration of `TextLine` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextLine; } +// Forward declaration of `TextElement` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextElement; } +// Forward declaration of `TextSymbol` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextSymbol; } +// Forward declaration of `HybridFrameSpec` to properly resolve imports. +namespace margelo::nitro::camera { class HybridFrameSpec; } +// Forward declaration of `MLKitBaseArguments` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct MLKitBaseArguments; } +// Forward declaration of `Orientation` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class Orientation; } + +#include "TextRecognitionResult.hpp" +#include "JTextRecognitionResult.hpp" +#include +#include "TextBlock.hpp" +#include +#include "JTextBlock.hpp" +#include "BoundingBox.hpp" +#include "JBoundingBox.hpp" +#include "Corner.hpp" +#include "JCorner.hpp" +#include "TextLine.hpp" +#include "JTextLine.hpp" +#include +#include +#include +#include "JVariant_NullType_Double.hpp" +#include +#include "TextElement.hpp" +#include "JTextElement.hpp" +#include "TextSymbol.hpp" +#include "JTextSymbol.hpp" +#include +#include +#include +#include +#include +#include "MLKitBaseArguments.hpp" +#include "JMLKitBaseArguments.hpp" +#include "Orientation.hpp" +#include "JOrientation.hpp" + +namespace margelo::nitro::visioncameramlkit { + + std::shared_ptr JHybridTextRecognizerSpec::JavaPart::getJHybridTextRecognizerSpec() { + auto hybridObject = JHybridObject::JavaPart::getJHybridObject(); + auto castHybridObject = std::dynamic_pointer_cast(hybridObject); + if (castHybridObject == nullptr) [[unlikely]] { + throw std::runtime_error("Failed to downcast JHybridObject to JHybridTextRecognizerSpec!"); + } + return castHybridObject; + } + + jni::local_ref JHybridTextRecognizerSpec::CxxPart::initHybrid(jni::alias_ref jThis) { + return makeCxxInstance(jThis); + } + + std::shared_ptr JHybridTextRecognizerSpec::CxxPart::createHybridObject(const jni::local_ref& javaPart) { + auto castJavaPart = jni::dynamic_ref_cast(javaPart); + if (castJavaPart == nullptr) [[unlikely]] { + throw std::runtime_error("Failed to cast JHybridObject::JavaPart to JHybridTextRecognizerSpec::JavaPart!"); + } + return std::make_shared(castJavaPart); + } + + void JHybridTextRecognizerSpec::CxxPart::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", JHybridTextRecognizerSpec::CxxPart::initHybrid), + }); + } + + // Properties + + + // Methods + TextRecognitionResult JHybridTextRecognizerSpec::recognize(const std::shared_ptr& frame, const std::optional& args) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* frame */, jni::alias_ref /* args */)>("recognize"); + auto __result = method(_javaPart, std::dynamic_pointer_cast(frame)->getJavaPart(), args.has_value() ? JMLKitBaseArguments::fromCpp(args.value()) : nullptr); + return __result->toCpp(); + } + std::shared_ptr> JHybridTextRecognizerSpec::recognizeImage(const std::string& uri) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* uri */)>("recognizeImage"); + auto __result = method(_javaPart, jni::make_jstring(uri)); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& __boxedResult) { + auto __result = jni::static_ref_cast(__boxedResult); + __promise->resolve(__result->toCpp()); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JHybridTextRecognizerSpec.hpp b/nitrogen/generated/android/c++/JHybridTextRecognizerSpec.hpp new file mode 100644 index 0000000..69c4cfd --- /dev/null +++ b/nitrogen/generated/android/c++/JHybridTextRecognizerSpec.hpp @@ -0,0 +1,64 @@ +/// +/// HybridTextRecognizerSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include +#include "HybridTextRecognizerSpec.hpp" + + + + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + class JHybridTextRecognizerSpec: public virtual HybridTextRecognizerSpec, public virtual JHybridObject { + public: + struct JavaPart: public jni::JavaClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/HybridTextRecognizerSpec;"; + std::shared_ptr getJHybridTextRecognizerSpec(); + }; + struct CxxPart: public jni::HybridClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/HybridTextRecognizerSpec$CxxPart;"; + static jni::local_ref initHybrid(jni::alias_ref jThis); + static void registerNatives(); + using HybridBase::HybridBase; + protected: + std::shared_ptr createHybridObject(const jni::local_ref& javaPart) override; + }; + + public: + explicit JHybridTextRecognizerSpec(const jni::local_ref& javaPart): + HybridObject(HybridTextRecognizerSpec::TAG), + JHybridObject(javaPart), + _javaPart(jni::make_global(javaPart)) {} + ~JHybridTextRecognizerSpec() override { + // Hermes GC can destroy JS objects on a non-JNI Thread. + jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); }); + } + + public: + inline const jni::global_ref& getJavaPart() const noexcept { + return _javaPart; + } + + public: + // Properties + + + public: + // Methods + TextRecognitionResult recognize(const std::shared_ptr& frame, const std::optional& args) override; + std::shared_ptr> recognizeImage(const std::string& uri) override; + + private: + jni::global_ref _javaPart; + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JHybridVisionCameraMLKitSpec.cpp b/nitrogen/generated/android/c++/JHybridVisionCameraMLKitSpec.cpp new file mode 100644 index 0000000..5be7333 --- /dev/null +++ b/nitrogen/generated/android/c++/JHybridVisionCameraMLKitSpec.cpp @@ -0,0 +1,104 @@ +/// +/// JHybridVisionCameraMLKitSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "JHybridVisionCameraMLKitSpec.hpp" + +// Forward declaration of `HybridTextRecognizerSpec` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { class HybridTextRecognizerSpec; } +// Forward declaration of `HybridBarcodeScannerSpec` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { class HybridBarcodeScannerSpec; } +// Forward declaration of `MLKitFeature` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class MLKitFeature; } +// Forward declaration of `TextRecognizerOptions` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextRecognizerOptions; } +// Forward declaration of `TextRecognitionLanguage` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class TextRecognitionLanguage; } +// Forward declaration of `RegionOfInterest` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct RegionOfInterest; } +// Forward declaration of `RegionOfInterestUnit` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class RegionOfInterestUnit; } +// Forward declaration of `Orientation` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class Orientation; } +// Forward declaration of `BarcodeScannerOptions` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerOptions; } +// Forward declaration of `BarcodeFormat` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeFormat; } + +#include +#include "HybridTextRecognizerSpec.hpp" +#include "JHybridTextRecognizerSpec.hpp" +#include "HybridBarcodeScannerSpec.hpp" +#include "JHybridBarcodeScannerSpec.hpp" +#include "MLKitFeature.hpp" +#include "JMLKitFeature.hpp" +#include "TextRecognizerOptions.hpp" +#include "JTextRecognizerOptions.hpp" +#include "TextRecognitionLanguage.hpp" +#include +#include "JTextRecognitionLanguage.hpp" +#include "RegionOfInterest.hpp" +#include "JRegionOfInterest.hpp" +#include "RegionOfInterestUnit.hpp" +#include "JRegionOfInterestUnit.hpp" +#include "Orientation.hpp" +#include "JOrientation.hpp" +#include "BarcodeScannerOptions.hpp" +#include "JBarcodeScannerOptions.hpp" +#include "BarcodeFormat.hpp" +#include +#include "JBarcodeFormat.hpp" + +namespace margelo::nitro::visioncameramlkit { + + std::shared_ptr JHybridVisionCameraMLKitSpec::JavaPart::getJHybridVisionCameraMLKitSpec() { + auto hybridObject = JHybridObject::JavaPart::getJHybridObject(); + auto castHybridObject = std::dynamic_pointer_cast(hybridObject); + if (castHybridObject == nullptr) [[unlikely]] { + throw std::runtime_error("Failed to downcast JHybridObject to JHybridVisionCameraMLKitSpec!"); + } + return castHybridObject; + } + + jni::local_ref JHybridVisionCameraMLKitSpec::CxxPart::initHybrid(jni::alias_ref jThis) { + return makeCxxInstance(jThis); + } + + std::shared_ptr JHybridVisionCameraMLKitSpec::CxxPart::createHybridObject(const jni::local_ref& javaPart) { + auto castJavaPart = jni::dynamic_ref_cast(javaPart); + if (castJavaPart == nullptr) [[unlikely]] { + throw std::runtime_error("Failed to cast JHybridObject::JavaPart to JHybridVisionCameraMLKitSpec::JavaPart!"); + } + return std::make_shared(castJavaPart); + } + + void JHybridVisionCameraMLKitSpec::CxxPart::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", JHybridVisionCameraMLKitSpec::CxxPart::initHybrid), + }); + } + + // Properties + + + // Methods + bool JHybridVisionCameraMLKitSpec::isFeatureAvailable(MLKitFeature feature) { + static const auto method = _javaPart->javaClassStatic()->getMethod /* feature */)>("isFeatureAvailable"); + auto __result = method(_javaPart, JMLKitFeature::fromCpp(feature)); + return static_cast(__result); + } + std::shared_ptr JHybridVisionCameraMLKitSpec::createTextRecognizer(const TextRecognizerOptions& options) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* options */)>("createTextRecognizer"); + auto __result = method(_javaPart, JTextRecognizerOptions::fromCpp(options)); + return __result->getJHybridTextRecognizerSpec(); + } + std::shared_ptr JHybridVisionCameraMLKitSpec::createBarcodeScanner(const BarcodeScannerOptions& options) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* options */)>("createBarcodeScanner"); + auto __result = method(_javaPart, JBarcodeScannerOptions::fromCpp(options)); + return __result->getJHybridBarcodeScannerSpec(); + } + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JHybridVisionCameraMLKitSpec.hpp b/nitrogen/generated/android/c++/JHybridVisionCameraMLKitSpec.hpp new file mode 100644 index 0000000..13df72b --- /dev/null +++ b/nitrogen/generated/android/c++/JHybridVisionCameraMLKitSpec.hpp @@ -0,0 +1,65 @@ +/// +/// HybridVisionCameraMLKitSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include +#include "HybridVisionCameraMLKitSpec.hpp" + + + + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + class JHybridVisionCameraMLKitSpec: public virtual HybridVisionCameraMLKitSpec, public virtual JHybridObject { + public: + struct JavaPart: public jni::JavaClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/HybridVisionCameraMLKitSpec;"; + std::shared_ptr getJHybridVisionCameraMLKitSpec(); + }; + struct CxxPart: public jni::HybridClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/HybridVisionCameraMLKitSpec$CxxPart;"; + static jni::local_ref initHybrid(jni::alias_ref jThis); + static void registerNatives(); + using HybridBase::HybridBase; + protected: + std::shared_ptr createHybridObject(const jni::local_ref& javaPart) override; + }; + + public: + explicit JHybridVisionCameraMLKitSpec(const jni::local_ref& javaPart): + HybridObject(HybridVisionCameraMLKitSpec::TAG), + JHybridObject(javaPart), + _javaPart(jni::make_global(javaPart)) {} + ~JHybridVisionCameraMLKitSpec() override { + // Hermes GC can destroy JS objects on a non-JNI Thread. + jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); }); + } + + public: + inline const jni::global_ref& getJavaPart() const noexcept { + return _javaPart; + } + + public: + // Properties + + + public: + // Methods + bool isFeatureAvailable(MLKitFeature feature) override; + std::shared_ptr createTextRecognizer(const TextRecognizerOptions& options) override; + std::shared_ptr createBarcodeScanner(const BarcodeScannerOptions& options) override; + + private: + jni::global_ref _javaPart; + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JMLKitBaseArguments.hpp b/nitrogen/generated/android/c++/JMLKitBaseArguments.hpp new file mode 100644 index 0000000..9f998bf --- /dev/null +++ b/nitrogen/generated/android/c++/JMLKitBaseArguments.hpp @@ -0,0 +1,59 @@ +/// +/// JMLKitBaseArguments.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "MLKitBaseArguments.hpp" + +#include "JOrientation.hpp" +#include "Orientation.hpp" +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "MLKitBaseArguments" and the Kotlin data class "MLKitBaseArguments". + */ + struct JMLKitBaseArguments final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/MLKitBaseArguments;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct MLKitBaseArguments by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + MLKitBaseArguments toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldOutputOrientation = clazz->getField("outputOrientation"); + jni::local_ref outputOrientation = this->getFieldValue(fieldOutputOrientation); + return MLKitBaseArguments( + outputOrientation != nullptr ? std::make_optional(outputOrientation->toCpp()) : std::nullopt + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const MLKitBaseArguments& value) { + using JSignature = JMLKitBaseArguments(jni::alias_ref); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + value.outputOrientation.has_value() ? JOrientation::fromCpp(value.outputOrientation.value()) : nullptr + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JMLKitFeature.hpp b/nitrogen/generated/android/c++/JMLKitFeature.hpp new file mode 100644 index 0000000..b241ead --- /dev/null +++ b/nitrogen/generated/android/c++/JMLKitFeature.hpp @@ -0,0 +1,85 @@ +/// +/// JMLKitFeature.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "MLKitFeature.hpp" + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ enum "MLKitFeature" and the Kotlin enum "MLKitFeature". + */ + struct JMLKitFeature final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/MLKitFeature;"; + + public: + /** + * Convert this Java/Kotlin-based enum to the C++ enum MLKitFeature. + */ + [[maybe_unused]] + [[nodiscard]] + MLKitFeature toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldOrdinal = clazz->getField("value"); + int ordinal = this->getFieldValue(fieldOrdinal); + return static_cast(ordinal); + } + + public: + /** + * Create a Java/Kotlin-based enum with the given C++ enum's value. + */ + [[maybe_unused]] + static jni::alias_ref fromCpp(MLKitFeature value) { + static const auto clazz = javaClassStatic(); + switch (value) { + case MLKitFeature::TEXTRECOGNITION: + static const auto fieldTEXTRECOGNITION = clazz->getStaticField("TEXTRECOGNITION"); + return clazz->getStaticFieldValue(fieldTEXTRECOGNITION); + case MLKitFeature::FACEDETECTION: + static const auto fieldFACEDETECTION = clazz->getStaticField("FACEDETECTION"); + return clazz->getStaticFieldValue(fieldFACEDETECTION); + case MLKitFeature::FACEMESHDETECTION: + static const auto fieldFACEMESHDETECTION = clazz->getStaticField("FACEMESHDETECTION"); + return clazz->getStaticFieldValue(fieldFACEMESHDETECTION); + case MLKitFeature::POSEDETECTION: + static const auto fieldPOSEDETECTION = clazz->getStaticField("POSEDETECTION"); + return clazz->getStaticFieldValue(fieldPOSEDETECTION); + case MLKitFeature::SELFIESEGMENTATION: + static const auto fieldSELFIESEGMENTATION = clazz->getStaticField("SELFIESEGMENTATION"); + return clazz->getStaticFieldValue(fieldSELFIESEGMENTATION); + case MLKitFeature::SUBJECTSEGMENTATION: + static const auto fieldSUBJECTSEGMENTATION = clazz->getStaticField("SUBJECTSEGMENTATION"); + return clazz->getStaticFieldValue(fieldSUBJECTSEGMENTATION); + case MLKitFeature::DOCUMENTSCANNER: + static const auto fieldDOCUMENTSCANNER = clazz->getStaticField("DOCUMENTSCANNER"); + return clazz->getStaticFieldValue(fieldDOCUMENTSCANNER); + case MLKitFeature::BARCODESCANNING: + static const auto fieldBARCODESCANNING = clazz->getStaticField("BARCODESCANNING"); + return clazz->getStaticFieldValue(fieldBARCODESCANNING); + case MLKitFeature::IMAGELABELING: + static const auto fieldIMAGELABELING = clazz->getStaticField("IMAGELABELING"); + return clazz->getStaticFieldValue(fieldIMAGELABELING); + case MLKitFeature::OBJECTDETECTION: + static const auto fieldOBJECTDETECTION = clazz->getStaticField("OBJECTDETECTION"); + return clazz->getStaticFieldValue(fieldOBJECTDETECTION); + case MLKitFeature::DIGITALINKRECOGNITION: + static const auto fieldDIGITALINKRECOGNITION = clazz->getStaticField("DIGITALINKRECOGNITION"); + return clazz->getStaticFieldValue(fieldDIGITALINKRECOGNITION); + default: + std::string stringValue = std::to_string(static_cast(value)); + throw std::invalid_argument("Invalid enum value (" + stringValue + "!"); + } + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JOrientation.hpp b/nitrogen/generated/android/c++/JOrientation.hpp new file mode 100644 index 0000000..2c6f089 --- /dev/null +++ b/nitrogen/generated/android/c++/JOrientation.hpp @@ -0,0 +1,64 @@ +/// +/// JOrientation.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "Orientation.hpp" + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ enum "Orientation" and the Kotlin enum "Orientation". + */ + struct JOrientation final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/Orientation;"; + + public: + /** + * Convert this Java/Kotlin-based enum to the C++ enum Orientation. + */ + [[maybe_unused]] + [[nodiscard]] + Orientation toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldOrdinal = clazz->getField("value"); + int ordinal = this->getFieldValue(fieldOrdinal); + return static_cast(ordinal); + } + + public: + /** + * Create a Java/Kotlin-based enum with the given C++ enum's value. + */ + [[maybe_unused]] + static jni::alias_ref fromCpp(Orientation value) { + static const auto clazz = javaClassStatic(); + switch (value) { + case Orientation::PORTRAIT: + static const auto fieldPORTRAIT = clazz->getStaticField("PORTRAIT"); + return clazz->getStaticFieldValue(fieldPORTRAIT); + case Orientation::PORTRAIT_UPSIDE_DOWN: + static const auto fieldPORTRAIT_UPSIDE_DOWN = clazz->getStaticField("PORTRAIT_UPSIDE_DOWN"); + return clazz->getStaticFieldValue(fieldPORTRAIT_UPSIDE_DOWN); + case Orientation::LANDSCAPE_LEFT: + static const auto fieldLANDSCAPE_LEFT = clazz->getStaticField("LANDSCAPE_LEFT"); + return clazz->getStaticFieldValue(fieldLANDSCAPE_LEFT); + case Orientation::LANDSCAPE_RIGHT: + static const auto fieldLANDSCAPE_RIGHT = clazz->getStaticField("LANDSCAPE_RIGHT"); + return clazz->getStaticFieldValue(fieldLANDSCAPE_RIGHT); + default: + std::string stringValue = std::to_string(static_cast(value)); + throw std::invalid_argument("Invalid enum value (" + stringValue + "!"); + } + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JRegionOfInterest.hpp b/nitrogen/generated/android/c++/JRegionOfInterest.hpp new file mode 100644 index 0000000..0a5ce83 --- /dev/null +++ b/nitrogen/generated/android/c++/JRegionOfInterest.hpp @@ -0,0 +1,75 @@ +/// +/// JRegionOfInterest.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "RegionOfInterest.hpp" + +#include "JRegionOfInterestUnit.hpp" +#include "RegionOfInterestUnit.hpp" +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "RegionOfInterest" and the Kotlin data class "RegionOfInterest". + */ + struct JRegionOfInterest final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/RegionOfInterest;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct RegionOfInterest by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + RegionOfInterest toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldX = clazz->getField("x"); + double x = this->getFieldValue(fieldX); + static const auto fieldY = clazz->getField("y"); + double y = this->getFieldValue(fieldY); + static const auto fieldWidth = clazz->getField("width"); + double width = this->getFieldValue(fieldWidth); + static const auto fieldHeight = clazz->getField("height"); + double height = this->getFieldValue(fieldHeight); + static const auto fieldUnit = clazz->getField("unit"); + jni::local_ref unit = this->getFieldValue(fieldUnit); + return RegionOfInterest( + x, + y, + width, + height, + unit != nullptr ? std::make_optional(unit->toCpp()) : std::nullopt + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const RegionOfInterest& value) { + using JSignature = JRegionOfInterest(double, double, double, double, jni::alias_ref); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + value.x, + value.y, + value.width, + value.height, + value.unit.has_value() ? JRegionOfInterestUnit::fromCpp(value.unit.value()) : nullptr + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JRegionOfInterestUnit.hpp b/nitrogen/generated/android/c++/JRegionOfInterestUnit.hpp new file mode 100644 index 0000000..6166c88 --- /dev/null +++ b/nitrogen/generated/android/c++/JRegionOfInterestUnit.hpp @@ -0,0 +1,58 @@ +/// +/// JRegionOfInterestUnit.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "RegionOfInterestUnit.hpp" + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ enum "RegionOfInterestUnit" and the Kotlin enum "RegionOfInterestUnit". + */ + struct JRegionOfInterestUnit final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/RegionOfInterestUnit;"; + + public: + /** + * Convert this Java/Kotlin-based enum to the C++ enum RegionOfInterestUnit. + */ + [[maybe_unused]] + [[nodiscard]] + RegionOfInterestUnit toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldOrdinal = clazz->getField("value"); + int ordinal = this->getFieldValue(fieldOrdinal); + return static_cast(ordinal); + } + + public: + /** + * Create a Java/Kotlin-based enum with the given C++ enum's value. + */ + [[maybe_unused]] + static jni::alias_ref fromCpp(RegionOfInterestUnit value) { + static const auto clazz = javaClassStatic(); + switch (value) { + case RegionOfInterestUnit::NORMALIZED: + static const auto fieldNORMALIZED = clazz->getStaticField("NORMALIZED"); + return clazz->getStaticFieldValue(fieldNORMALIZED); + case RegionOfInterestUnit::PIXEL: + static const auto fieldPIXEL = clazz->getStaticField("PIXEL"); + return clazz->getStaticFieldValue(fieldPIXEL); + default: + std::string stringValue = std::to_string(static_cast(value)); + throw std::invalid_argument("Invalid enum value (" + stringValue + "!"); + } + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JTextBlock.hpp b/nitrogen/generated/android/c++/JTextBlock.hpp new file mode 100644 index 0000000..e2f8f1c --- /dev/null +++ b/nitrogen/generated/android/c++/JTextBlock.hpp @@ -0,0 +1,143 @@ +/// +/// JTextBlock.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TextBlock.hpp" + +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include "JBoundingBox.hpp" +#include "JCorner.hpp" +#include "JTextElement.hpp" +#include "JTextLine.hpp" +#include "JTextSymbol.hpp" +#include "JVariant_NullType_Double.hpp" +#include "TextElement.hpp" +#include "TextLine.hpp" +#include "TextSymbol.hpp" +#include +#include +#include +#include +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "TextBlock" and the Kotlin data class "TextBlock". + */ + struct JTextBlock final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/TextBlock;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct TextBlock by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + TextBlock toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldBounds = clazz->getField("bounds"); + jni::local_ref bounds = this->getFieldValue(fieldBounds); + static const auto fieldCorners = clazz->getField>("corners"); + jni::local_ref> corners = this->getFieldValue(fieldCorners); + static const auto fieldLanguages = clazz->getField>("languages"); + jni::local_ref> languages = this->getFieldValue(fieldLanguages); + static const auto fieldText = clazz->getField("text"); + jni::local_ref text = this->getFieldValue(fieldText); + static const auto fieldLines = clazz->getField>("lines"); + jni::local_ref> lines = this->getFieldValue(fieldLines); + return TextBlock( + bounds->toCpp(), + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(corners), + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toStdString()); + } + return __vector; + }(languages), + text->toStdString(), + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(lines) + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const TextBlock& value) { + using JSignature = JTextBlock(jni::alias_ref, jni::alias_ref>, jni::alias_ref>, jni::alias_ref, jni::alias_ref>); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + JBoundingBox::fromCpp(value.bounds), + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = JCorner::fromCpp(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.corners), + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = jni::make_jstring(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.languages), + jni::make_jstring(value.text), + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = JTextLine::fromCpp(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.lines) + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JTextElement.hpp b/nitrogen/generated/android/c++/JTextElement.hpp new file mode 100644 index 0000000..ee39031 --- /dev/null +++ b/nitrogen/generated/android/c++/JTextElement.hpp @@ -0,0 +1,147 @@ +/// +/// JTextElement.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TextElement.hpp" + +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include "JBoundingBox.hpp" +#include "JCorner.hpp" +#include "JTextSymbol.hpp" +#include "JVariant_NullType_Double.hpp" +#include "TextSymbol.hpp" +#include +#include +#include +#include +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "TextElement" and the Kotlin data class "TextElement". + */ + struct JTextElement final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/TextElement;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct TextElement by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + TextElement toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldBounds = clazz->getField("bounds"); + jni::local_ref bounds = this->getFieldValue(fieldBounds); + static const auto fieldCorners = clazz->getField>("corners"); + jni::local_ref> corners = this->getFieldValue(fieldCorners); + static const auto fieldConfidence = clazz->getField("confidence"); + jni::local_ref confidence = this->getFieldValue(fieldConfidence); + static const auto fieldAngle = clazz->getField("angle"); + jni::local_ref angle = this->getFieldValue(fieldAngle); + static const auto fieldLanguages = clazz->getField>("languages"); + jni::local_ref> languages = this->getFieldValue(fieldLanguages); + static const auto fieldText = clazz->getField("text"); + jni::local_ref text = this->getFieldValue(fieldText); + static const auto fieldSymbols = clazz->getField>("symbols"); + jni::local_ref> symbols = this->getFieldValue(fieldSymbols); + return TextElement( + bounds->toCpp(), + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(corners), + confidence != nullptr ? std::make_optional(confidence->toCpp()) : std::nullopt, + angle != nullptr ? std::make_optional(angle->toCpp()) : std::nullopt, + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toStdString()); + } + return __vector; + }(languages), + text->toStdString(), + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(symbols) + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const TextElement& value) { + using JSignature = JTextElement(jni::alias_ref, jni::alias_ref>, jni::alias_ref, jni::alias_ref, jni::alias_ref>, jni::alias_ref, jni::alias_ref>); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + JBoundingBox::fromCpp(value.bounds), + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = JCorner::fromCpp(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.corners), + value.confidence.has_value() ? JVariant_NullType_Double::fromCpp(value.confidence.value()) : nullptr, + value.angle.has_value() ? JVariant_NullType_Double::fromCpp(value.angle.value()) : nullptr, + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = jni::make_jstring(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.languages), + jni::make_jstring(value.text), + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = JTextSymbol::fromCpp(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.symbols) + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JTextLine.hpp b/nitrogen/generated/android/c++/JTextLine.hpp new file mode 100644 index 0000000..ea33db4 --- /dev/null +++ b/nitrogen/generated/android/c++/JTextLine.hpp @@ -0,0 +1,149 @@ +/// +/// JTextLine.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TextLine.hpp" + +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include "JBoundingBox.hpp" +#include "JCorner.hpp" +#include "JTextElement.hpp" +#include "JTextSymbol.hpp" +#include "JVariant_NullType_Double.hpp" +#include "TextElement.hpp" +#include "TextSymbol.hpp" +#include +#include +#include +#include +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "TextLine" and the Kotlin data class "TextLine". + */ + struct JTextLine final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/TextLine;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct TextLine by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + TextLine toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldBounds = clazz->getField("bounds"); + jni::local_ref bounds = this->getFieldValue(fieldBounds); + static const auto fieldCorners = clazz->getField>("corners"); + jni::local_ref> corners = this->getFieldValue(fieldCorners); + static const auto fieldConfidence = clazz->getField("confidence"); + jni::local_ref confidence = this->getFieldValue(fieldConfidence); + static const auto fieldAngle = clazz->getField("angle"); + jni::local_ref angle = this->getFieldValue(fieldAngle); + static const auto fieldLanguages = clazz->getField>("languages"); + jni::local_ref> languages = this->getFieldValue(fieldLanguages); + static const auto fieldText = clazz->getField("text"); + jni::local_ref text = this->getFieldValue(fieldText); + static const auto fieldElements = clazz->getField>("elements"); + jni::local_ref> elements = this->getFieldValue(fieldElements); + return TextLine( + bounds->toCpp(), + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(corners), + confidence != nullptr ? std::make_optional(confidence->toCpp()) : std::nullopt, + angle != nullptr ? std::make_optional(angle->toCpp()) : std::nullopt, + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toStdString()); + } + return __vector; + }(languages), + text->toStdString(), + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(elements) + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const TextLine& value) { + using JSignature = JTextLine(jni::alias_ref, jni::alias_ref>, jni::alias_ref, jni::alias_ref, jni::alias_ref>, jni::alias_ref, jni::alias_ref>); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + JBoundingBox::fromCpp(value.bounds), + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = JCorner::fromCpp(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.corners), + value.confidence.has_value() ? JVariant_NullType_Double::fromCpp(value.confidence.value()) : nullptr, + value.angle.has_value() ? JVariant_NullType_Double::fromCpp(value.angle.value()) : nullptr, + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = jni::make_jstring(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.languages), + jni::make_jstring(value.text), + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = JTextElement::fromCpp(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.elements) + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JTextRecognitionLanguage.hpp b/nitrogen/generated/android/c++/JTextRecognitionLanguage.hpp new file mode 100644 index 0000000..0e3c461 --- /dev/null +++ b/nitrogen/generated/android/c++/JTextRecognitionLanguage.hpp @@ -0,0 +1,67 @@ +/// +/// JTextRecognitionLanguage.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TextRecognitionLanguage.hpp" + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ enum "TextRecognitionLanguage" and the Kotlin enum "TextRecognitionLanguage". + */ + struct JTextRecognitionLanguage final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/TextRecognitionLanguage;"; + + public: + /** + * Convert this Java/Kotlin-based enum to the C++ enum TextRecognitionLanguage. + */ + [[maybe_unused]] + [[nodiscard]] + TextRecognitionLanguage toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldOrdinal = clazz->getField("value"); + int ordinal = this->getFieldValue(fieldOrdinal); + return static_cast(ordinal); + } + + public: + /** + * Create a Java/Kotlin-based enum with the given C++ enum's value. + */ + [[maybe_unused]] + static jni::alias_ref fromCpp(TextRecognitionLanguage value) { + static const auto clazz = javaClassStatic(); + switch (value) { + case TextRecognitionLanguage::LATIN: + static const auto fieldLATIN = clazz->getStaticField("LATIN"); + return clazz->getStaticFieldValue(fieldLATIN); + case TextRecognitionLanguage::CHINESE: + static const auto fieldCHINESE = clazz->getStaticField("CHINESE"); + return clazz->getStaticFieldValue(fieldCHINESE); + case TextRecognitionLanguage::DEVANAGARI: + static const auto fieldDEVANAGARI = clazz->getStaticField("DEVANAGARI"); + return clazz->getStaticFieldValue(fieldDEVANAGARI); + case TextRecognitionLanguage::JAPANESE: + static const auto fieldJAPANESE = clazz->getStaticField("JAPANESE"); + return clazz->getStaticFieldValue(fieldJAPANESE); + case TextRecognitionLanguage::KOREAN: + static const auto fieldKOREAN = clazz->getStaticField("KOREAN"); + return clazz->getStaticFieldValue(fieldKOREAN); + default: + std::string stringValue = std::to_string(static_cast(value)); + throw std::invalid_argument("Invalid enum value (" + stringValue + "!"); + } + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JTextRecognitionResult.hpp b/nitrogen/generated/android/c++/JTextRecognitionResult.hpp new file mode 100644 index 0000000..093ba58 --- /dev/null +++ b/nitrogen/generated/android/c++/JTextRecognitionResult.hpp @@ -0,0 +1,97 @@ +/// +/// JTextRecognitionResult.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TextRecognitionResult.hpp" + +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include "JBoundingBox.hpp" +#include "JCorner.hpp" +#include "JTextBlock.hpp" +#include "JTextElement.hpp" +#include "JTextLine.hpp" +#include "JTextSymbol.hpp" +#include "JVariant_NullType_Double.hpp" +#include "TextBlock.hpp" +#include "TextElement.hpp" +#include "TextLine.hpp" +#include "TextSymbol.hpp" +#include +#include +#include +#include +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "TextRecognitionResult" and the Kotlin data class "TextRecognitionResult". + */ + struct JTextRecognitionResult final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/TextRecognitionResult;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct TextRecognitionResult by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + TextRecognitionResult toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldText = clazz->getField("text"); + jni::local_ref text = this->getFieldValue(fieldText); + static const auto fieldBlocks = clazz->getField>("blocks"); + jni::local_ref> blocks = this->getFieldValue(fieldBlocks); + return TextRecognitionResult( + text->toStdString(), + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(blocks) + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const TextRecognitionResult& value) { + using JSignature = JTextRecognitionResult(jni::alias_ref, jni::alias_ref>); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + jni::make_jstring(value.text), + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = JTextBlock::fromCpp(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.blocks) + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JTextRecognizerOptions.hpp b/nitrogen/generated/android/c++/JTextRecognizerOptions.hpp new file mode 100644 index 0000000..d69be4f --- /dev/null +++ b/nitrogen/generated/android/c++/JTextRecognizerOptions.hpp @@ -0,0 +1,85 @@ +/// +/// JTextRecognizerOptions.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TextRecognizerOptions.hpp" + +#include "JOrientation.hpp" +#include "JRegionOfInterest.hpp" +#include "JRegionOfInterestUnit.hpp" +#include "JTextRecognitionLanguage.hpp" +#include "Orientation.hpp" +#include "RegionOfInterest.hpp" +#include "RegionOfInterestUnit.hpp" +#include "TextRecognitionLanguage.hpp" +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "TextRecognizerOptions" and the Kotlin data class "TextRecognizerOptions". + */ + struct JTextRecognizerOptions final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/TextRecognizerOptions;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct TextRecognizerOptions by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + TextRecognizerOptions toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldLanguage = clazz->getField("language"); + jni::local_ref language = this->getFieldValue(fieldLanguage); + static const auto fieldScaleFactor = clazz->getField("scaleFactor"); + jni::local_ref scaleFactor = this->getFieldValue(fieldScaleFactor); + static const auto fieldInvertColors = clazz->getField("invertColors"); + jni::local_ref invertColors = this->getFieldValue(fieldInvertColors); + static const auto fieldFrameProcessInterval = clazz->getField("frameProcessInterval"); + jni::local_ref frameProcessInterval = this->getFieldValue(fieldFrameProcessInterval); + static const auto fieldRoi = clazz->getField("roi"); + jni::local_ref roi = this->getFieldValue(fieldRoi); + static const auto fieldOrientation = clazz->getField("orientation"); + jni::local_ref orientation = this->getFieldValue(fieldOrientation); + return TextRecognizerOptions( + language != nullptr ? std::make_optional(language->toCpp()) : std::nullopt, + scaleFactor != nullptr ? std::make_optional(scaleFactor->value()) : std::nullopt, + invertColors != nullptr ? std::make_optional(static_cast(invertColors->value())) : std::nullopt, + frameProcessInterval != nullptr ? std::make_optional(frameProcessInterval->value()) : std::nullopt, + roi != nullptr ? std::make_optional(roi->toCpp()) : std::nullopt, + orientation != nullptr ? std::make_optional(orientation->toCpp()) : std::nullopt + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const TextRecognizerOptions& value) { + using JSignature = JTextRecognizerOptions(jni::alias_ref, jni::alias_ref, jni::alias_ref, jni::alias_ref, jni::alias_ref, jni::alias_ref); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + value.language.has_value() ? JTextRecognitionLanguage::fromCpp(value.language.value()) : nullptr, + value.scaleFactor.has_value() ? jni::JDouble::valueOf(value.scaleFactor.value()) : nullptr, + value.invertColors.has_value() ? jni::JBoolean::valueOf(value.invertColors.value()) : nullptr, + value.frameProcessInterval.has_value() ? jni::JDouble::valueOf(value.frameProcessInterval.value()) : nullptr, + value.roi.has_value() ? JRegionOfInterest::fromCpp(value.roi.value()) : nullptr, + value.orientation.has_value() ? JOrientation::fromCpp(value.orientation.value()) : nullptr + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JTextSymbol.hpp b/nitrogen/generated/android/c++/JTextSymbol.hpp new file mode 100644 index 0000000..7349be5 --- /dev/null +++ b/nitrogen/generated/android/c++/JTextSymbol.hpp @@ -0,0 +1,118 @@ +/// +/// JTextSymbol.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TextSymbol.hpp" + +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include "JBoundingBox.hpp" +#include "JCorner.hpp" +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "TextSymbol" and the Kotlin data class "TextSymbol". + */ + struct JTextSymbol final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/TextSymbol;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct TextSymbol by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + TextSymbol toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldBounds = clazz->getField("bounds"); + jni::local_ref bounds = this->getFieldValue(fieldBounds); + static const auto fieldCorners = clazz->getField>("corners"); + jni::local_ref> corners = this->getFieldValue(fieldCorners); + static const auto fieldConfidence = clazz->getField("confidence"); + double confidence = this->getFieldValue(fieldConfidence); + static const auto fieldAngle = clazz->getField("angle"); + double angle = this->getFieldValue(fieldAngle); + static const auto fieldLanguages = clazz->getField>("languages"); + jni::local_ref> languages = this->getFieldValue(fieldLanguages); + static const auto fieldText = clazz->getField("text"); + jni::local_ref text = this->getFieldValue(fieldText); + return TextSymbol( + bounds->toCpp(), + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(corners), + confidence, + angle, + [&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toStdString()); + } + return __vector; + }(languages), + text->toStdString() + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const TextSymbol& value) { + using JSignature = JTextSymbol(jni::alias_ref, jni::alias_ref>, double, double, jni::alias_ref>, jni::alias_ref); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + JBoundingBox::fromCpp(value.bounds), + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = JCorner::fromCpp(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.corners), + value.confidence, + value.angle, + [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = jni::make_jstring(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(value.languages), + jni::make_jstring(value.text) + ); + } + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JVariant_NullType_Double.cpp b/nitrogen/generated/android/c++/JVariant_NullType_Double.cpp new file mode 100644 index 0000000..855daf5 --- /dev/null +++ b/nitrogen/generated/android/c++/JVariant_NullType_Double.cpp @@ -0,0 +1,26 @@ +/// +/// JVariant_NullType_Double.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "JVariant_NullType_Double.hpp" + +namespace margelo::nitro::visioncameramlkit { + /** + * Converts JVariant_NullType_Double to std::variant + */ + std::variant JVariant_NullType_Double::toCpp() const { + if (isInstanceOf(JVariant_NullType_Double_impl::First::javaClassStatic())) { + // It's a `nitro::NullType` + auto jniValue = static_cast(this)->getValue(); + return nitro::null; + } else if (isInstanceOf(JVariant_NullType_Double_impl::Second::javaClassStatic())) { + // It's a `double` + auto jniValue = static_cast(this)->getValue(); + return jniValue; + } + throw std::invalid_argument("Variant is unknown Kotlin instance!"); + } +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/c++/JVariant_NullType_Double.hpp b/nitrogen/generated/android/c++/JVariant_NullType_Double.hpp new file mode 100644 index 0000000..4a5e380 --- /dev/null +++ b/nitrogen/generated/android/c++/JVariant_NullType_Double.hpp @@ -0,0 +1,69 @@ +/// +/// JVariant_NullType_Double.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include + +#include +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ std::variant and the Java class "Variant_NullType_Double". + */ + class JVariant_NullType_Double: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/Variant_NullType_Double;"; + + static jni::local_ref create_0(jni::alias_ref value) { + static const auto method = javaClassStatic()->getStaticMethod)>("create"); + return method(javaClassStatic(), value); + } + static jni::local_ref create_1(double value) { + static const auto method = javaClassStatic()->getStaticMethod("create"); + return method(javaClassStatic(), value); + } + + static jni::local_ref fromCpp(const std::variant& variant) { + switch (variant.index()) { + case 0: return create_0(JNull::null()); + case 1: return create_1(std::get<1>(variant)); + default: throw std::invalid_argument("Variant holds unknown index! (" + std::to_string(variant.index()) + ")"); + } + } + + [[nodiscard]] std::variant toCpp() const; + }; + + namespace JVariant_NullType_Double_impl { + class First final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/Variant_NullType_Double$First;"; + + [[nodiscard]] jni::local_ref getValue() const { + static const auto field = javaClassStatic()->getField("value"); + return getFieldValue(field); + } + }; + + class Second final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/visioncameramlkit/Variant_NullType_Double$Second;"; + + [[nodiscard]] double getValue() const { + static const auto field = javaClassStatic()->getField("value"); + return getFieldValue(field); + } + }; + } // namespace JVariant_NullType_Double_impl +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeFormat.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeFormat.kt new file mode 100644 index 0000000..48de5ae --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeFormat.kt @@ -0,0 +1,36 @@ +/// +/// BarcodeFormat.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip + +/** + * Represents the JavaScript enum/union "BarcodeFormat". + */ +@DoNotStrip +@Keep +enum class BarcodeFormat(@DoNotStrip @Keep val value: Int) { + UNKNOWN(0), + ALL_FORMATS(1), + CODE_128(2), + CODE_39(3), + CODE_93(4), + CODABAR(5), + DATA_MATRIX(6), + EAN_13(7), + EAN_8(8), + ITF(9), + QR_CODE(10), + UPC_A(11), + UPC_E(12), + PDF417(13), + AZTEC(14); + + companion object +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeScannerBarcode.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeScannerBarcode.kt new file mode 100644 index 0000000..5939fc7 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeScannerBarcode.kt @@ -0,0 +1,101 @@ +/// +/// BarcodeScannerBarcode.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects +import com.margelo.nitro.core.AnyMap + +/** + * Represents the JavaScript object/struct "BarcodeScannerBarcode". + */ +@DoNotStrip +@Keep +data class BarcodeScannerBarcode( + @DoNotStrip + @Keep + val bounds: BoundingBox?, + @DoNotStrip + @Keep + val corners: Array?, + @DoNotStrip + @Keep + val format: Double, + @DoNotStrip + @Keep + val formatName: BarcodeFormat, + @DoNotStrip + @Keep + val valueType: Double, + @DoNotStrip + @Keep + val valueTypeName: BarcodeValueTypeName, + @DoNotStrip + @Keep + val rawValue: String?, + @DoNotStrip + @Keep + val displayValue: String?, + @DoNotStrip + @Keep + val rawBytes: DoubleArray?, + @DoNotStrip + @Keep + val value: AnyMap?, + @DoNotStrip + @Keep + val isPotential: Boolean +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is BarcodeScannerBarcode) return false + return Objects.deepEquals(this.bounds, other.bounds) + && Objects.deepEquals(this.corners, other.corners) + && Objects.deepEquals(this.format, other.format) + && Objects.deepEquals(this.formatName, other.formatName) + && Objects.deepEquals(this.valueType, other.valueType) + && Objects.deepEquals(this.valueTypeName, other.valueTypeName) + && Objects.deepEquals(this.rawValue, other.rawValue) + && Objects.deepEquals(this.displayValue, other.displayValue) + && Objects.deepEquals(this.rawBytes, other.rawBytes) + && Objects.deepEquals(this.value, other.value) + && Objects.deepEquals(this.isPotential, other.isPotential) + } + + override fun hashCode(): Int { + return arrayOf( + bounds, + corners, + format, + formatName, + valueType, + valueTypeName, + rawValue, + displayValue, + rawBytes, + value, + isPotential + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(bounds: BoundingBox?, corners: Array?, format: Double, formatName: BarcodeFormat, valueType: Double, valueTypeName: BarcodeValueTypeName, rawValue: String?, displayValue: String?, rawBytes: DoubleArray?, value: AnyMap?, isPotential: Boolean): BarcodeScannerBarcode { + return BarcodeScannerBarcode(bounds, corners, format, formatName, valueType, valueTypeName, rawValue, displayValue, rawBytes, value, isPotential) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions.kt new file mode 100644 index 0000000..da34102 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeScannerOptions.kt @@ -0,0 +1,81 @@ +/// +/// BarcodeScannerOptions.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "BarcodeScannerOptions". + */ +@DoNotStrip +@Keep +data class BarcodeScannerOptions( + @DoNotStrip + @Keep + val formats: Array?, + @DoNotStrip + @Keep + val enableAllPotentialBarcodes: Boolean?, + @DoNotStrip + @Keep + val scaleFactor: Double?, + @DoNotStrip + @Keep + val invertColors: Boolean?, + @DoNotStrip + @Keep + val frameProcessInterval: Double?, + @DoNotStrip + @Keep + val roi: RegionOfInterest?, + @DoNotStrip + @Keep + val orientation: Orientation? +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is BarcodeScannerOptions) return false + return Objects.deepEquals(this.formats, other.formats) + && Objects.deepEquals(this.enableAllPotentialBarcodes, other.enableAllPotentialBarcodes) + && Objects.deepEquals(this.scaleFactor, other.scaleFactor) + && Objects.deepEquals(this.invertColors, other.invertColors) + && Objects.deepEquals(this.frameProcessInterval, other.frameProcessInterval) + && Objects.deepEquals(this.roi, other.roi) + && Objects.deepEquals(this.orientation, other.orientation) + } + + override fun hashCode(): Int { + return arrayOf( + formats, + enableAllPotentialBarcodes, + scaleFactor, + invertColors, + frameProcessInterval, + roi, + orientation + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(formats: Array?, enableAllPotentialBarcodes: Boolean?, scaleFactor: Double?, invertColors: Boolean?, frameProcessInterval: Double?, roi: RegionOfInterest?, orientation: Orientation?): BarcodeScannerOptions { + return BarcodeScannerOptions(formats, enableAllPotentialBarcodes, scaleFactor, invertColors, frameProcessInterval, roi, orientation) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeScannerResult.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeScannerResult.kt new file mode 100644 index 0000000..2fff1d5 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeScannerResult.kt @@ -0,0 +1,51 @@ +/// +/// BarcodeScannerResult.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "BarcodeScannerResult". + */ +@DoNotStrip +@Keep +data class BarcodeScannerResult( + @DoNotStrip + @Keep + val barcodes: Array +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is BarcodeScannerResult) return false + return Objects.deepEquals(this.barcodes, other.barcodes) + } + + override fun hashCode(): Int { + return arrayOf( + barcodes + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(barcodes: Array): BarcodeScannerResult { + return BarcodeScannerResult(barcodes) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeValueTypeName.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeValueTypeName.kt new file mode 100644 index 0000000..ef833da --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BarcodeValueTypeName.kt @@ -0,0 +1,34 @@ +/// +/// BarcodeValueTypeName.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip + +/** + * Represents the JavaScript enum/union "BarcodeValueTypeName". + */ +@DoNotStrip +@Keep +enum class BarcodeValueTypeName(@DoNotStrip @Keep val value: Int) { + TYPE_UNKNOWN(0), + TYPE_CONTACT_INFO(1), + TYPE_EMAIL(2), + TYPE_ISBN(3), + TYPE_PHONE(4), + TYPE_PRODUCT(5), + TYPE_SMS(6), + TYPE_TEXT(7), + TYPE_URL(8), + TYPE_WIFI(9), + TYPE_GEO(10), + TYPE_CALENDAR_EVENT(11), + TYPE_DRIVER_LICENSE(12); + + companion object +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BoundingBox.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BoundingBox.kt new file mode 100644 index 0000000..2ccfc45 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/BoundingBox.kt @@ -0,0 +1,96 @@ +/// +/// BoundingBox.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "BoundingBox". + */ +@DoNotStrip +@Keep +data class BoundingBox( + @DoNotStrip + @Keep + val x: Double, + @DoNotStrip + @Keep + val y: Double, + @DoNotStrip + @Keep + val centerX: Double, + @DoNotStrip + @Keep + val centerY: Double, + @DoNotStrip + @Keep + val width: Double, + @DoNotStrip + @Keep + val height: Double, + @DoNotStrip + @Keep + val top: Double, + @DoNotStrip + @Keep + val left: Double, + @DoNotStrip + @Keep + val bottom: Double, + @DoNotStrip + @Keep + val right: Double +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is BoundingBox) return false + return Objects.deepEquals(this.x, other.x) + && Objects.deepEquals(this.y, other.y) + && Objects.deepEquals(this.centerX, other.centerX) + && Objects.deepEquals(this.centerY, other.centerY) + && Objects.deepEquals(this.width, other.width) + && Objects.deepEquals(this.height, other.height) + && Objects.deepEquals(this.top, other.top) + && Objects.deepEquals(this.left, other.left) + && Objects.deepEquals(this.bottom, other.bottom) + && Objects.deepEquals(this.right, other.right) + } + + override fun hashCode(): Int { + return arrayOf( + x, + y, + centerX, + centerY, + width, + height, + top, + left, + bottom, + right + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(x: Double, y: Double, centerX: Double, centerY: Double, width: Double, height: Double, top: Double, left: Double, bottom: Double, right: Double): BoundingBox { + return BoundingBox(x, y, centerX, centerY, width, height, top, left, bottom, right) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/Corner.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/Corner.kt new file mode 100644 index 0000000..3dfc9bc --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/Corner.kt @@ -0,0 +1,56 @@ +/// +/// Corner.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "Corner". + */ +@DoNotStrip +@Keep +data class Corner( + @DoNotStrip + @Keep + val x: Double, + @DoNotStrip + @Keep + val y: Double +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Corner) return false + return Objects.deepEquals(this.x, other.x) + && Objects.deepEquals(this.y, other.y) + } + + override fun hashCode(): Int { + return arrayOf( + x, + y + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(x: Double, y: Double): Corner { + return Corner(x, y) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/HybridBarcodeScannerSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/HybridBarcodeScannerSpec.kt new file mode 100644 index 0000000..8ffcd0d --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/HybridBarcodeScannerSpec.kt @@ -0,0 +1,60 @@ +/// +/// HybridBarcodeScannerSpec.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.camera.HybridFrameSpec +import com.margelo.nitro.core.Promise +import com.margelo.nitro.core.HybridObject + +/** + * A Kotlin class representing the BarcodeScanner HybridObject. + * Implement this abstract class to create Kotlin-based instances of BarcodeScanner. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet", + "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName" +) +abstract class HybridBarcodeScannerSpec: HybridObject() { + // Properties + + + // Methods + @DoNotStrip + @Keep + abstract fun recognize(frame: com.margelo.nitro.camera.HybridFrameSpec, args: MLKitBaseArguments?): BarcodeScannerResult + + @DoNotStrip + @Keep + abstract fun recognizeImage(uri: String): Promise + + // Default implementation of `HybridObject.toString()` + override fun toString(): String { + return "[HybridObject BarcodeScanner]" + } + + // C++ backing class + @DoNotStrip + @Keep + protected open class CxxPart(javaPart: HybridBarcodeScannerSpec): HybridObject.CxxPart(javaPart) { + // C++ JHybridBarcodeScannerSpec::CxxPart::initHybrid(...) + external override fun initHybrid(): HybridData + } + override fun createCxxPart(): CxxPart { + return CxxPart(this) + } + + companion object { + protected const val TAG = "HybridBarcodeScannerSpec" + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/HybridTextRecognizerSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/HybridTextRecognizerSpec.kt new file mode 100644 index 0000000..ab6499a --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/HybridTextRecognizerSpec.kt @@ -0,0 +1,60 @@ +/// +/// HybridTextRecognizerSpec.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.camera.HybridFrameSpec +import com.margelo.nitro.core.Promise +import com.margelo.nitro.core.HybridObject + +/** + * A Kotlin class representing the TextRecognizer HybridObject. + * Implement this abstract class to create Kotlin-based instances of TextRecognizer. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet", + "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName" +) +abstract class HybridTextRecognizerSpec: HybridObject() { + // Properties + + + // Methods + @DoNotStrip + @Keep + abstract fun recognize(frame: com.margelo.nitro.camera.HybridFrameSpec, args: MLKitBaseArguments?): TextRecognitionResult + + @DoNotStrip + @Keep + abstract fun recognizeImage(uri: String): Promise + + // Default implementation of `HybridObject.toString()` + override fun toString(): String { + return "[HybridObject TextRecognizer]" + } + + // C++ backing class + @DoNotStrip + @Keep + protected open class CxxPart(javaPart: HybridTextRecognizerSpec): HybridObject.CxxPart(javaPart) { + // C++ JHybridTextRecognizerSpec::CxxPart::initHybrid(...) + external override fun initHybrid(): HybridData + } + override fun createCxxPart(): CxxPart { + return CxxPart(this) + } + + companion object { + protected const val TAG = "HybridTextRecognizerSpec" + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/HybridVisionCameraMLKitSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/HybridVisionCameraMLKitSpec.kt new file mode 100644 index 0000000..1708610 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/HybridVisionCameraMLKitSpec.kt @@ -0,0 +1,62 @@ +/// +/// HybridVisionCameraMLKitSpec.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.core.HybridObject + +/** + * A Kotlin class representing the VisionCameraMLKit HybridObject. + * Implement this abstract class to create Kotlin-based instances of VisionCameraMLKit. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet", + "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName" +) +abstract class HybridVisionCameraMLKitSpec: HybridObject() { + // Properties + + + // Methods + @DoNotStrip + @Keep + abstract fun isFeatureAvailable(feature: MLKitFeature): Boolean + + @DoNotStrip + @Keep + abstract fun createTextRecognizer(options: TextRecognizerOptions): HybridTextRecognizerSpec + + @DoNotStrip + @Keep + abstract fun createBarcodeScanner(options: BarcodeScannerOptions): HybridBarcodeScannerSpec + + // Default implementation of `HybridObject.toString()` + override fun toString(): String { + return "[HybridObject VisionCameraMLKit]" + } + + // C++ backing class + @DoNotStrip + @Keep + protected open class CxxPart(javaPart: HybridVisionCameraMLKitSpec): HybridObject.CxxPart(javaPart) { + // C++ JHybridVisionCameraMLKitSpec::CxxPart::initHybrid(...) + external override fun initHybrid(): HybridData + } + override fun createCxxPart(): CxxPart { + return CxxPart(this) + } + + companion object { + protected const val TAG = "HybridVisionCameraMLKitSpec" + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/MLKitBaseArguments.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/MLKitBaseArguments.kt new file mode 100644 index 0000000..ef94e51 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/MLKitBaseArguments.kt @@ -0,0 +1,51 @@ +/// +/// MLKitBaseArguments.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "MLKitBaseArguments". + */ +@DoNotStrip +@Keep +data class MLKitBaseArguments( + @DoNotStrip + @Keep + val outputOrientation: Orientation? +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is MLKitBaseArguments) return false + return Objects.deepEquals(this.outputOrientation, other.outputOrientation) + } + + override fun hashCode(): Int { + return arrayOf( + outputOrientation + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(outputOrientation: Orientation?): MLKitBaseArguments { + return MLKitBaseArguments(outputOrientation) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/MLKitFeature.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/MLKitFeature.kt new file mode 100644 index 0000000..abd7e6b --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/MLKitFeature.kt @@ -0,0 +1,32 @@ +/// +/// MLKitFeature.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip + +/** + * Represents the JavaScript enum/union "MLKitFeature". + */ +@DoNotStrip +@Keep +enum class MLKitFeature(@DoNotStrip @Keep val value: Int) { + TEXTRECOGNITION(0), + FACEDETECTION(1), + FACEMESHDETECTION(2), + POSEDETECTION(3), + SELFIESEGMENTATION(4), + SUBJECTSEGMENTATION(5), + DOCUMENTSCANNER(6), + BARCODESCANNING(7), + IMAGELABELING(8), + OBJECTDETECTION(9), + DIGITALINKRECOGNITION(10); + + companion object +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/Orientation.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/Orientation.kt new file mode 100644 index 0000000..603ec61 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/Orientation.kt @@ -0,0 +1,25 @@ +/// +/// Orientation.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip + +/** + * Represents the JavaScript enum/union "Orientation". + */ +@DoNotStrip +@Keep +enum class Orientation(@DoNotStrip @Keep val value: Int) { + PORTRAIT(0), + PORTRAIT_UPSIDE_DOWN(1), + LANDSCAPE_LEFT(2), + LANDSCAPE_RIGHT(3); + + companion object +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/RegionOfInterest.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/RegionOfInterest.kt new file mode 100644 index 0000000..776b400 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/RegionOfInterest.kt @@ -0,0 +1,71 @@ +/// +/// RegionOfInterest.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "RegionOfInterest". + */ +@DoNotStrip +@Keep +data class RegionOfInterest( + @DoNotStrip + @Keep + val x: Double, + @DoNotStrip + @Keep + val y: Double, + @DoNotStrip + @Keep + val width: Double, + @DoNotStrip + @Keep + val height: Double, + @DoNotStrip + @Keep + val unit: RegionOfInterestUnit? +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is RegionOfInterest) return false + return Objects.deepEquals(this.x, other.x) + && Objects.deepEquals(this.y, other.y) + && Objects.deepEquals(this.width, other.width) + && Objects.deepEquals(this.height, other.height) + && Objects.deepEquals(this.unit, other.unit) + } + + override fun hashCode(): Int { + return arrayOf( + x, + y, + width, + height, + unit + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(x: Double, y: Double, width: Double, height: Double, unit: RegionOfInterestUnit?): RegionOfInterest { + return RegionOfInterest(x, y, width, height, unit) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/RegionOfInterestUnit.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/RegionOfInterestUnit.kt new file mode 100644 index 0000000..3cf1a6f --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/RegionOfInterestUnit.kt @@ -0,0 +1,23 @@ +/// +/// RegionOfInterestUnit.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip + +/** + * Represents the JavaScript enum/union "RegionOfInterestUnit". + */ +@DoNotStrip +@Keep +enum class RegionOfInterestUnit(@DoNotStrip @Keep val value: Int) { + NORMALIZED(0), + PIXEL(1); + + companion object +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextBlock.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextBlock.kt new file mode 100644 index 0000000..11b406e --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextBlock.kt @@ -0,0 +1,71 @@ +/// +/// TextBlock.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "TextBlock". + */ +@DoNotStrip +@Keep +data class TextBlock( + @DoNotStrip + @Keep + val bounds: BoundingBox, + @DoNotStrip + @Keep + val corners: Array, + @DoNotStrip + @Keep + val languages: Array, + @DoNotStrip + @Keep + val text: String, + @DoNotStrip + @Keep + val lines: Array +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TextBlock) return false + return Objects.deepEquals(this.bounds, other.bounds) + && Objects.deepEquals(this.corners, other.corners) + && Objects.deepEquals(this.languages, other.languages) + && Objects.deepEquals(this.text, other.text) + && Objects.deepEquals(this.lines, other.lines) + } + + override fun hashCode(): Int { + return arrayOf( + bounds, + corners, + languages, + text, + lines + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(bounds: BoundingBox, corners: Array, languages: Array, text: String, lines: Array): TextBlock { + return TextBlock(bounds, corners, languages, text, lines) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextElement.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextElement.kt new file mode 100644 index 0000000..9d11599 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextElement.kt @@ -0,0 +1,81 @@ +/// +/// TextElement.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects +import com.margelo.nitro.core.NullType + +/** + * Represents the JavaScript object/struct "TextElement". + */ +@DoNotStrip +@Keep +data class TextElement( + @DoNotStrip + @Keep + val bounds: BoundingBox, + @DoNotStrip + @Keep + val corners: Array, + @DoNotStrip + @Keep + val confidence: Variant_NullType_Double?, + @DoNotStrip + @Keep + val angle: Variant_NullType_Double?, + @DoNotStrip + @Keep + val languages: Array, + @DoNotStrip + @Keep + val text: String, + @DoNotStrip + @Keep + val symbols: Array +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TextElement) return false + return Objects.deepEquals(this.bounds, other.bounds) + && Objects.deepEquals(this.corners, other.corners) + && Objects.deepEquals(this.confidence, other.confidence) + && Objects.deepEquals(this.angle, other.angle) + && Objects.deepEquals(this.languages, other.languages) + && Objects.deepEquals(this.text, other.text) + && Objects.deepEquals(this.symbols, other.symbols) + } + + override fun hashCode(): Int { + return arrayOf( + bounds, + corners, + confidence, + angle, + languages, + text, + symbols + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(bounds: BoundingBox, corners: Array, confidence: Variant_NullType_Double?, angle: Variant_NullType_Double?, languages: Array, text: String, symbols: Array): TextElement { + return TextElement(bounds, corners, confidence, angle, languages, text, symbols) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextLine.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextLine.kt new file mode 100644 index 0000000..3e85714 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextLine.kt @@ -0,0 +1,81 @@ +/// +/// TextLine.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects +import com.margelo.nitro.core.NullType + +/** + * Represents the JavaScript object/struct "TextLine". + */ +@DoNotStrip +@Keep +data class TextLine( + @DoNotStrip + @Keep + val bounds: BoundingBox, + @DoNotStrip + @Keep + val corners: Array, + @DoNotStrip + @Keep + val confidence: Variant_NullType_Double?, + @DoNotStrip + @Keep + val angle: Variant_NullType_Double?, + @DoNotStrip + @Keep + val languages: Array, + @DoNotStrip + @Keep + val text: String, + @DoNotStrip + @Keep + val elements: Array +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TextLine) return false + return Objects.deepEquals(this.bounds, other.bounds) + && Objects.deepEquals(this.corners, other.corners) + && Objects.deepEquals(this.confidence, other.confidence) + && Objects.deepEquals(this.angle, other.angle) + && Objects.deepEquals(this.languages, other.languages) + && Objects.deepEquals(this.text, other.text) + && Objects.deepEquals(this.elements, other.elements) + } + + override fun hashCode(): Int { + return arrayOf( + bounds, + corners, + confidence, + angle, + languages, + text, + elements + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(bounds: BoundingBox, corners: Array, confidence: Variant_NullType_Double?, angle: Variant_NullType_Double?, languages: Array, text: String, elements: Array): TextLine { + return TextLine(bounds, corners, confidence, angle, languages, text, elements) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextRecognitionLanguage.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextRecognitionLanguage.kt new file mode 100644 index 0000000..40d14a2 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextRecognitionLanguage.kt @@ -0,0 +1,26 @@ +/// +/// TextRecognitionLanguage.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip + +/** + * Represents the JavaScript enum/union "TextRecognitionLanguage". + */ +@DoNotStrip +@Keep +enum class TextRecognitionLanguage(@DoNotStrip @Keep val value: Int) { + LATIN(0), + CHINESE(1), + DEVANAGARI(2), + JAPANESE(3), + KOREAN(4); + + companion object +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextRecognitionResult.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextRecognitionResult.kt new file mode 100644 index 0000000..cd7a232 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextRecognitionResult.kt @@ -0,0 +1,56 @@ +/// +/// TextRecognitionResult.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "TextRecognitionResult". + */ +@DoNotStrip +@Keep +data class TextRecognitionResult( + @DoNotStrip + @Keep + val text: String, + @DoNotStrip + @Keep + val blocks: Array +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TextRecognitionResult) return false + return Objects.deepEquals(this.text, other.text) + && Objects.deepEquals(this.blocks, other.blocks) + } + + override fun hashCode(): Int { + return arrayOf( + text, + blocks + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(text: String, blocks: Array): TextRecognitionResult { + return TextRecognitionResult(text, blocks) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions.kt new file mode 100644 index 0000000..7467558 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextRecognizerOptions.kt @@ -0,0 +1,76 @@ +/// +/// TextRecognizerOptions.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "TextRecognizerOptions". + */ +@DoNotStrip +@Keep +data class TextRecognizerOptions( + @DoNotStrip + @Keep + val language: TextRecognitionLanguage?, + @DoNotStrip + @Keep + val scaleFactor: Double?, + @DoNotStrip + @Keep + val invertColors: Boolean?, + @DoNotStrip + @Keep + val frameProcessInterval: Double?, + @DoNotStrip + @Keep + val roi: RegionOfInterest?, + @DoNotStrip + @Keep + val orientation: Orientation? +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TextRecognizerOptions) return false + return Objects.deepEquals(this.language, other.language) + && Objects.deepEquals(this.scaleFactor, other.scaleFactor) + && Objects.deepEquals(this.invertColors, other.invertColors) + && Objects.deepEquals(this.frameProcessInterval, other.frameProcessInterval) + && Objects.deepEquals(this.roi, other.roi) + && Objects.deepEquals(this.orientation, other.orientation) + } + + override fun hashCode(): Int { + return arrayOf( + language, + scaleFactor, + invertColors, + frameProcessInterval, + roi, + orientation + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(language: TextRecognitionLanguage?, scaleFactor: Double?, invertColors: Boolean?, frameProcessInterval: Double?, roi: RegionOfInterest?, orientation: Orientation?): TextRecognizerOptions { + return TextRecognizerOptions(language, scaleFactor, invertColors, frameProcessInterval, roi, orientation) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextSymbol.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextSymbol.kt new file mode 100644 index 0000000..a9c70e2 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/TextSymbol.kt @@ -0,0 +1,76 @@ +/// +/// TextSymbol.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "TextSymbol". + */ +@DoNotStrip +@Keep +data class TextSymbol( + @DoNotStrip + @Keep + val bounds: BoundingBox, + @DoNotStrip + @Keep + val corners: Array, + @DoNotStrip + @Keep + val confidence: Double, + @DoNotStrip + @Keep + val angle: Double, + @DoNotStrip + @Keep + val languages: Array, + @DoNotStrip + @Keep + val text: String +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TextSymbol) return false + return Objects.deepEquals(this.bounds, other.bounds) + && Objects.deepEquals(this.corners, other.corners) + && Objects.deepEquals(this.confidence, other.confidence) + && Objects.deepEquals(this.angle, other.angle) + && Objects.deepEquals(this.languages, other.languages) + && Objects.deepEquals(this.text, other.text) + } + + override fun hashCode(): Int { + return arrayOf( + bounds, + corners, + confidence, + angle, + languages, + text + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(bounds: BoundingBox, corners: Array, confidence: Double, angle: Double, languages: Array, text: String): TextSymbol { + return TextSymbol(bounds, corners, confidence, angle, languages, text) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/Variant_NullType_Double.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/Variant_NullType_Double.kt new file mode 100644 index 0000000..1c1deef --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/Variant_NullType_Double.kt @@ -0,0 +1,62 @@ +/// +/// Variant_NullType_Double.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.core.NullType + +/** + * Represents the TypeScript variant "NullType | Double". + */ +@Suppress("ClassName") +@DoNotStrip +sealed class Variant_NullType_Double { + @DoNotStrip + data class First(@DoNotStrip val value: NullType): Variant_NullType_Double() + @DoNotStrip + data class Second(@DoNotStrip val value: Double): Variant_NullType_Double() + + inline fun asType(): T? { + return when (this) { + is First -> (value) as? T + is Second -> (value) as? T + } + } + inline fun isType(): Boolean { + return asType() != null + } + inline fun match(first: (NullType) -> R, second: (Double) -> R): R { + return when (this) { + is First -> first(value) + is Second -> second(value) + } + } + + val isFirst: Boolean + get() = this is First + val isSecond: Boolean + get() = this is Second + + fun asFirstOrNull(): NullType? { + val value = (this as? First)?.value ?: return null + return value + } + fun asSecondOrNull(): Double? { + val value = (this as? Second)?.value ?: return null + return value + } + + companion object { + @JvmStatic + @DoNotStrip + fun create(value: NullType): Variant_NullType_Double = First(value) + @JvmStatic + @DoNotStrip + fun create(value: Double): Variant_NullType_Double = Second(value) + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/VisionCameraMLKitOnLoad.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/VisionCameraMLKitOnLoad.kt new file mode 100644 index 0000000..e63a5bf --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit/VisionCameraMLKitOnLoad.kt @@ -0,0 +1,35 @@ +/// +/// VisionCameraMLKitOnLoad.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.visioncameramlkit + +import android.util.Log + +internal class VisionCameraMLKitOnLoad { + companion object { + private const val TAG = "VisionCameraMLKitOnLoad" + private var didLoad = false + /** + * Initializes the native part of "VisionCameraMLKit". + * This method is idempotent and can be called more than once. + */ + @JvmStatic + fun initializeNative() { + if (didLoad) return + try { + Log.i(TAG, "Loading VisionCameraMLKit C++ library...") + System.loadLibrary("VisionCameraMLKit") + Log.i(TAG, "Successfully loaded VisionCameraMLKit C++ library!") + didLoad = true + } catch (e: Error) { + Log.e(TAG, "Failed to load VisionCameraMLKit C++ library! Is it properly installed and linked? " + + "Is the name correct? (see `CMakeLists.txt`, at `add_library(...)`)", e) + throw e + } + } + } +} diff --git a/nitrogen/generated/ios/VisionCameraMLKit+autolinking.rb b/nitrogen/generated/ios/VisionCameraMLKit+autolinking.rb new file mode 100644 index 0000000..2d1a84f --- /dev/null +++ b/nitrogen/generated/ios/VisionCameraMLKit+autolinking.rb @@ -0,0 +1,62 @@ +# +# VisionCameraMLKit+autolinking.rb +# This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +# https://github.com/mrousavy/nitro +# Copyright © Marc Rousavy @ Margelo +# + +# This is a Ruby script that adds all files generated by Nitrogen +# to the given podspec. +# +# To use it, add this to your .podspec: +# ```ruby +# Pod::Spec.new do |spec| +# # ... +# +# # Add all files generated by Nitrogen +# load 'nitrogen/generated/ios/VisionCameraMLKit+autolinking.rb' +# add_nitrogen_files(spec) +# end +# ``` + +def add_nitrogen_files(spec) + Pod::UI.puts "[NitroModules] 🔥 VisionCameraMLKit is boosted by nitro!" + + spec.dependency "NitroModules" + + current_source_files = Array(spec.attributes_hash['source_files']) + spec.source_files = current_source_files + [ + # Generated cross-platform specs + "nitrogen/generated/shared/**/*.{h,hpp,c,cpp,swift}", + # Generated bridges for the cross-platform specs + "nitrogen/generated/ios/**/*.{h,hpp,c,cpp,mm,swift}", + ] + + current_public_header_files = Array(spec.attributes_hash['public_header_files']) + spec.public_header_files = current_public_header_files + [ + # Generated specs + "nitrogen/generated/shared/**/*.{h,hpp}", + # Swift to C++ bridging helpers + "nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Bridge.hpp" + ] + + current_private_header_files = Array(spec.attributes_hash['private_header_files']) + spec.private_header_files = current_private_header_files + [ + # iOS specific specs + "nitrogen/generated/ios/c++/**/*.{h,hpp}", + # Views are framework-specific and should be private + "nitrogen/generated/shared/**/views/**/*" + ] + + current_pod_target_xcconfig = spec.attributes_hash['pod_target_xcconfig'] || {} + spec.pod_target_xcconfig = current_pod_target_xcconfig.merge({ + # Use C++ 20 + "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", + # Enables C++ <-> Swift interop (by default it's only ObjC) + "SWIFT_OBJC_INTEROP_MODE" => "objcxx", + # Enables stricter modular headers + "DEFINES_MODULE" => "YES", + # Disable auto-generated ObjC header for Swift (Static linkage on Xcode 26.4 breaks here) + "SWIFT_INSTALL_OBJC_HEADER" => "NO", + }) +end diff --git a/nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Bridge.cpp b/nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Bridge.cpp new file mode 100644 index 0000000..627efef --- /dev/null +++ b/nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Bridge.cpp @@ -0,0 +1,102 @@ +/// +/// VisionCameraMLKit-Swift-Cxx-Bridge.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "VisionCameraMLKit-Swift-Cxx-Bridge.hpp" + +// Include C++ implementation defined types +#include "HybridBarcodeScannerSpecSwift.hpp" +#include "HybridTextRecognizerSpecSwift.hpp" +#include "HybridVisionCameraMLKitSpecSwift.hpp" +#include "VisionCameraMLKit-Swift-Cxx-Umbrella.hpp" +#include +#include + +namespace margelo::nitro::visioncameramlkit::bridge::swift { + + // pragma MARK: std::shared_ptr + std::shared_ptr create_std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_(void* NON_NULL swiftUnsafePointer) noexcept { + // Implemented in VisionCamera + return margelo::nitro::camera::bridge::swift::create_std__shared_ptr_HybridFrameSpec_(swiftUnsafePointer); + } + void* NON_NULL get_std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_(std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_ cppType) { + // Implemented in VisionCamera + return margelo::nitro::camera::bridge::swift::get_std__shared_ptr_HybridFrameSpec_(cppType); + } + + // pragma MARK: std::function + Func_void_BarcodeScannerResult create_Func_void_BarcodeScannerResult(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = VisionCameraMLKit::Func_void_BarcodeScannerResult::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](const BarcodeScannerResult& result) mutable -> void { + swiftClosure.call(result); + }; + } + + // pragma MARK: std::function + Func_void_std__exception_ptr create_Func_void_std__exception_ptr(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = VisionCameraMLKit::Func_void_std__exception_ptr::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](const std::exception_ptr& error) mutable -> void { + swiftClosure.call(error); + }; + } + + // pragma MARK: std::shared_ptr + std::shared_ptr create_std__shared_ptr_HybridBarcodeScannerSpec_(void* NON_NULL swiftUnsafePointer) noexcept { + VisionCameraMLKit::HybridBarcodeScannerSpec_cxx swiftPart = VisionCameraMLKit::HybridBarcodeScannerSpec_cxx::fromUnsafe(swiftUnsafePointer); + return std::make_shared(swiftPart); + } + void* NON_NULL get_std__shared_ptr_HybridBarcodeScannerSpec_(std__shared_ptr_HybridBarcodeScannerSpec_ cppType) { + std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType); + #ifdef NITRO_DEBUG + if (swiftWrapper == nullptr) [[unlikely]] { + throw std::runtime_error("Class \"HybridBarcodeScannerSpec\" is not implemented in Swift!"); + } + #endif + VisionCameraMLKit::HybridBarcodeScannerSpec_cxx& swiftPart = swiftWrapper->getSwiftPart(); + return swiftPart.toUnsafe(); + } + + // pragma MARK: std::function + Func_void_TextRecognitionResult create_Func_void_TextRecognitionResult(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = VisionCameraMLKit::Func_void_TextRecognitionResult::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](const TextRecognitionResult& result) mutable -> void { + swiftClosure.call(result); + }; + } + + // pragma MARK: std::shared_ptr + std::shared_ptr create_std__shared_ptr_HybridTextRecognizerSpec_(void* NON_NULL swiftUnsafePointer) noexcept { + VisionCameraMLKit::HybridTextRecognizerSpec_cxx swiftPart = VisionCameraMLKit::HybridTextRecognizerSpec_cxx::fromUnsafe(swiftUnsafePointer); + return std::make_shared(swiftPart); + } + void* NON_NULL get_std__shared_ptr_HybridTextRecognizerSpec_(std__shared_ptr_HybridTextRecognizerSpec_ cppType) { + std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType); + #ifdef NITRO_DEBUG + if (swiftWrapper == nullptr) [[unlikely]] { + throw std::runtime_error("Class \"HybridTextRecognizerSpec\" is not implemented in Swift!"); + } + #endif + VisionCameraMLKit::HybridTextRecognizerSpec_cxx& swiftPart = swiftWrapper->getSwiftPart(); + return swiftPart.toUnsafe(); + } + + // pragma MARK: std::shared_ptr + std::shared_ptr create_std__shared_ptr_HybridVisionCameraMLKitSpec_(void* NON_NULL swiftUnsafePointer) noexcept { + VisionCameraMLKit::HybridVisionCameraMLKitSpec_cxx swiftPart = VisionCameraMLKit::HybridVisionCameraMLKitSpec_cxx::fromUnsafe(swiftUnsafePointer); + return std::make_shared(swiftPart); + } + void* NON_NULL get_std__shared_ptr_HybridVisionCameraMLKitSpec_(std__shared_ptr_HybridVisionCameraMLKitSpec_ cppType) { + std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType); + #ifdef NITRO_DEBUG + if (swiftWrapper == nullptr) [[unlikely]] { + throw std::runtime_error("Class \"HybridVisionCameraMLKitSpec\" is not implemented in Swift!"); + } + #endif + VisionCameraMLKit::HybridVisionCameraMLKitSpec_cxx& swiftPart = swiftWrapper->getSwiftPart(); + return swiftPart.toUnsafe(); + } + +} // namespace margelo::nitro::visioncameramlkit::bridge::swift diff --git a/nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Bridge.hpp b/nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Bridge.hpp new file mode 100644 index 0000000..1b0992a --- /dev/null +++ b/nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Bridge.hpp @@ -0,0 +1,641 @@ +/// +/// VisionCameraMLKit-Swift-Cxx-Bridge.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +// Forward declarations of C++ defined types +// Forward declaration of `BarcodeFormat` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeFormat; } +// Forward declaration of `BarcodeScannerBarcode` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerBarcode; } +// Forward declaration of `BarcodeScannerResult` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerResult; } +// Forward declaration of `BarcodeValueTypeName` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeValueTypeName; } +// Forward declaration of `BoundingBox` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BoundingBox; } +// Forward declaration of `Corner` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct Corner; } +// Forward declaration of `HybridBarcodeScannerSpec` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { class HybridBarcodeScannerSpec; } +// Forward declaration of `HybridFrameSpec` to properly resolve imports. +namespace margelo::nitro::camera { class HybridFrameSpec; } +// Forward declaration of `HybridTextRecognizerSpec` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { class HybridTextRecognizerSpec; } +// Forward declaration of `HybridVisionCameraMLKitSpec` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { class HybridVisionCameraMLKitSpec; } +// Forward declaration of `MLKitBaseArguments` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct MLKitBaseArguments; } +// Forward declaration of `Orientation` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class Orientation; } +// Forward declaration of `RegionOfInterestUnit` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class RegionOfInterestUnit; } +// Forward declaration of `RegionOfInterest` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct RegionOfInterest; } +// Forward declaration of `TextBlock` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextBlock; } +// Forward declaration of `TextElement` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextElement; } +// Forward declaration of `TextLine` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextLine; } +// Forward declaration of `TextRecognitionLanguage` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class TextRecognitionLanguage; } +// Forward declaration of `TextRecognitionResult` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextRecognitionResult; } +// Forward declaration of `TextSymbol` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextSymbol; } + +// Forward declarations of Swift defined types +// Forward declaration of `HybridBarcodeScannerSpec_cxx` to properly resolve imports. +namespace VisionCameraMLKit { class HybridBarcodeScannerSpec_cxx; } +// Forward declaration of `HybridFrameSpec_cxx` to properly resolve imports. +namespace VisionCamera { class HybridFrameSpec_cxx; } +// Forward declaration of `HybridTextRecognizerSpec_cxx` to properly resolve imports. +namespace VisionCameraMLKit { class HybridTextRecognizerSpec_cxx; } +// Forward declaration of `HybridVisionCameraMLKitSpec_cxx` to properly resolve imports. +namespace VisionCameraMLKit { class HybridVisionCameraMLKitSpec_cxx; } + +// Include C++ defined types +#include "BarcodeFormat.hpp" +#include "BarcodeScannerBarcode.hpp" +#include "BarcodeScannerResult.hpp" +#include "BarcodeValueTypeName.hpp" +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include "HybridBarcodeScannerSpec.hpp" +#include "HybridTextRecognizerSpec.hpp" +#include "HybridVisionCameraMLKitSpec.hpp" +#include "MLKitBaseArguments.hpp" +#include "Orientation.hpp" +#include "RegionOfInterest.hpp" +#include "RegionOfInterestUnit.hpp" +#include "TextBlock.hpp" +#include "TextElement.hpp" +#include "TextLine.hpp" +#include "TextRecognitionLanguage.hpp" +#include "TextRecognitionResult.hpp" +#include "TextSymbol.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Contains specialized versions of C++ templated types so they can be accessed from Swift, + * as well as helper functions to interact with those C++ types from Swift. + */ +namespace margelo::nitro::visioncameramlkit::bridge::swift { + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_BoundingBox_ = std::optional; + inline std::optional create_std__optional_BoundingBox_(const BoundingBox& value) noexcept { + return std::optional(value); + } + inline bool has_value_std__optional_BoundingBox_(const std::optional& optional) noexcept { + return optional.has_value(); + } + inline BoundingBox get_std__optional_BoundingBox_(const std::optional& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_Corner_ = std::vector; + inline std::vector create_std__vector_Corner_(size_t size) noexcept { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::optional> + /** + * Specialized version of `std::optional>`. + */ + using std__optional_std__vector_Corner__ = std::optional>; + inline std::optional> create_std__optional_std__vector_Corner__(const std::vector& value) noexcept { + return std::optional>(value); + } + inline bool has_value_std__optional_std__vector_Corner__(const std::optional>& optional) noexcept { + return optional.has_value(); + } + inline std::vector get_std__optional_std__vector_Corner__(const std::optional>& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_std__string_ = std::optional; + inline std::optional create_std__optional_std__string_(const std::string& value) noexcept { + return std::optional(value); + } + inline bool has_value_std__optional_std__string_(const std::optional& optional) noexcept { + return optional.has_value(); + } + inline std::string get_std__optional_std__string_(const std::optional& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_double_ = std::vector; + inline std::vector create_std__vector_double_(size_t size) noexcept { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::optional> + /** + * Specialized version of `std::optional>`. + */ + using std__optional_std__vector_double__ = std::optional>; + inline std::optional> create_std__optional_std__vector_double__(const std::vector& value) noexcept { + return std::optional>(value); + } + inline bool has_value_std__optional_std__vector_double__(const std::optional>& optional) noexcept { + return optional.has_value(); + } + inline std::vector get_std__optional_std__vector_double__(const std::optional>& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::optional> + /** + * Specialized version of `std::optional>`. + */ + using std__optional_std__shared_ptr_AnyMap__ = std::optional>; + inline std::optional> create_std__optional_std__shared_ptr_AnyMap__(const std::shared_ptr& value) noexcept { + return std::optional>(value); + } + inline bool has_value_std__optional_std__shared_ptr_AnyMap__(const std::optional>& optional) noexcept { + return optional.has_value(); + } + inline std::shared_ptr get_std__optional_std__shared_ptr_AnyMap__(const std::optional>& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_BarcodeScannerBarcode_ = std::vector; + inline std::vector create_std__vector_BarcodeScannerBarcode_(size_t size) noexcept { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::shared_ptr + /** + * Specialized version of `std::shared_ptr`. + */ + using std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_(std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_ cppType); + + // pragma MARK: std::weak_ptr + using std__weak_ptr_margelo__nitro__camera__HybridFrameSpec_ = std::weak_ptr; + inline std__weak_ptr_margelo__nitro__camera__HybridFrameSpec_ weakify_std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_(const std::shared_ptr& strong) noexcept { return strong; } + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_Orientation_ = std::optional; + inline std::optional create_std__optional_Orientation_(const Orientation& value) noexcept { + return std::optional(value); + } + inline bool has_value_std__optional_Orientation_(const std::optional& optional) noexcept { + return optional.has_value(); + } + inline Orientation get_std__optional_Orientation_(const std::optional& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_MLKitBaseArguments_ = std::optional; + inline std::optional create_std__optional_MLKitBaseArguments_(const MLKitBaseArguments& value) noexcept { + return std::optional(value); + } + inline bool has_value_std__optional_MLKitBaseArguments_(const std::optional& optional) noexcept { + return optional.has_value(); + } + inline MLKitBaseArguments get_std__optional_MLKitBaseArguments_(const std::optional& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::shared_ptr> + /** + * Specialized version of `std::shared_ptr>`. + */ + using std__shared_ptr_Promise_BarcodeScannerResult__ = std::shared_ptr>; + inline std::shared_ptr> create_std__shared_ptr_Promise_BarcodeScannerResult__() noexcept { + return Promise::create(); + } + inline PromiseHolder wrap_std__shared_ptr_Promise_BarcodeScannerResult__(std::shared_ptr> promise) noexcept { + return PromiseHolder(std::move(promise)); + } + + // pragma MARK: std::function + /** + * Specialized version of `std::function`. + */ + using Func_void_BarcodeScannerResult = std::function; + /** + * Wrapper class for a `std::function`, this can be used from Swift. + */ + class Func_void_BarcodeScannerResult_Wrapper final { + public: + explicit Func_void_BarcodeScannerResult_Wrapper(std::function&& func): _function(std::make_unique>(std::move(func))) {} + inline void call(BarcodeScannerResult result) const noexcept { + _function->operator()(result); + } + private: + std::unique_ptr> _function; + } SWIFT_NONCOPYABLE; + Func_void_BarcodeScannerResult create_Func_void_BarcodeScannerResult(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_BarcodeScannerResult_Wrapper wrap_Func_void_BarcodeScannerResult(Func_void_BarcodeScannerResult value) noexcept { + return Func_void_BarcodeScannerResult_Wrapper(std::move(value)); + } + + // pragma MARK: std::function + /** + * Specialized version of `std::function`. + */ + using Func_void_std__exception_ptr = std::function; + /** + * Wrapper class for a `std::function`, this can be used from Swift. + */ + class Func_void_std__exception_ptr_Wrapper final { + public: + explicit Func_void_std__exception_ptr_Wrapper(std::function&& func): _function(std::make_unique>(std::move(func))) {} + inline void call(std::exception_ptr error) const noexcept { + _function->operator()(error); + } + private: + std::unique_ptr> _function; + } SWIFT_NONCOPYABLE; + Func_void_std__exception_ptr create_Func_void_std__exception_ptr(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_std__exception_ptr_Wrapper wrap_Func_void_std__exception_ptr(Func_void_std__exception_ptr value) noexcept { + return Func_void_std__exception_ptr_Wrapper(std::move(value)); + } + + // pragma MARK: std::shared_ptr + /** + * Specialized version of `std::shared_ptr`. + */ + using std__shared_ptr_HybridBarcodeScannerSpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_HybridBarcodeScannerSpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_HybridBarcodeScannerSpec_(std__shared_ptr_HybridBarcodeScannerSpec_ cppType); + + // pragma MARK: std::weak_ptr + using std__weak_ptr_HybridBarcodeScannerSpec_ = std::weak_ptr; + inline std__weak_ptr_HybridBarcodeScannerSpec_ weakify_std__shared_ptr_HybridBarcodeScannerSpec_(const std::shared_ptr& strong) noexcept { return strong; } + + // pragma MARK: Result + using Result_BarcodeScannerResult_ = Result; + inline Result_BarcodeScannerResult_ create_Result_BarcodeScannerResult_(const BarcodeScannerResult& value) noexcept { + return Result::withValue(value); + } + inline Result_BarcodeScannerResult_ create_Result_BarcodeScannerResult_(const std::exception_ptr& error) noexcept { + return Result::withError(error); + } + + // pragma MARK: Result>> + using Result_std__shared_ptr_Promise_BarcodeScannerResult___ = Result>>; + inline Result_std__shared_ptr_Promise_BarcodeScannerResult___ create_Result_std__shared_ptr_Promise_BarcodeScannerResult___(const std::shared_ptr>& value) noexcept { + return Result>>::withValue(value); + } + inline Result_std__shared_ptr_Promise_BarcodeScannerResult___ create_Result_std__shared_ptr_Promise_BarcodeScannerResult___(const std::exception_ptr& error) noexcept { + return Result>>::withError(error); + } + + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_std__string_ = std::vector; + inline std::vector create_std__vector_std__string_(size_t size) noexcept { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::variant + /** + * Wrapper struct for `std::variant`. + * std::variant cannot be used in Swift because of a Swift bug. + * Not even specializing it works. So we create a wrapper struct. + */ + struct std__variant_nitro__NullType__double_ final { + std::variant variant; + std__variant_nitro__NullType__double_(std::variant variant): variant(variant) { } + operator std::variant() const noexcept { + return variant; + } + inline size_t index() const noexcept { + return variant.index(); + } + inline nitro::NullType get_0() const noexcept { + return std::get<0>(variant); + } + inline double get_1() const noexcept { + return std::get<1>(variant); + } + }; + inline std__variant_nitro__NullType__double_ create_std__variant_nitro__NullType__double_(nitro::NullType value) noexcept { + return std__variant_nitro__NullType__double_(value); + } + inline std__variant_nitro__NullType__double_ create_std__variant_nitro__NullType__double_(double value) noexcept { + return std__variant_nitro__NullType__double_(value); + } + + // pragma MARK: std::optional> + /** + * Specialized version of `std::optional>`. + */ + using std__optional_std__variant_nitro__NullType__double__ = std::optional>; + inline std::optional> create_std__optional_std__variant_nitro__NullType__double__(const std::variant& value) noexcept { + return std::optional>(value); + } + inline bool has_value_std__optional_std__variant_nitro__NullType__double__(const std::optional>& optional) noexcept { + return optional.has_value(); + } + inline std::variant get_std__optional_std__variant_nitro__NullType__double__(const std::optional>& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_TextSymbol_ = std::vector; + inline std::vector create_std__vector_TextSymbol_(size_t size) noexcept { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_TextElement_ = std::vector; + inline std::vector create_std__vector_TextElement_(size_t size) noexcept { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_TextLine_ = std::vector; + inline std::vector create_std__vector_TextLine_(size_t size) noexcept { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_TextBlock_ = std::vector; + inline std::vector create_std__vector_TextBlock_(size_t size) noexcept { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::shared_ptr> + /** + * Specialized version of `std::shared_ptr>`. + */ + using std__shared_ptr_Promise_TextRecognitionResult__ = std::shared_ptr>; + inline std::shared_ptr> create_std__shared_ptr_Promise_TextRecognitionResult__() noexcept { + return Promise::create(); + } + inline PromiseHolder wrap_std__shared_ptr_Promise_TextRecognitionResult__(std::shared_ptr> promise) noexcept { + return PromiseHolder(std::move(promise)); + } + + // pragma MARK: std::function + /** + * Specialized version of `std::function`. + */ + using Func_void_TextRecognitionResult = std::function; + /** + * Wrapper class for a `std::function`, this can be used from Swift. + */ + class Func_void_TextRecognitionResult_Wrapper final { + public: + explicit Func_void_TextRecognitionResult_Wrapper(std::function&& func): _function(std::make_unique>(std::move(func))) {} + inline void call(TextRecognitionResult result) const noexcept { + _function->operator()(result); + } + private: + std::unique_ptr> _function; + } SWIFT_NONCOPYABLE; + Func_void_TextRecognitionResult create_Func_void_TextRecognitionResult(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_TextRecognitionResult_Wrapper wrap_Func_void_TextRecognitionResult(Func_void_TextRecognitionResult value) noexcept { + return Func_void_TextRecognitionResult_Wrapper(std::move(value)); + } + + // pragma MARK: std::shared_ptr + /** + * Specialized version of `std::shared_ptr`. + */ + using std__shared_ptr_HybridTextRecognizerSpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_HybridTextRecognizerSpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_HybridTextRecognizerSpec_(std__shared_ptr_HybridTextRecognizerSpec_ cppType); + + // pragma MARK: std::weak_ptr + using std__weak_ptr_HybridTextRecognizerSpec_ = std::weak_ptr; + inline std__weak_ptr_HybridTextRecognizerSpec_ weakify_std__shared_ptr_HybridTextRecognizerSpec_(const std::shared_ptr& strong) noexcept { return strong; } + + // pragma MARK: Result + using Result_TextRecognitionResult_ = Result; + inline Result_TextRecognitionResult_ create_Result_TextRecognitionResult_(const TextRecognitionResult& value) noexcept { + return Result::withValue(value); + } + inline Result_TextRecognitionResult_ create_Result_TextRecognitionResult_(const std::exception_ptr& error) noexcept { + return Result::withError(error); + } + + // pragma MARK: Result>> + using Result_std__shared_ptr_Promise_TextRecognitionResult___ = Result>>; + inline Result_std__shared_ptr_Promise_TextRecognitionResult___ create_Result_std__shared_ptr_Promise_TextRecognitionResult___(const std::shared_ptr>& value) noexcept { + return Result>>::withValue(value); + } + inline Result_std__shared_ptr_Promise_TextRecognitionResult___ create_Result_std__shared_ptr_Promise_TextRecognitionResult___(const std::exception_ptr& error) noexcept { + return Result>>::withError(error); + } + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_TextRecognitionLanguage_ = std::optional; + inline std::optional create_std__optional_TextRecognitionLanguage_(const TextRecognitionLanguage& value) noexcept { + return std::optional(value); + } + inline bool has_value_std__optional_TextRecognitionLanguage_(const std::optional& optional) noexcept { + return optional.has_value(); + } + inline TextRecognitionLanguage get_std__optional_TextRecognitionLanguage_(const std::optional& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_double_ = std::optional; + inline std::optional create_std__optional_double_(const double& value) noexcept { + return std::optional(value); + } + inline bool has_value_std__optional_double_(const std::optional& optional) noexcept { + return optional.has_value(); + } + inline double get_std__optional_double_(const std::optional& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_bool_ = std::optional; + inline std::optional create_std__optional_bool_(const bool& value) noexcept { + return std::optional(value); + } + inline bool has_value_std__optional_bool_(const std::optional& optional) noexcept { + return optional.has_value(); + } + inline bool get_std__optional_bool_(const std::optional& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_RegionOfInterestUnit_ = std::optional; + inline std::optional create_std__optional_RegionOfInterestUnit_(const RegionOfInterestUnit& value) noexcept { + return std::optional(value); + } + inline bool has_value_std__optional_RegionOfInterestUnit_(const std::optional& optional) noexcept { + return optional.has_value(); + } + inline RegionOfInterestUnit get_std__optional_RegionOfInterestUnit_(const std::optional& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_RegionOfInterest_ = std::optional; + inline std::optional create_std__optional_RegionOfInterest_(const RegionOfInterest& value) noexcept { + return std::optional(value); + } + inline bool has_value_std__optional_RegionOfInterest_(const std::optional& optional) noexcept { + return optional.has_value(); + } + inline RegionOfInterest get_std__optional_RegionOfInterest_(const std::optional& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_BarcodeFormat_ = std::vector; + inline std::vector create_std__vector_BarcodeFormat_(size_t size) noexcept { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::optional> + /** + * Specialized version of `std::optional>`. + */ + using std__optional_std__vector_BarcodeFormat__ = std::optional>; + inline std::optional> create_std__optional_std__vector_BarcodeFormat__(const std::vector& value) noexcept { + return std::optional>(value); + } + inline bool has_value_std__optional_std__vector_BarcodeFormat__(const std::optional>& optional) noexcept { + return optional.has_value(); + } + inline std::vector get_std__optional_std__vector_BarcodeFormat__(const std::optional>& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::shared_ptr + /** + * Specialized version of `std::shared_ptr`. + */ + using std__shared_ptr_HybridVisionCameraMLKitSpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_HybridVisionCameraMLKitSpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_HybridVisionCameraMLKitSpec_(std__shared_ptr_HybridVisionCameraMLKitSpec_ cppType); + + // pragma MARK: std::weak_ptr + using std__weak_ptr_HybridVisionCameraMLKitSpec_ = std::weak_ptr; + inline std__weak_ptr_HybridVisionCameraMLKitSpec_ weakify_std__shared_ptr_HybridVisionCameraMLKitSpec_(const std::shared_ptr& strong) noexcept { return strong; } + + // pragma MARK: Result + using Result_bool_ = Result; + inline Result_bool_ create_Result_bool_(bool value) noexcept { + return Result::withValue(std::move(value)); + } + inline Result_bool_ create_Result_bool_(const std::exception_ptr& error) noexcept { + return Result::withError(error); + } + + // pragma MARK: Result> + using Result_std__shared_ptr_HybridTextRecognizerSpec__ = Result>; + inline Result_std__shared_ptr_HybridTextRecognizerSpec__ create_Result_std__shared_ptr_HybridTextRecognizerSpec__(const std::shared_ptr& value) noexcept { + return Result>::withValue(value); + } + inline Result_std__shared_ptr_HybridTextRecognizerSpec__ create_Result_std__shared_ptr_HybridTextRecognizerSpec__(const std::exception_ptr& error) noexcept { + return Result>::withError(error); + } + + // pragma MARK: Result> + using Result_std__shared_ptr_HybridBarcodeScannerSpec__ = Result>; + inline Result_std__shared_ptr_HybridBarcodeScannerSpec__ create_Result_std__shared_ptr_HybridBarcodeScannerSpec__(const std::shared_ptr& value) noexcept { + return Result>::withValue(value); + } + inline Result_std__shared_ptr_HybridBarcodeScannerSpec__ create_Result_std__shared_ptr_HybridBarcodeScannerSpec__(const std::exception_ptr& error) noexcept { + return Result>::withError(error); + } + +} // namespace margelo::nitro::visioncameramlkit::bridge::swift diff --git a/nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Umbrella.hpp b/nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Umbrella.hpp new file mode 100644 index 0000000..67dd681 --- /dev/null +++ b/nitrogen/generated/ios/VisionCameraMLKit-Swift-Cxx-Umbrella.hpp @@ -0,0 +1,122 @@ +/// +/// VisionCameraMLKit-Swift-Cxx-Umbrella.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +// Forward declarations of C++ defined types +// Forward declaration of `BarcodeFormat` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeFormat; } +// Forward declaration of `BarcodeScannerBarcode` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerBarcode; } +// Forward declaration of `BarcodeScannerOptions` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerOptions; } +// Forward declaration of `BarcodeScannerResult` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerResult; } +// Forward declaration of `BarcodeValueTypeName` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeValueTypeName; } +// Forward declaration of `BoundingBox` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BoundingBox; } +// Forward declaration of `Corner` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct Corner; } +// Forward declaration of `HybridBarcodeScannerSpec` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { class HybridBarcodeScannerSpec; } +// Forward declaration of `HybridFrameSpec` to properly resolve imports. +namespace margelo::nitro::camera { class HybridFrameSpec; } +// Forward declaration of `HybridTextRecognizerSpec` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { class HybridTextRecognizerSpec; } +// Forward declaration of `HybridVisionCameraMLKitSpec` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { class HybridVisionCameraMLKitSpec; } +// Forward declaration of `MLKitBaseArguments` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct MLKitBaseArguments; } +// Forward declaration of `MLKitFeature` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class MLKitFeature; } +// Forward declaration of `Orientation` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class Orientation; } +// Forward declaration of `RegionOfInterestUnit` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class RegionOfInterestUnit; } +// Forward declaration of `RegionOfInterest` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct RegionOfInterest; } +// Forward declaration of `TextBlock` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextBlock; } +// Forward declaration of `TextElement` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextElement; } +// Forward declaration of `TextLine` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextLine; } +// Forward declaration of `TextRecognitionLanguage` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class TextRecognitionLanguage; } +// Forward declaration of `TextRecognitionResult` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextRecognitionResult; } +// Forward declaration of `TextRecognizerOptions` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextRecognizerOptions; } +// Forward declaration of `TextSymbol` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextSymbol; } + +// Include C++ defined types +#include "BarcodeFormat.hpp" +#include "BarcodeScannerBarcode.hpp" +#include "BarcodeScannerOptions.hpp" +#include "BarcodeScannerResult.hpp" +#include "BarcodeValueTypeName.hpp" +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include "HybridBarcodeScannerSpec.hpp" +#include "HybridTextRecognizerSpec.hpp" +#include "HybridVisionCameraMLKitSpec.hpp" +#include "MLKitBaseArguments.hpp" +#include "MLKitFeature.hpp" +#include "Orientation.hpp" +#include "RegionOfInterest.hpp" +#include "RegionOfInterestUnit.hpp" +#include "TextBlock.hpp" +#include "TextElement.hpp" +#include "TextLine.hpp" +#include "TextRecognitionLanguage.hpp" +#include "TextRecognitionResult.hpp" +#include "TextRecognizerOptions.hpp" +#include "TextSymbol.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// C++ helpers for Swift +#include "VisionCameraMLKit-Swift-Cxx-Bridge.hpp" + +// Common C++ types used in Swift +#include +#include +#include +#include + +// Forward declarations of Swift defined types +// Forward declaration of `HybridBarcodeScannerSpec_cxx` to properly resolve imports. +namespace VisionCameraMLKit { class HybridBarcodeScannerSpec_cxx; } +// Forward declaration of `HybridFrameSpec_cxx` to properly resolve imports. +namespace VisionCamera { class HybridFrameSpec_cxx; } +// Forward declaration of `HybridTextRecognizerSpec_cxx` to properly resolve imports. +namespace VisionCameraMLKit { class HybridTextRecognizerSpec_cxx; } +// Forward declaration of `HybridVisionCameraMLKitSpec_cxx` to properly resolve imports. +namespace VisionCameraMLKit { class HybridVisionCameraMLKitSpec_cxx; } + +// Include Swift defined types +#if __has_include("VisionCameraMLKit-Swift.h") +// This header is generated by Xcode/Swift on every app build. +// If it cannot be found, make sure the Swift module's name (= podspec name) is actually "VisionCameraMLKit". +#include "VisionCameraMLKit-Swift.h" +// Same as above, but used when building with frameworks (`use_frameworks`) +#elif __has_include() +#include +#else +#error VisionCameraMLKit's autogenerated Swift header cannot be found! Make sure the Swift module's name (= podspec name) is actually "VisionCameraMLKit", and try building the app first. +#endif diff --git a/nitrogen/generated/ios/VisionCameraMLKitAutolinking.mm b/nitrogen/generated/ios/VisionCameraMLKitAutolinking.mm new file mode 100644 index 0000000..e0f195e --- /dev/null +++ b/nitrogen/generated/ios/VisionCameraMLKitAutolinking.mm @@ -0,0 +1,33 @@ +/// +/// VisionCameraMLKitAutolinking.mm +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#import +#import +#import "VisionCameraMLKit-Swift-Cxx-Umbrella.hpp" +#import + +#include "HybridVisionCameraMLKitSpecSwift.hpp" + +@interface VisionCameraMLKitAutolinking : NSObject +@end + +@implementation VisionCameraMLKitAutolinking + ++ (void) load { + using namespace margelo::nitro; + using namespace margelo::nitro::visioncameramlkit; + + HybridObjectRegistry::registerHybridObjectConstructor( + "VisionCameraMLKit", + []() -> std::shared_ptr { + std::shared_ptr hybridObject = VisionCameraMLKit::VisionCameraMLKitAutolinking::createVisionCameraMLKit(); + return hybridObject; + } + ); +} + +@end diff --git a/nitrogen/generated/ios/VisionCameraMLKitAutolinking.swift b/nitrogen/generated/ios/VisionCameraMLKitAutolinking.swift new file mode 100644 index 0000000..ea9ddd3 --- /dev/null +++ b/nitrogen/generated/ios/VisionCameraMLKitAutolinking.swift @@ -0,0 +1,26 @@ +/// +/// VisionCameraMLKitAutolinking.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +// TODO: Use empty enums once Swift supports exporting them as namespaces +// See: https://github.com/swiftlang/swift/pull/83616 +public final class VisionCameraMLKitAutolinking { + public typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + public static func createVisionCameraMLKit() -> bridge.std__shared_ptr_HybridVisionCameraMLKitSpec_ { + let hybridObject = HybridVisionCameraMLKit() + return { () -> bridge.std__shared_ptr_HybridVisionCameraMLKitSpec_ in + let __cxxWrapped = hybridObject.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }() + } + + public static func isVisionCameraMLKitRecyclable() -> Bool { + return HybridVisionCameraMLKit.self is any RecyclableView.Type + } +} diff --git a/nitrogen/generated/ios/c++/HybridBarcodeScannerSpecSwift.cpp b/nitrogen/generated/ios/c++/HybridBarcodeScannerSpecSwift.cpp new file mode 100644 index 0000000..4f850da --- /dev/null +++ b/nitrogen/generated/ios/c++/HybridBarcodeScannerSpecSwift.cpp @@ -0,0 +1,11 @@ +/// +/// HybridBarcodeScannerSpecSwift.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridBarcodeScannerSpecSwift.hpp" + +namespace margelo::nitro::visioncameramlkit { +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/ios/c++/HybridBarcodeScannerSpecSwift.hpp b/nitrogen/generated/ios/c++/HybridBarcodeScannerSpecSwift.hpp new file mode 100644 index 0000000..48d857f --- /dev/null +++ b/nitrogen/generated/ios/c++/HybridBarcodeScannerSpecSwift.hpp @@ -0,0 +1,121 @@ +/// +/// HybridBarcodeScannerSpecSwift.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include "HybridBarcodeScannerSpec.hpp" + +// Forward declaration of `HybridBarcodeScannerSpec_cxx` to properly resolve imports. +namespace VisionCameraMLKit { class HybridBarcodeScannerSpec_cxx; } + +// Forward declaration of `BarcodeScannerResult` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerResult; } +// Forward declaration of `BarcodeScannerBarcode` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerBarcode; } +// Forward declaration of `BoundingBox` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BoundingBox; } +// Forward declaration of `Corner` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct Corner; } +// Forward declaration of `BarcodeFormat` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeFormat; } +// Forward declaration of `BarcodeValueTypeName` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeValueTypeName; } +// Forward declaration of `HybridFrameSpec` to properly resolve imports. +namespace margelo::nitro::camera { class HybridFrameSpec; } +// Forward declaration of `MLKitBaseArguments` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct MLKitBaseArguments; } +// Forward declaration of `Orientation` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class Orientation; } + +#include "BarcodeScannerResult.hpp" +#include "BarcodeScannerBarcode.hpp" +#include +#include "BoundingBox.hpp" +#include +#include "Corner.hpp" +#include "BarcodeFormat.hpp" +#include "BarcodeValueTypeName.hpp" +#include +#include +#include +#include +#include "MLKitBaseArguments.hpp" +#include "Orientation.hpp" +#include + +#include "VisionCameraMLKit-Swift-Cxx-Umbrella.hpp" + +namespace margelo::nitro::visioncameramlkit { + + /** + * The C++ part of HybridBarcodeScannerSpec_cxx.swift. + * + * HybridBarcodeScannerSpecSwift (C++) accesses HybridBarcodeScannerSpec_cxx (Swift), and might + * contain some additional bridging code for C++ <> Swift interop. + * + * Since this obviously introduces an overhead, I hope at some point in + * the future, HybridBarcodeScannerSpec_cxx can directly inherit from the C++ class HybridBarcodeScannerSpec + * to simplify the whole structure and memory management. + */ + class HybridBarcodeScannerSpecSwift: public virtual HybridBarcodeScannerSpec { + public: + // Constructor from a Swift instance + explicit HybridBarcodeScannerSpecSwift(const VisionCameraMLKit::HybridBarcodeScannerSpec_cxx& swiftPart): + HybridObject(HybridBarcodeScannerSpec::TAG), + _swiftPart(swiftPart) { } + + public: + // Get the Swift part + inline VisionCameraMLKit::HybridBarcodeScannerSpec_cxx& getSwiftPart() noexcept { + return _swiftPart; + } + + public: + inline size_t getExternalMemorySize() noexcept override { + return _swiftPart.getMemorySize(); + } + bool equals(const std::shared_ptr& other) override { + if (auto otherCast = std::dynamic_pointer_cast(other)) { + return _swiftPart.equals(otherCast->_swiftPart); + } + return false; + } + void dispose() noexcept override { + _swiftPart.dispose(); + } + std::string toString() override { + return _swiftPart.toString(); + } + + public: + // Properties + + + public: + // Methods + inline BarcodeScannerResult recognize(const std::shared_ptr& frame, const std::optional& args) override { + auto __result = _swiftPart.recognize(frame, args); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> recognizeImage(const std::string& uri) override { + auto __result = _swiftPart.recognizeImage(uri); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + + private: + VisionCameraMLKit::HybridBarcodeScannerSpec_cxx _swiftPart; + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/ios/c++/HybridTextRecognizerSpecSwift.cpp b/nitrogen/generated/ios/c++/HybridTextRecognizerSpecSwift.cpp new file mode 100644 index 0000000..2b94b1d --- /dev/null +++ b/nitrogen/generated/ios/c++/HybridTextRecognizerSpecSwift.cpp @@ -0,0 +1,11 @@ +/// +/// HybridTextRecognizerSpecSwift.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridTextRecognizerSpecSwift.hpp" + +namespace margelo::nitro::visioncameramlkit { +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/ios/c++/HybridTextRecognizerSpecSwift.hpp b/nitrogen/generated/ios/c++/HybridTextRecognizerSpecSwift.hpp new file mode 100644 index 0000000..7dc3c66 --- /dev/null +++ b/nitrogen/generated/ios/c++/HybridTextRecognizerSpecSwift.hpp @@ -0,0 +1,125 @@ +/// +/// HybridTextRecognizerSpecSwift.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include "HybridTextRecognizerSpec.hpp" + +// Forward declaration of `HybridTextRecognizerSpec_cxx` to properly resolve imports. +namespace VisionCameraMLKit { class HybridTextRecognizerSpec_cxx; } + +// Forward declaration of `TextRecognitionResult` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextRecognitionResult; } +// Forward declaration of `TextBlock` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextBlock; } +// Forward declaration of `BoundingBox` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BoundingBox; } +// Forward declaration of `Corner` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct Corner; } +// Forward declaration of `TextLine` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextLine; } +// Forward declaration of `TextElement` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextElement; } +// Forward declaration of `TextSymbol` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextSymbol; } +// Forward declaration of `HybridFrameSpec` to properly resolve imports. +namespace margelo::nitro::camera { class HybridFrameSpec; } +// Forward declaration of `MLKitBaseArguments` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct MLKitBaseArguments; } +// Forward declaration of `Orientation` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class Orientation; } + +#include "TextRecognitionResult.hpp" +#include +#include "TextBlock.hpp" +#include +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include "TextLine.hpp" +#include +#include +#include +#include "TextElement.hpp" +#include "TextSymbol.hpp" +#include +#include +#include "MLKitBaseArguments.hpp" +#include "Orientation.hpp" +#include + +#include "VisionCameraMLKit-Swift-Cxx-Umbrella.hpp" + +namespace margelo::nitro::visioncameramlkit { + + /** + * The C++ part of HybridTextRecognizerSpec_cxx.swift. + * + * HybridTextRecognizerSpecSwift (C++) accesses HybridTextRecognizerSpec_cxx (Swift), and might + * contain some additional bridging code for C++ <> Swift interop. + * + * Since this obviously introduces an overhead, I hope at some point in + * the future, HybridTextRecognizerSpec_cxx can directly inherit from the C++ class HybridTextRecognizerSpec + * to simplify the whole structure and memory management. + */ + class HybridTextRecognizerSpecSwift: public virtual HybridTextRecognizerSpec { + public: + // Constructor from a Swift instance + explicit HybridTextRecognizerSpecSwift(const VisionCameraMLKit::HybridTextRecognizerSpec_cxx& swiftPart): + HybridObject(HybridTextRecognizerSpec::TAG), + _swiftPart(swiftPart) { } + + public: + // Get the Swift part + inline VisionCameraMLKit::HybridTextRecognizerSpec_cxx& getSwiftPart() noexcept { + return _swiftPart; + } + + public: + inline size_t getExternalMemorySize() noexcept override { + return _swiftPart.getMemorySize(); + } + bool equals(const std::shared_ptr& other) override { + if (auto otherCast = std::dynamic_pointer_cast(other)) { + return _swiftPart.equals(otherCast->_swiftPart); + } + return false; + } + void dispose() noexcept override { + _swiftPart.dispose(); + } + std::string toString() override { + return _swiftPart.toString(); + } + + public: + // Properties + + + public: + // Methods + inline TextRecognitionResult recognize(const std::shared_ptr& frame, const std::optional& args) override { + auto __result = _swiftPart.recognize(frame, args); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> recognizeImage(const std::string& uri) override { + auto __result = _swiftPart.recognizeImage(uri); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + + private: + VisionCameraMLKit::HybridTextRecognizerSpec_cxx _swiftPart; + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/ios/c++/HybridVisionCameraMLKitSpecSwift.cpp b/nitrogen/generated/ios/c++/HybridVisionCameraMLKitSpecSwift.cpp new file mode 100644 index 0000000..ed5a009 --- /dev/null +++ b/nitrogen/generated/ios/c++/HybridVisionCameraMLKitSpecSwift.cpp @@ -0,0 +1,11 @@ +/// +/// HybridVisionCameraMLKitSpecSwift.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridVisionCameraMLKitSpecSwift.hpp" + +namespace margelo::nitro::visioncameramlkit { +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/ios/c++/HybridVisionCameraMLKitSpecSwift.hpp b/nitrogen/generated/ios/c++/HybridVisionCameraMLKitSpecSwift.hpp new file mode 100644 index 0000000..b73a252 --- /dev/null +++ b/nitrogen/generated/ios/c++/HybridVisionCameraMLKitSpecSwift.hpp @@ -0,0 +1,129 @@ +/// +/// HybridVisionCameraMLKitSpecSwift.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include "HybridVisionCameraMLKitSpec.hpp" + +// Forward declaration of `HybridVisionCameraMLKitSpec_cxx` to properly resolve imports. +namespace VisionCameraMLKit { class HybridVisionCameraMLKitSpec_cxx; } + +// Forward declaration of `MLKitFeature` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class MLKitFeature; } +// Forward declaration of `HybridTextRecognizerSpec` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { class HybridTextRecognizerSpec; } +// Forward declaration of `TextRecognizerOptions` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextRecognizerOptions; } +// Forward declaration of `TextRecognitionLanguage` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class TextRecognitionLanguage; } +// Forward declaration of `RegionOfInterest` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct RegionOfInterest; } +// Forward declaration of `RegionOfInterestUnit` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class RegionOfInterestUnit; } +// Forward declaration of `Orientation` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class Orientation; } +// Forward declaration of `HybridBarcodeScannerSpec` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { class HybridBarcodeScannerSpec; } +// Forward declaration of `BarcodeScannerOptions` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerOptions; } +// Forward declaration of `BarcodeFormat` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeFormat; } + +#include "MLKitFeature.hpp" +#include +#include "HybridTextRecognizerSpec.hpp" +#include "TextRecognizerOptions.hpp" +#include "TextRecognitionLanguage.hpp" +#include +#include "RegionOfInterest.hpp" +#include "RegionOfInterestUnit.hpp" +#include "Orientation.hpp" +#include "HybridBarcodeScannerSpec.hpp" +#include "BarcodeScannerOptions.hpp" +#include "BarcodeFormat.hpp" +#include + +#include "VisionCameraMLKit-Swift-Cxx-Umbrella.hpp" + +namespace margelo::nitro::visioncameramlkit { + + /** + * The C++ part of HybridVisionCameraMLKitSpec_cxx.swift. + * + * HybridVisionCameraMLKitSpecSwift (C++) accesses HybridVisionCameraMLKitSpec_cxx (Swift), and might + * contain some additional bridging code for C++ <> Swift interop. + * + * Since this obviously introduces an overhead, I hope at some point in + * the future, HybridVisionCameraMLKitSpec_cxx can directly inherit from the C++ class HybridVisionCameraMLKitSpec + * to simplify the whole structure and memory management. + */ + class HybridVisionCameraMLKitSpecSwift: public virtual HybridVisionCameraMLKitSpec { + public: + // Constructor from a Swift instance + explicit HybridVisionCameraMLKitSpecSwift(const VisionCameraMLKit::HybridVisionCameraMLKitSpec_cxx& swiftPart): + HybridObject(HybridVisionCameraMLKitSpec::TAG), + _swiftPart(swiftPart) { } + + public: + // Get the Swift part + inline VisionCameraMLKit::HybridVisionCameraMLKitSpec_cxx& getSwiftPart() noexcept { + return _swiftPart; + } + + public: + inline size_t getExternalMemorySize() noexcept override { + return _swiftPart.getMemorySize(); + } + bool equals(const std::shared_ptr& other) override { + if (auto otherCast = std::dynamic_pointer_cast(other)) { + return _swiftPart.equals(otherCast->_swiftPart); + } + return false; + } + void dispose() noexcept override { + _swiftPart.dispose(); + } + std::string toString() override { + return _swiftPart.toString(); + } + + public: + // Properties + + + public: + // Methods + inline bool isFeatureAvailable(MLKitFeature feature) override { + auto __result = _swiftPart.isFeatureAvailable(static_cast(feature)); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr createTextRecognizer(const TextRecognizerOptions& options) override { + auto __result = _swiftPart.createTextRecognizer(std::forward(options)); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr createBarcodeScanner(const BarcodeScannerOptions& options) override { + auto __result = _swiftPart.createBarcodeScanner(std::forward(options)); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + + private: + VisionCameraMLKit::HybridVisionCameraMLKitSpec_cxx _swiftPart; + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/ios/swift/BarcodeFormat.swift b/nitrogen/generated/ios/swift/BarcodeFormat.swift new file mode 100644 index 0000000..65e565e --- /dev/null +++ b/nitrogen/generated/ios/swift/BarcodeFormat.swift @@ -0,0 +1,92 @@ +/// +/// BarcodeFormat.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +/** + * Represents the JS union `BarcodeFormat`, backed by a C++ enum. + */ +public typealias BarcodeFormat = margelo.nitro.visioncameramlkit.BarcodeFormat + +public extension BarcodeFormat { + /** + * Get a BarcodeFormat for the given String value, or + * return `nil` if the given value was invalid/unknown. + */ + init?(fromString string: String) { + switch string { + case "UNKNOWN": + self = .unknown + case "ALL_FORMATS": + self = .allFormats + case "CODE_128": + self = .code128 + case "CODE_39": + self = .code39 + case "CODE_93": + self = .code93 + case "CODABAR": + self = .codabar + case "DATA_MATRIX": + self = .dataMatrix + case "EAN_13": + self = .ean13 + case "EAN_8": + self = .ean8 + case "ITF": + self = .itf + case "QR_CODE": + self = .qrCode + case "UPC_A": + self = .upcA + case "UPC_E": + self = .upcE + case "PDF417": + self = .pdf417 + case "AZTEC": + self = .aztec + default: + return nil + } + } + + /** + * Get the String value this BarcodeFormat represents. + */ + var stringValue: String { + switch self { + case .unknown: + return "UNKNOWN" + case .allFormats: + return "ALL_FORMATS" + case .code128: + return "CODE_128" + case .code39: + return "CODE_39" + case .code93: + return "CODE_93" + case .codabar: + return "CODABAR" + case .dataMatrix: + return "DATA_MATRIX" + case .ean13: + return "EAN_13" + case .ean8: + return "EAN_8" + case .itf: + return "ITF" + case .qrCode: + return "QR_CODE" + case .upcA: + return "UPC_A" + case .upcE: + return "UPC_E" + case .pdf417: + return "PDF417" + case .aztec: + return "AZTEC" + } + } +} diff --git a/nitrogen/generated/ios/swift/BarcodeScannerBarcode.swift b/nitrogen/generated/ios/swift/BarcodeScannerBarcode.swift new file mode 100644 index 0000000..a788533 --- /dev/null +++ b/nitrogen/generated/ios/swift/BarcodeScannerBarcode.swift @@ -0,0 +1,162 @@ +/// +/// BarcodeScannerBarcode.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `BarcodeScannerBarcode`, backed by a C++ struct. + */ +public typealias BarcodeScannerBarcode = margelo.nitro.visioncameramlkit.BarcodeScannerBarcode + +public extension BarcodeScannerBarcode { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `BarcodeScannerBarcode`. + */ + init(bounds: BoundingBox?, corners: [Corner]?, format: Double, formatName: BarcodeFormat, valueType: Double, valueTypeName: BarcodeValueTypeName, rawValue: String?, displayValue: String?, rawBytes: [Double]?, value: AnyMap?, isPotential: Bool) { + self.init({ () -> bridge.std__optional_BoundingBox_ in + if let __unwrappedValue = bounds { + return bridge.create_std__optional_BoundingBox_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_std__vector_Corner__ in + if let __unwrappedValue = corners { + return bridge.create_std__optional_std__vector_Corner__({ () -> bridge.std__vector_Corner_ in + var __vector = bridge.create_std__vector_Corner_(__unwrappedValue.count) + for __item in __unwrappedValue { + __vector.push_back(__item) + } + return __vector + }()) + } else { + return .init() + } + }(), format, formatName, valueType, valueTypeName, { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = rawValue { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }(), { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = displayValue { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }(), { () -> bridge.std__optional_std__vector_double__ in + if let __unwrappedValue = rawBytes { + return bridge.create_std__optional_std__vector_double__({ () -> bridge.std__vector_double_ in + var __vector = bridge.create_std__vector_double_(__unwrappedValue.count) + for __item in __unwrappedValue { + __vector.push_back(__item) + } + return __vector + }()) + } else { + return .init() + } + }(), { () -> bridge.std__optional_std__shared_ptr_AnyMap__ in + if let __unwrappedValue = value { + return bridge.create_std__optional_std__shared_ptr_AnyMap__(__unwrappedValue.cppPart) + } else { + return .init() + } + }(), isPotential) + } + + @inline(__always) + var bounds: BoundingBox? { + return self.__bounds.value + } + + @inline(__always) + var corners: [Corner]? { + return { () -> [Corner]? in + if bridge.has_value_std__optional_std__vector_Corner__(self.__corners) { + let __unwrapped = bridge.get_std__optional_std__vector_Corner__(self.__corners) + return __unwrapped.map({ __item in __item }) + } else { + return nil + } + }() + } + + @inline(__always) + var format: Double { + return self.__format + } + + @inline(__always) + var formatName: BarcodeFormat { + return self.__formatName + } + + @inline(__always) + var valueType: Double { + return self.__valueType + } + + @inline(__always) + var valueTypeName: BarcodeValueTypeName { + return self.__valueTypeName + } + + @inline(__always) + var rawValue: String? { + return { () -> String? in + if bridge.has_value_std__optional_std__string_(self.__rawValue) { + let __unwrapped = bridge.get_std__optional_std__string_(self.__rawValue) + return String(__unwrapped) + } else { + return nil + } + }() + } + + @inline(__always) + var displayValue: String? { + return { () -> String? in + if bridge.has_value_std__optional_std__string_(self.__displayValue) { + let __unwrapped = bridge.get_std__optional_std__string_(self.__displayValue) + return String(__unwrapped) + } else { + return nil + } + }() + } + + @inline(__always) + var rawBytes: [Double]? { + return { () -> [Double]? in + if bridge.has_value_std__optional_std__vector_double__(self.__rawBytes) { + let __unwrapped = bridge.get_std__optional_std__vector_double__(self.__rawBytes) + return __unwrapped.map({ __item in __item }) + } else { + return nil + } + }() + } + + @inline(__always) + var value: AnyMap? { + return { () -> AnyMap? in + if bridge.has_value_std__optional_std__shared_ptr_AnyMap__(self.__value) { + let __unwrapped = bridge.get_std__optional_std__shared_ptr_AnyMap__(self.__value) + return AnyMap(withCppPart: __unwrapped) + } else { + return nil + } + }() + } + + @inline(__always) + var isPotential: Bool { + return self.__isPotential + } +} diff --git a/nitrogen/generated/ios/swift/BarcodeScannerOptions.swift b/nitrogen/generated/ios/swift/BarcodeScannerOptions.swift new file mode 100644 index 0000000..44913a2 --- /dev/null +++ b/nitrogen/generated/ios/swift/BarcodeScannerOptions.swift @@ -0,0 +1,142 @@ +/// +/// BarcodeScannerOptions.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `BarcodeScannerOptions`, backed by a C++ struct. + */ +public typealias BarcodeScannerOptions = margelo.nitro.visioncameramlkit.BarcodeScannerOptions + +public extension BarcodeScannerOptions { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `BarcodeScannerOptions`. + */ + init(formats: [BarcodeFormat]?, enableAllPotentialBarcodes: Bool?, scaleFactor: Double?, invertColors: Bool?, frameProcessInterval: Double?, roi: RegionOfInterest?, orientation: Orientation?) { + self.init({ () -> bridge.std__optional_std__vector_BarcodeFormat__ in + if let __unwrappedValue = formats { + return bridge.create_std__optional_std__vector_BarcodeFormat__({ () -> bridge.std__vector_BarcodeFormat_ in + var __vector = bridge.create_std__vector_BarcodeFormat_(__unwrappedValue.count) + for __item in __unwrappedValue { + __vector.push_back(__item) + } + return __vector + }()) + } else { + return .init() + } + }(), { () -> bridge.std__optional_bool_ in + if let __unwrappedValue = enableAllPotentialBarcodes { + return bridge.create_std__optional_bool_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_double_ in + if let __unwrappedValue = scaleFactor { + return bridge.create_std__optional_double_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_bool_ in + if let __unwrappedValue = invertColors { + return bridge.create_std__optional_bool_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_double_ in + if let __unwrappedValue = frameProcessInterval { + return bridge.create_std__optional_double_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_RegionOfInterest_ in + if let __unwrappedValue = roi { + return bridge.create_std__optional_RegionOfInterest_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_Orientation_ in + if let __unwrappedValue = orientation { + return bridge.create_std__optional_Orientation_(__unwrappedValue) + } else { + return .init() + } + }()) + } + + @inline(__always) + var formats: [BarcodeFormat]? { + return { () -> [BarcodeFormat]? in + if bridge.has_value_std__optional_std__vector_BarcodeFormat__(self.__formats) { + let __unwrapped = bridge.get_std__optional_std__vector_BarcodeFormat__(self.__formats) + return __unwrapped.map({ __item in __item }) + } else { + return nil + } + }() + } + + @inline(__always) + var enableAllPotentialBarcodes: Bool? { + return { () -> Bool? in + if bridge.has_value_std__optional_bool_(self.__enableAllPotentialBarcodes) { + let __unwrapped = bridge.get_std__optional_bool_(self.__enableAllPotentialBarcodes) + return __unwrapped + } else { + return nil + } + }() + } + + @inline(__always) + var scaleFactor: Double? { + return { () -> Double? in + if bridge.has_value_std__optional_double_(self.__scaleFactor) { + let __unwrapped = bridge.get_std__optional_double_(self.__scaleFactor) + return __unwrapped + } else { + return nil + } + }() + } + + @inline(__always) + var invertColors: Bool? { + return { () -> Bool? in + if bridge.has_value_std__optional_bool_(self.__invertColors) { + let __unwrapped = bridge.get_std__optional_bool_(self.__invertColors) + return __unwrapped + } else { + return nil + } + }() + } + + @inline(__always) + var frameProcessInterval: Double? { + return { () -> Double? in + if bridge.has_value_std__optional_double_(self.__frameProcessInterval) { + let __unwrapped = bridge.get_std__optional_double_(self.__frameProcessInterval) + return __unwrapped + } else { + return nil + } + }() + } + + @inline(__always) + var roi: RegionOfInterest? { + return self.__roi.value + } + + @inline(__always) + var orientation: Orientation? { + return self.__orientation.value + } +} diff --git a/nitrogen/generated/ios/swift/BarcodeScannerResult.swift b/nitrogen/generated/ios/swift/BarcodeScannerResult.swift new file mode 100644 index 0000000..4d4d679 --- /dev/null +++ b/nitrogen/generated/ios/swift/BarcodeScannerResult.swift @@ -0,0 +1,35 @@ +/// +/// BarcodeScannerResult.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `BarcodeScannerResult`, backed by a C++ struct. + */ +public typealias BarcodeScannerResult = margelo.nitro.visioncameramlkit.BarcodeScannerResult + +public extension BarcodeScannerResult { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `BarcodeScannerResult`. + */ + init(barcodes: [BarcodeScannerBarcode]) { + self.init({ () -> bridge.std__vector_BarcodeScannerBarcode_ in + var __vector = bridge.create_std__vector_BarcodeScannerBarcode_(barcodes.count) + for __item in barcodes { + __vector.push_back(__item) + } + return __vector + }()) + } + + @inline(__always) + var barcodes: [BarcodeScannerBarcode] { + return self.__barcodes.map({ __item in __item }) + } +} diff --git a/nitrogen/generated/ios/swift/BarcodeValueTypeName.swift b/nitrogen/generated/ios/swift/BarcodeValueTypeName.swift new file mode 100644 index 0000000..8dfd6a9 --- /dev/null +++ b/nitrogen/generated/ios/swift/BarcodeValueTypeName.swift @@ -0,0 +1,84 @@ +/// +/// BarcodeValueTypeName.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +/** + * Represents the JS union `BarcodeValueTypeName`, backed by a C++ enum. + */ +public typealias BarcodeValueTypeName = margelo.nitro.visioncameramlkit.BarcodeValueTypeName + +public extension BarcodeValueTypeName { + /** + * Get a BarcodeValueTypeName for the given String value, or + * return `nil` if the given value was invalid/unknown. + */ + init?(fromString string: String) { + switch string { + case "TYPE_UNKNOWN": + self = .typeUnknown + case "TYPE_CONTACT_INFO": + self = .typeContactInfo + case "TYPE_EMAIL": + self = .typeEmail + case "TYPE_ISBN": + self = .typeIsbn + case "TYPE_PHONE": + self = .typePhone + case "TYPE_PRODUCT": + self = .typeProduct + case "TYPE_SMS": + self = .typeSms + case "TYPE_TEXT": + self = .typeText + case "TYPE_URL": + self = .typeUrl + case "TYPE_WIFI": + self = .typeWifi + case "TYPE_GEO": + self = .typeGeo + case "TYPE_CALENDAR_EVENT": + self = .typeCalendarEvent + case "TYPE_DRIVER_LICENSE": + self = .typeDriverLicense + default: + return nil + } + } + + /** + * Get the String value this BarcodeValueTypeName represents. + */ + var stringValue: String { + switch self { + case .typeUnknown: + return "TYPE_UNKNOWN" + case .typeContactInfo: + return "TYPE_CONTACT_INFO" + case .typeEmail: + return "TYPE_EMAIL" + case .typeIsbn: + return "TYPE_ISBN" + case .typePhone: + return "TYPE_PHONE" + case .typeProduct: + return "TYPE_PRODUCT" + case .typeSms: + return "TYPE_SMS" + case .typeText: + return "TYPE_TEXT" + case .typeUrl: + return "TYPE_URL" + case .typeWifi: + return "TYPE_WIFI" + case .typeGeo: + return "TYPE_GEO" + case .typeCalendarEvent: + return "TYPE_CALENDAR_EVENT" + case .typeDriverLicense: + return "TYPE_DRIVER_LICENSE" + } + } +} diff --git a/nitrogen/generated/ios/swift/BoundingBox.swift b/nitrogen/generated/ios/swift/BoundingBox.swift new file mode 100644 index 0000000..d230897 --- /dev/null +++ b/nitrogen/generated/ios/swift/BoundingBox.swift @@ -0,0 +1,74 @@ +/// +/// BoundingBox.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `BoundingBox`, backed by a C++ struct. + */ +public typealias BoundingBox = margelo.nitro.visioncameramlkit.BoundingBox + +public extension BoundingBox { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `BoundingBox`. + */ + init(x: Double, y: Double, centerX: Double, centerY: Double, width: Double, height: Double, top: Double, left: Double, bottom: Double, right: Double) { + self.init(x, y, centerX, centerY, width, height, top, left, bottom, right) + } + + @inline(__always) + var x: Double { + return self.__x + } + + @inline(__always) + var y: Double { + return self.__y + } + + @inline(__always) + var centerX: Double { + return self.__centerX + } + + @inline(__always) + var centerY: Double { + return self.__centerY + } + + @inline(__always) + var width: Double { + return self.__width + } + + @inline(__always) + var height: Double { + return self.__height + } + + @inline(__always) + var top: Double { + return self.__top + } + + @inline(__always) + var left: Double { + return self.__left + } + + @inline(__always) + var bottom: Double { + return self.__bottom + } + + @inline(__always) + var right: Double { + return self.__right + } +} diff --git a/nitrogen/generated/ios/swift/Corner.swift b/nitrogen/generated/ios/swift/Corner.swift new file mode 100644 index 0000000..f62d6da --- /dev/null +++ b/nitrogen/generated/ios/swift/Corner.swift @@ -0,0 +1,34 @@ +/// +/// Corner.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `Corner`, backed by a C++ struct. + */ +public typealias Corner = margelo.nitro.visioncameramlkit.Corner + +public extension Corner { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `Corner`. + */ + init(x: Double, y: Double) { + self.init(x, y) + } + + @inline(__always) + var x: Double { + return self.__x + } + + @inline(__always) + var y: Double { + return self.__y + } +} diff --git a/nitrogen/generated/ios/swift/Func_void_BarcodeScannerResult.swift b/nitrogen/generated/ios/swift/Func_void_BarcodeScannerResult.swift new file mode 100644 index 0000000..1863916 --- /dev/null +++ b/nitrogen/generated/ios/swift/Func_void_BarcodeScannerResult.swift @@ -0,0 +1,46 @@ +/// +/// Func_void_BarcodeScannerResult.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Wraps a Swift `(_ value: BarcodeScannerResult) -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void_BarcodeScannerResult { + public typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + private let closure: (_ value: BarcodeScannerResult) -> Void + + public init(_ closure: @escaping (_ value: BarcodeScannerResult) -> Void) { + self.closure = closure + } + + @inline(__always) + public func call(value: BarcodeScannerResult) -> Void { + self.closure(value) + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void_BarcodeScannerResult`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_BarcodeScannerResult { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/nitrogen/generated/ios/swift/Func_void_TextRecognitionResult.swift b/nitrogen/generated/ios/swift/Func_void_TextRecognitionResult.swift new file mode 100644 index 0000000..6627cb1 --- /dev/null +++ b/nitrogen/generated/ios/swift/Func_void_TextRecognitionResult.swift @@ -0,0 +1,46 @@ +/// +/// Func_void_TextRecognitionResult.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Wraps a Swift `(_ value: TextRecognitionResult) -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void_TextRecognitionResult { + public typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + private let closure: (_ value: TextRecognitionResult) -> Void + + public init(_ closure: @escaping (_ value: TextRecognitionResult) -> Void) { + self.closure = closure + } + + @inline(__always) + public func call(value: TextRecognitionResult) -> Void { + self.closure(value) + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void_TextRecognitionResult`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_TextRecognitionResult { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift b/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift new file mode 100644 index 0000000..7c53657 --- /dev/null +++ b/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift @@ -0,0 +1,46 @@ +/// +/// Func_void_std__exception_ptr.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Wraps a Swift `(_ error: Error) -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void_std__exception_ptr { + public typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + private let closure: (_ error: Error) -> Void + + public init(_ closure: @escaping (_ error: Error) -> Void) { + self.closure = closure + } + + @inline(__always) + public func call(error: std.exception_ptr) -> Void { + self.closure(RuntimeError.from(cppError: error)) + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void_std__exception_ptr`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_std__exception_ptr { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/nitrogen/generated/ios/swift/HybridBarcodeScannerSpec.swift b/nitrogen/generated/ios/swift/HybridBarcodeScannerSpec.swift new file mode 100644 index 0000000..6122920 --- /dev/null +++ b/nitrogen/generated/ios/swift/HybridBarcodeScannerSpec.swift @@ -0,0 +1,57 @@ +/// +/// HybridBarcodeScannerSpec.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import VisionCamera +import NitroModules + +/// See ``HybridBarcodeScannerSpec`` +public protocol HybridBarcodeScannerSpec_protocol: HybridObject { + // Properties + + + // Methods + func recognize(frame: (any HybridFrameSpec), args: MLKitBaseArguments?) throws -> BarcodeScannerResult + func recognizeImage(uri: String) throws -> Promise +} + +public extension HybridBarcodeScannerSpec_protocol { + /// Default implementation of ``HybridObject.toString`` + func toString() -> String { + return "[HybridObject BarcodeScanner]" + } +} + +/// See ``HybridBarcodeScannerSpec`` +open class HybridBarcodeScannerSpec_base { + private weak var cxxWrapper: HybridBarcodeScannerSpec_cxx? = nil + public init() { } + public func getCxxWrapper() -> HybridBarcodeScannerSpec_cxx { + #if DEBUG + guard self is any HybridBarcodeScannerSpec else { + fatalError("`self` is not a `HybridBarcodeScannerSpec`! Did you accidentally inherit from `HybridBarcodeScannerSpec_base` instead of `HybridBarcodeScannerSpec`?") + } + #endif + if let cxxWrapper = self.cxxWrapper { + return cxxWrapper + } else { + let cxxWrapper = HybridBarcodeScannerSpec_cxx(self as! any HybridBarcodeScannerSpec) + self.cxxWrapper = cxxWrapper + return cxxWrapper + } + } +} + +/** + * A Swift base-protocol representing the BarcodeScanner HybridObject. + * Implement this protocol to create Swift-based instances of BarcodeScanner. + * ```swift + * class HybridBarcodeScanner : HybridBarcodeScannerSpec { + * // ... + * } + * ``` + */ +public typealias HybridBarcodeScannerSpec = HybridBarcodeScannerSpec_protocol & HybridBarcodeScannerSpec_base diff --git a/nitrogen/generated/ios/swift/HybridBarcodeScannerSpec_cxx.swift b/nitrogen/generated/ios/swift/HybridBarcodeScannerSpec_cxx.swift new file mode 100644 index 0000000..cfc0080 --- /dev/null +++ b/nitrogen/generated/ios/swift/HybridBarcodeScannerSpec_cxx.swift @@ -0,0 +1,162 @@ +/// +/// HybridBarcodeScannerSpec_cxx.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules +import VisionCamera + +/** + * A class implementation that bridges HybridBarcodeScannerSpec over to C++. + * In C++, we cannot use Swift protocols - so we need to wrap it in a class to make it strongly defined. + * + * Also, some Swift types need to be bridged with special handling: + * - Enums need to be wrapped in Structs, otherwise they cannot be accessed bi-directionally (Swift bug: https://github.com/swiftlang/swift/issues/75330) + * - Other HybridObjects need to be wrapped/unwrapped from the Swift TCxx wrapper + * - Throwing methods need to be wrapped with a Result type, as exceptions cannot be propagated to C++ + */ +open class HybridBarcodeScannerSpec_cxx { + /** + * The Swift <> C++ bridge's namespace (`margelo::nitro::visioncameramlkit::bridge::swift`) + * from `VisionCameraMLKit-Swift-Cxx-Bridge.hpp`. + * This contains specialized C++ templates, and C++ helper functions that can be accessed from Swift. + */ + public typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Holds an instance of the `HybridBarcodeScannerSpec` Swift protocol. + */ + private var __implementation: any HybridBarcodeScannerSpec + + /** + * Holds a weak pointer to the C++ class that wraps the Swift class. + */ + private var __cxxPart: bridge.std__weak_ptr_HybridBarcodeScannerSpec_ + + /** + * Create a new `HybridBarcodeScannerSpec_cxx` that wraps the given `HybridBarcodeScannerSpec`. + * All properties and methods bridge to C++ types. + */ + public init(_ implementation: any HybridBarcodeScannerSpec) { + self.__implementation = implementation + self.__cxxPart = .init() + /* no base class */ + } + + /** + * Get the actual `HybridBarcodeScannerSpec` instance this class wraps. + */ + @inline(__always) + public func getHybridBarcodeScannerSpec() -> any HybridBarcodeScannerSpec { + return __implementation + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `HybridBarcodeScannerSpec_cxx`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + public class func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> HybridBarcodeScannerSpec_cxx { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } + + /** + * Gets (or creates) the C++ part of this Hybrid Object. + * The C++ part is a `std::shared_ptr`. + */ + public func getCxxPart() -> bridge.std__shared_ptr_HybridBarcodeScannerSpec_ { + let cachedCxxPart = self.__cxxPart.lock() + if Bool(fromCxx: cachedCxxPart) { + return cachedCxxPart + } else { + let newCxxPart = bridge.create_std__shared_ptr_HybridBarcodeScannerSpec_(self.toUnsafe()) + __cxxPart = bridge.weakify_std__shared_ptr_HybridBarcodeScannerSpec_(newCxxPart) + return newCxxPart + } + } + + + + /** + * Get the memory size of the Swift class (plus size of any other allocations) + * so the JS VM can properly track it and garbage-collect the JS object if needed. + */ + @inline(__always) + public var memorySize: Int { + return MemoryHelper.getSizeOf(self.__implementation) + self.__implementation.memorySize + } + + /** + * Compares this object with the given [other] object for reference equality. + */ + @inline(__always) + public func equals(other: HybridBarcodeScannerSpec_cxx) -> Bool { + return self.__implementation === other.__implementation + } + + /** + * Call dispose() on the Swift class. + * This _may_ be called manually from JS. + */ + @inline(__always) + public func dispose() { + self.__implementation.dispose() + } + + /** + * Call toString() on the Swift class. + */ + @inline(__always) + public func toString() -> String { + return self.__implementation.toString() + } + + // Properties + + + // Methods + @inline(__always) + public final func recognize(frame: bridge.std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_, args: bridge.std__optional_MLKitBaseArguments_) -> bridge.Result_BarcodeScannerResult_ { + do { + let __result = try self.__implementation.recognize(frame: { () -> any HybridFrameSpec in + let __unsafePointer = bridge.get_std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_(frame) + let __instance = HybridFrameSpec_cxx.fromUnsafe(__unsafePointer) + return __instance.getHybridFrameSpec() + }(), args: args.value) + let __resultCpp = __result + return bridge.create_Result_BarcodeScannerResult_(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_BarcodeScannerResult_(__exceptionPtr) + } + } + + @inline(__always) + public final func recognizeImage(uri: std.string) -> bridge.Result_std__shared_ptr_Promise_BarcodeScannerResult___ { + do { + let __result = try self.__implementation.recognizeImage(uri: String(uri)) + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_BarcodeScannerResult__ in + let __promise = bridge.create_std__shared_ptr_Promise_BarcodeScannerResult__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_BarcodeScannerResult__(__promise) + __result + .then({ __result in __promiseHolder.resolve(__result) }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_BarcodeScannerResult___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_BarcodeScannerResult___(__exceptionPtr) + } + } +} diff --git a/nitrogen/generated/ios/swift/HybridTextRecognizerSpec.swift b/nitrogen/generated/ios/swift/HybridTextRecognizerSpec.swift new file mode 100644 index 0000000..8dff317 --- /dev/null +++ b/nitrogen/generated/ios/swift/HybridTextRecognizerSpec.swift @@ -0,0 +1,57 @@ +/// +/// HybridTextRecognizerSpec.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import VisionCamera +import NitroModules + +/// See ``HybridTextRecognizerSpec`` +public protocol HybridTextRecognizerSpec_protocol: HybridObject { + // Properties + + + // Methods + func recognize(frame: (any HybridFrameSpec), args: MLKitBaseArguments?) throws -> TextRecognitionResult + func recognizeImage(uri: String) throws -> Promise +} + +public extension HybridTextRecognizerSpec_protocol { + /// Default implementation of ``HybridObject.toString`` + func toString() -> String { + return "[HybridObject TextRecognizer]" + } +} + +/// See ``HybridTextRecognizerSpec`` +open class HybridTextRecognizerSpec_base { + private weak var cxxWrapper: HybridTextRecognizerSpec_cxx? = nil + public init() { } + public func getCxxWrapper() -> HybridTextRecognizerSpec_cxx { + #if DEBUG + guard self is any HybridTextRecognizerSpec else { + fatalError("`self` is not a `HybridTextRecognizerSpec`! Did you accidentally inherit from `HybridTextRecognizerSpec_base` instead of `HybridTextRecognizerSpec`?") + } + #endif + if let cxxWrapper = self.cxxWrapper { + return cxxWrapper + } else { + let cxxWrapper = HybridTextRecognizerSpec_cxx(self as! any HybridTextRecognizerSpec) + self.cxxWrapper = cxxWrapper + return cxxWrapper + } + } +} + +/** + * A Swift base-protocol representing the TextRecognizer HybridObject. + * Implement this protocol to create Swift-based instances of TextRecognizer. + * ```swift + * class HybridTextRecognizer : HybridTextRecognizerSpec { + * // ... + * } + * ``` + */ +public typealias HybridTextRecognizerSpec = HybridTextRecognizerSpec_protocol & HybridTextRecognizerSpec_base diff --git a/nitrogen/generated/ios/swift/HybridTextRecognizerSpec_cxx.swift b/nitrogen/generated/ios/swift/HybridTextRecognizerSpec_cxx.swift new file mode 100644 index 0000000..013329e --- /dev/null +++ b/nitrogen/generated/ios/swift/HybridTextRecognizerSpec_cxx.swift @@ -0,0 +1,162 @@ +/// +/// HybridTextRecognizerSpec_cxx.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules +import VisionCamera + +/** + * A class implementation that bridges HybridTextRecognizerSpec over to C++. + * In C++, we cannot use Swift protocols - so we need to wrap it in a class to make it strongly defined. + * + * Also, some Swift types need to be bridged with special handling: + * - Enums need to be wrapped in Structs, otherwise they cannot be accessed bi-directionally (Swift bug: https://github.com/swiftlang/swift/issues/75330) + * - Other HybridObjects need to be wrapped/unwrapped from the Swift TCxx wrapper + * - Throwing methods need to be wrapped with a Result type, as exceptions cannot be propagated to C++ + */ +open class HybridTextRecognizerSpec_cxx { + /** + * The Swift <> C++ bridge's namespace (`margelo::nitro::visioncameramlkit::bridge::swift`) + * from `VisionCameraMLKit-Swift-Cxx-Bridge.hpp`. + * This contains specialized C++ templates, and C++ helper functions that can be accessed from Swift. + */ + public typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Holds an instance of the `HybridTextRecognizerSpec` Swift protocol. + */ + private var __implementation: any HybridTextRecognizerSpec + + /** + * Holds a weak pointer to the C++ class that wraps the Swift class. + */ + private var __cxxPart: bridge.std__weak_ptr_HybridTextRecognizerSpec_ + + /** + * Create a new `HybridTextRecognizerSpec_cxx` that wraps the given `HybridTextRecognizerSpec`. + * All properties and methods bridge to C++ types. + */ + public init(_ implementation: any HybridTextRecognizerSpec) { + self.__implementation = implementation + self.__cxxPart = .init() + /* no base class */ + } + + /** + * Get the actual `HybridTextRecognizerSpec` instance this class wraps. + */ + @inline(__always) + public func getHybridTextRecognizerSpec() -> any HybridTextRecognizerSpec { + return __implementation + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `HybridTextRecognizerSpec_cxx`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + public class func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> HybridTextRecognizerSpec_cxx { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } + + /** + * Gets (or creates) the C++ part of this Hybrid Object. + * The C++ part is a `std::shared_ptr`. + */ + public func getCxxPart() -> bridge.std__shared_ptr_HybridTextRecognizerSpec_ { + let cachedCxxPart = self.__cxxPart.lock() + if Bool(fromCxx: cachedCxxPart) { + return cachedCxxPart + } else { + let newCxxPart = bridge.create_std__shared_ptr_HybridTextRecognizerSpec_(self.toUnsafe()) + __cxxPart = bridge.weakify_std__shared_ptr_HybridTextRecognizerSpec_(newCxxPart) + return newCxxPart + } + } + + + + /** + * Get the memory size of the Swift class (plus size of any other allocations) + * so the JS VM can properly track it and garbage-collect the JS object if needed. + */ + @inline(__always) + public var memorySize: Int { + return MemoryHelper.getSizeOf(self.__implementation) + self.__implementation.memorySize + } + + /** + * Compares this object with the given [other] object for reference equality. + */ + @inline(__always) + public func equals(other: HybridTextRecognizerSpec_cxx) -> Bool { + return self.__implementation === other.__implementation + } + + /** + * Call dispose() on the Swift class. + * This _may_ be called manually from JS. + */ + @inline(__always) + public func dispose() { + self.__implementation.dispose() + } + + /** + * Call toString() on the Swift class. + */ + @inline(__always) + public func toString() -> String { + return self.__implementation.toString() + } + + // Properties + + + // Methods + @inline(__always) + public final func recognize(frame: bridge.std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_, args: bridge.std__optional_MLKitBaseArguments_) -> bridge.Result_TextRecognitionResult_ { + do { + let __result = try self.__implementation.recognize(frame: { () -> any HybridFrameSpec in + let __unsafePointer = bridge.get_std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_(frame) + let __instance = HybridFrameSpec_cxx.fromUnsafe(__unsafePointer) + return __instance.getHybridFrameSpec() + }(), args: args.value) + let __resultCpp = __result + return bridge.create_Result_TextRecognitionResult_(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_TextRecognitionResult_(__exceptionPtr) + } + } + + @inline(__always) + public final func recognizeImage(uri: std.string) -> bridge.Result_std__shared_ptr_Promise_TextRecognitionResult___ { + do { + let __result = try self.__implementation.recognizeImage(uri: String(uri)) + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_TextRecognitionResult__ in + let __promise = bridge.create_std__shared_ptr_Promise_TextRecognitionResult__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_TextRecognitionResult__(__promise) + __result + .then({ __result in __promiseHolder.resolve(__result) }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_TextRecognitionResult___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_TextRecognitionResult___(__exceptionPtr) + } + } +} diff --git a/nitrogen/generated/ios/swift/HybridVisionCameraMLKitSpec.swift b/nitrogen/generated/ios/swift/HybridVisionCameraMLKitSpec.swift new file mode 100644 index 0000000..0c0ed11 --- /dev/null +++ b/nitrogen/generated/ios/swift/HybridVisionCameraMLKitSpec.swift @@ -0,0 +1,57 @@ +/// +/// HybridVisionCameraMLKitSpec.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/// See ``HybridVisionCameraMLKitSpec`` +public protocol HybridVisionCameraMLKitSpec_protocol: HybridObject { + // Properties + + + // Methods + func isFeatureAvailable(feature: MLKitFeature) throws -> Bool + func createTextRecognizer(options: TextRecognizerOptions) throws -> (any HybridTextRecognizerSpec) + func createBarcodeScanner(options: BarcodeScannerOptions) throws -> (any HybridBarcodeScannerSpec) +} + +public extension HybridVisionCameraMLKitSpec_protocol { + /// Default implementation of ``HybridObject.toString`` + func toString() -> String { + return "[HybridObject VisionCameraMLKit]" + } +} + +/// See ``HybridVisionCameraMLKitSpec`` +open class HybridVisionCameraMLKitSpec_base { + private weak var cxxWrapper: HybridVisionCameraMLKitSpec_cxx? = nil + public init() { } + public func getCxxWrapper() -> HybridVisionCameraMLKitSpec_cxx { + #if DEBUG + guard self is any HybridVisionCameraMLKitSpec else { + fatalError("`self` is not a `HybridVisionCameraMLKitSpec`! Did you accidentally inherit from `HybridVisionCameraMLKitSpec_base` instead of `HybridVisionCameraMLKitSpec`?") + } + #endif + if let cxxWrapper = self.cxxWrapper { + return cxxWrapper + } else { + let cxxWrapper = HybridVisionCameraMLKitSpec_cxx(self as! any HybridVisionCameraMLKitSpec) + self.cxxWrapper = cxxWrapper + return cxxWrapper + } + } +} + +/** + * A Swift base-protocol representing the VisionCameraMLKit HybridObject. + * Implement this protocol to create Swift-based instances of VisionCameraMLKit. + * ```swift + * class HybridVisionCameraMLKit : HybridVisionCameraMLKitSpec { + * // ... + * } + * ``` + */ +public typealias HybridVisionCameraMLKitSpec = HybridVisionCameraMLKitSpec_protocol & HybridVisionCameraMLKitSpec_base diff --git a/nitrogen/generated/ios/swift/HybridVisionCameraMLKitSpec_cxx.swift b/nitrogen/generated/ios/swift/HybridVisionCameraMLKitSpec_cxx.swift new file mode 100644 index 0000000..713de6d --- /dev/null +++ b/nitrogen/generated/ios/swift/HybridVisionCameraMLKitSpec_cxx.swift @@ -0,0 +1,168 @@ +/// +/// HybridVisionCameraMLKitSpec_cxx.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * A class implementation that bridges HybridVisionCameraMLKitSpec over to C++. + * In C++, we cannot use Swift protocols - so we need to wrap it in a class to make it strongly defined. + * + * Also, some Swift types need to be bridged with special handling: + * - Enums need to be wrapped in Structs, otherwise they cannot be accessed bi-directionally (Swift bug: https://github.com/swiftlang/swift/issues/75330) + * - Other HybridObjects need to be wrapped/unwrapped from the Swift TCxx wrapper + * - Throwing methods need to be wrapped with a Result type, as exceptions cannot be propagated to C++ + */ +open class HybridVisionCameraMLKitSpec_cxx { + /** + * The Swift <> C++ bridge's namespace (`margelo::nitro::visioncameramlkit::bridge::swift`) + * from `VisionCameraMLKit-Swift-Cxx-Bridge.hpp`. + * This contains specialized C++ templates, and C++ helper functions that can be accessed from Swift. + */ + public typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Holds an instance of the `HybridVisionCameraMLKitSpec` Swift protocol. + */ + private var __implementation: any HybridVisionCameraMLKitSpec + + /** + * Holds a weak pointer to the C++ class that wraps the Swift class. + */ + private var __cxxPart: bridge.std__weak_ptr_HybridVisionCameraMLKitSpec_ + + /** + * Create a new `HybridVisionCameraMLKitSpec_cxx` that wraps the given `HybridVisionCameraMLKitSpec`. + * All properties and methods bridge to C++ types. + */ + public init(_ implementation: any HybridVisionCameraMLKitSpec) { + self.__implementation = implementation + self.__cxxPart = .init() + /* no base class */ + } + + /** + * Get the actual `HybridVisionCameraMLKitSpec` instance this class wraps. + */ + @inline(__always) + public func getHybridVisionCameraMLKitSpec() -> any HybridVisionCameraMLKitSpec { + return __implementation + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `HybridVisionCameraMLKitSpec_cxx`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + public class func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> HybridVisionCameraMLKitSpec_cxx { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } + + /** + * Gets (or creates) the C++ part of this Hybrid Object. + * The C++ part is a `std::shared_ptr`. + */ + public func getCxxPart() -> bridge.std__shared_ptr_HybridVisionCameraMLKitSpec_ { + let cachedCxxPart = self.__cxxPart.lock() + if Bool(fromCxx: cachedCxxPart) { + return cachedCxxPart + } else { + let newCxxPart = bridge.create_std__shared_ptr_HybridVisionCameraMLKitSpec_(self.toUnsafe()) + __cxxPart = bridge.weakify_std__shared_ptr_HybridVisionCameraMLKitSpec_(newCxxPart) + return newCxxPart + } + } + + + + /** + * Get the memory size of the Swift class (plus size of any other allocations) + * so the JS VM can properly track it and garbage-collect the JS object if needed. + */ + @inline(__always) + public var memorySize: Int { + return MemoryHelper.getSizeOf(self.__implementation) + self.__implementation.memorySize + } + + /** + * Compares this object with the given [other] object for reference equality. + */ + @inline(__always) + public func equals(other: HybridVisionCameraMLKitSpec_cxx) -> Bool { + return self.__implementation === other.__implementation + } + + /** + * Call dispose() on the Swift class. + * This _may_ be called manually from JS. + */ + @inline(__always) + public func dispose() { + self.__implementation.dispose() + } + + /** + * Call toString() on the Swift class. + */ + @inline(__always) + public func toString() -> String { + return self.__implementation.toString() + } + + // Properties + + + // Methods + @inline(__always) + public final func isFeatureAvailable(feature: Int32) -> bridge.Result_bool_ { + do { + let __result = try self.__implementation.isFeatureAvailable(feature: margelo.nitro.visioncameramlkit.MLKitFeature(rawValue: feature)!) + let __resultCpp = __result + return bridge.create_Result_bool_(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_bool_(__exceptionPtr) + } + } + + @inline(__always) + public final func createTextRecognizer(options: TextRecognizerOptions) -> bridge.Result_std__shared_ptr_HybridTextRecognizerSpec__ { + do { + let __result = try self.__implementation.createTextRecognizer(options: options) + let __resultCpp = { () -> bridge.std__shared_ptr_HybridTextRecognizerSpec_ in + let __cxxWrapped = __result.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }() + return bridge.create_Result_std__shared_ptr_HybridTextRecognizerSpec__(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_HybridTextRecognizerSpec__(__exceptionPtr) + } + } + + @inline(__always) + public final func createBarcodeScanner(options: BarcodeScannerOptions) -> bridge.Result_std__shared_ptr_HybridBarcodeScannerSpec__ { + do { + let __result = try self.__implementation.createBarcodeScanner(options: options) + let __resultCpp = { () -> bridge.std__shared_ptr_HybridBarcodeScannerSpec_ in + let __cxxWrapped = __result.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }() + return bridge.create_Result_std__shared_ptr_HybridBarcodeScannerSpec__(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_HybridBarcodeScannerSpec__(__exceptionPtr) + } + } +} diff --git a/nitrogen/generated/ios/swift/MLKitBaseArguments.swift b/nitrogen/generated/ios/swift/MLKitBaseArguments.swift new file mode 100644 index 0000000..6012fd4 --- /dev/null +++ b/nitrogen/generated/ios/swift/MLKitBaseArguments.swift @@ -0,0 +1,35 @@ +/// +/// MLKitBaseArguments.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `MLKitBaseArguments`, backed by a C++ struct. + */ +public typealias MLKitBaseArguments = margelo.nitro.visioncameramlkit.MLKitBaseArguments + +public extension MLKitBaseArguments { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `MLKitBaseArguments`. + */ + init(outputOrientation: Orientation?) { + self.init({ () -> bridge.std__optional_Orientation_ in + if let __unwrappedValue = outputOrientation { + return bridge.create_std__optional_Orientation_(__unwrappedValue) + } else { + return .init() + } + }()) + } + + @inline(__always) + var outputOrientation: Orientation? { + return self.__outputOrientation.value + } +} diff --git a/nitrogen/generated/ios/swift/MLKitFeature.swift b/nitrogen/generated/ios/swift/MLKitFeature.swift new file mode 100644 index 0000000..8ce63b7 --- /dev/null +++ b/nitrogen/generated/ios/swift/MLKitFeature.swift @@ -0,0 +1,76 @@ +/// +/// MLKitFeature.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +/** + * Represents the JS union `MLKitFeature`, backed by a C++ enum. + */ +public typealias MLKitFeature = margelo.nitro.visioncameramlkit.MLKitFeature + +public extension MLKitFeature { + /** + * Get a MLKitFeature for the given String value, or + * return `nil` if the given value was invalid/unknown. + */ + init?(fromString string: String) { + switch string { + case "TextRecognition": + self = .textrecognition + case "FaceDetection": + self = .facedetection + case "FaceMeshDetection": + self = .facemeshdetection + case "PoseDetection": + self = .posedetection + case "SelfieSegmentation": + self = .selfiesegmentation + case "SubjectSegmentation": + self = .subjectsegmentation + case "DocumentScanner": + self = .documentscanner + case "BarcodeScanning": + self = .barcodescanning + case "ImageLabeling": + self = .imagelabeling + case "ObjectDetection": + self = .objectdetection + case "DigitalInkRecognition": + self = .digitalinkrecognition + default: + return nil + } + } + + /** + * Get the String value this MLKitFeature represents. + */ + var stringValue: String { + switch self { + case .textrecognition: + return "TextRecognition" + case .facedetection: + return "FaceDetection" + case .facemeshdetection: + return "FaceMeshDetection" + case .posedetection: + return "PoseDetection" + case .selfiesegmentation: + return "SelfieSegmentation" + case .subjectsegmentation: + return "SubjectSegmentation" + case .documentscanner: + return "DocumentScanner" + case .barcodescanning: + return "BarcodeScanning" + case .imagelabeling: + return "ImageLabeling" + case .objectdetection: + return "ObjectDetection" + case .digitalinkrecognition: + return "DigitalInkRecognition" + } + } +} diff --git a/nitrogen/generated/ios/swift/Orientation.swift b/nitrogen/generated/ios/swift/Orientation.swift new file mode 100644 index 0000000..338dcb4 --- /dev/null +++ b/nitrogen/generated/ios/swift/Orientation.swift @@ -0,0 +1,48 @@ +/// +/// Orientation.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +/** + * Represents the JS union `Orientation`, backed by a C++ enum. + */ +public typealias Orientation = margelo.nitro.visioncameramlkit.Orientation + +public extension Orientation { + /** + * Get a Orientation for the given String value, or + * return `nil` if the given value was invalid/unknown. + */ + init?(fromString string: String) { + switch string { + case "portrait": + self = .portrait + case "portrait-upside-down": + self = .portraitUpsideDown + case "landscape-left": + self = .landscapeLeft + case "landscape-right": + self = .landscapeRight + default: + return nil + } + } + + /** + * Get the String value this Orientation represents. + */ + var stringValue: String { + switch self { + case .portrait: + return "portrait" + case .portraitUpsideDown: + return "portrait-upside-down" + case .landscapeLeft: + return "landscape-left" + case .landscapeRight: + return "landscape-right" + } + } +} diff --git a/nitrogen/generated/ios/swift/RegionOfInterest.swift b/nitrogen/generated/ios/swift/RegionOfInterest.swift new file mode 100644 index 0000000..67af6d1 --- /dev/null +++ b/nitrogen/generated/ios/swift/RegionOfInterest.swift @@ -0,0 +1,55 @@ +/// +/// RegionOfInterest.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `RegionOfInterest`, backed by a C++ struct. + */ +public typealias RegionOfInterest = margelo.nitro.visioncameramlkit.RegionOfInterest + +public extension RegionOfInterest { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `RegionOfInterest`. + */ + init(x: Double, y: Double, width: Double, height: Double, unit: RegionOfInterestUnit?) { + self.init(x, y, width, height, { () -> bridge.std__optional_RegionOfInterestUnit_ in + if let __unwrappedValue = unit { + return bridge.create_std__optional_RegionOfInterestUnit_(__unwrappedValue) + } else { + return .init() + } + }()) + } + + @inline(__always) + var x: Double { + return self.__x + } + + @inline(__always) + var y: Double { + return self.__y + } + + @inline(__always) + var width: Double { + return self.__width + } + + @inline(__always) + var height: Double { + return self.__height + } + + @inline(__always) + var unit: RegionOfInterestUnit? { + return self.__unit.value + } +} diff --git a/nitrogen/generated/ios/swift/RegionOfInterestUnit.swift b/nitrogen/generated/ios/swift/RegionOfInterestUnit.swift new file mode 100644 index 0000000..1a4e5ac --- /dev/null +++ b/nitrogen/generated/ios/swift/RegionOfInterestUnit.swift @@ -0,0 +1,40 @@ +/// +/// RegionOfInterestUnit.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +/** + * Represents the JS union `RegionOfInterestUnit`, backed by a C++ enum. + */ +public typealias RegionOfInterestUnit = margelo.nitro.visioncameramlkit.RegionOfInterestUnit + +public extension RegionOfInterestUnit { + /** + * Get a RegionOfInterestUnit for the given String value, or + * return `nil` if the given value was invalid/unknown. + */ + init?(fromString string: String) { + switch string { + case "normalized": + self = .normalized + case "pixel": + self = .pixel + default: + return nil + } + } + + /** + * Get the String value this RegionOfInterestUnit represents. + */ + var stringValue: String { + switch self { + case .normalized: + return "normalized" + case .pixel: + return "pixel" + } + } +} diff --git a/nitrogen/generated/ios/swift/TextBlock.swift b/nitrogen/generated/ios/swift/TextBlock.swift new file mode 100644 index 0000000..0c0cb43 --- /dev/null +++ b/nitrogen/generated/ios/swift/TextBlock.swift @@ -0,0 +1,67 @@ +/// +/// TextBlock.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `TextBlock`, backed by a C++ struct. + */ +public typealias TextBlock = margelo.nitro.visioncameramlkit.TextBlock + +public extension TextBlock { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `TextBlock`. + */ + init(bounds: BoundingBox, corners: [Corner], languages: [String], text: String, lines: [TextLine]) { + self.init(bounds, { () -> bridge.std__vector_Corner_ in + var __vector = bridge.create_std__vector_Corner_(corners.count) + for __item in corners { + __vector.push_back(__item) + } + return __vector + }(), { () -> bridge.std__vector_std__string_ in + var __vector = bridge.create_std__vector_std__string_(languages.count) + for __item in languages { + __vector.push_back(std.string(__item)) + } + return __vector + }(), std.string(text), { () -> bridge.std__vector_TextLine_ in + var __vector = bridge.create_std__vector_TextLine_(lines.count) + for __item in lines { + __vector.push_back(__item) + } + return __vector + }()) + } + + @inline(__always) + var bounds: BoundingBox { + return self.__bounds + } + + @inline(__always) + var corners: [Corner] { + return self.__corners.map({ __item in __item }) + } + + @inline(__always) + var languages: [String] { + return self.__languages.map({ __item in String(__item) }) + } + + @inline(__always) + var text: String { + return String(self.__text) + } + + @inline(__always) + var lines: [TextLine] { + return self.__lines.map({ __item in __item }) + } +} diff --git a/nitrogen/generated/ios/swift/TextElement.swift b/nitrogen/generated/ios/swift/TextElement.swift new file mode 100644 index 0000000..c91f560 --- /dev/null +++ b/nitrogen/generated/ios/swift/TextElement.swift @@ -0,0 +1,141 @@ +/// +/// TextElement.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `TextElement`, backed by a C++ struct. + */ +public typealias TextElement = margelo.nitro.visioncameramlkit.TextElement + +public extension TextElement { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `TextElement`. + */ + init(bounds: BoundingBox, corners: [Corner], confidence: Variant_NullType_Double?, angle: Variant_NullType_Double?, languages: [String], text: String, symbols: [TextSymbol]) { + self.init(bounds, { () -> bridge.std__vector_Corner_ in + var __vector = bridge.create_std__vector_Corner_(corners.count) + for __item in corners { + __vector.push_back(__item) + } + return __vector + }(), { () -> bridge.std__optional_std__variant_nitro__NullType__double__ in + if let __unwrappedValue = confidence { + return bridge.create_std__optional_std__variant_nitro__NullType__double__({ () -> bridge.std__variant_nitro__NullType__double_ in + switch __unwrappedValue { + case .first(let __value): + return bridge.create_std__variant_nitro__NullType__double_(margelo.nitro.NullType.null) + case .second(let __value): + return bridge.create_std__variant_nitro__NullType__double_(__value) + } + }().variant) + } else { + return .init() + } + }(), { () -> bridge.std__optional_std__variant_nitro__NullType__double__ in + if let __unwrappedValue = angle { + return bridge.create_std__optional_std__variant_nitro__NullType__double__({ () -> bridge.std__variant_nitro__NullType__double_ in + switch __unwrappedValue { + case .first(let __value): + return bridge.create_std__variant_nitro__NullType__double_(margelo.nitro.NullType.null) + case .second(let __value): + return bridge.create_std__variant_nitro__NullType__double_(__value) + } + }().variant) + } else { + return .init() + } + }(), { () -> bridge.std__vector_std__string_ in + var __vector = bridge.create_std__vector_std__string_(languages.count) + for __item in languages { + __vector.push_back(std.string(__item)) + } + return __vector + }(), std.string(text), { () -> bridge.std__vector_TextSymbol_ in + var __vector = bridge.create_std__vector_TextSymbol_(symbols.count) + for __item in symbols { + __vector.push_back(__item) + } + return __vector + }()) + } + + @inline(__always) + var bounds: BoundingBox { + return self.__bounds + } + + @inline(__always) + var corners: [Corner] { + return self.__corners.map({ __item in __item }) + } + + @inline(__always) + var confidence: Variant_NullType_Double? { + return { () -> Variant_NullType_Double? in + if bridge.has_value_std__optional_std__variant_nitro__NullType__double__(self.__confidence) { + let __unwrapped = bridge.get_std__optional_std__variant_nitro__NullType__double__(self.__confidence) + return { () -> Variant_NullType_Double in + let __variant = bridge.std__variant_nitro__NullType__double_(__unwrapped) + switch __variant.index() { + case 0: + let __actual = __variant.get_0() + return .first(NullType.null) + case 1: + let __actual = __variant.get_1() + return .second(__actual) + default: + fatalError("Variant can never have index \(__variant.index())!") + } + }() + } else { + return nil + } + }() + } + + @inline(__always) + var angle: Variant_NullType_Double? { + return { () -> Variant_NullType_Double? in + if bridge.has_value_std__optional_std__variant_nitro__NullType__double__(self.__angle) { + let __unwrapped = bridge.get_std__optional_std__variant_nitro__NullType__double__(self.__angle) + return { () -> Variant_NullType_Double in + let __variant = bridge.std__variant_nitro__NullType__double_(__unwrapped) + switch __variant.index() { + case 0: + let __actual = __variant.get_0() + return .first(NullType.null) + case 1: + let __actual = __variant.get_1() + return .second(__actual) + default: + fatalError("Variant can never have index \(__variant.index())!") + } + }() + } else { + return nil + } + }() + } + + @inline(__always) + var languages: [String] { + return self.__languages.map({ __item in String(__item) }) + } + + @inline(__always) + var text: String { + return String(self.__text) + } + + @inline(__always) + var symbols: [TextSymbol] { + return self.__symbols.map({ __item in __item }) + } +} diff --git a/nitrogen/generated/ios/swift/TextLine.swift b/nitrogen/generated/ios/swift/TextLine.swift new file mode 100644 index 0000000..3fd2d75 --- /dev/null +++ b/nitrogen/generated/ios/swift/TextLine.swift @@ -0,0 +1,141 @@ +/// +/// TextLine.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `TextLine`, backed by a C++ struct. + */ +public typealias TextLine = margelo.nitro.visioncameramlkit.TextLine + +public extension TextLine { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `TextLine`. + */ + init(bounds: BoundingBox, corners: [Corner], confidence: Variant_NullType_Double?, angle: Variant_NullType_Double?, languages: [String], text: String, elements: [TextElement]) { + self.init(bounds, { () -> bridge.std__vector_Corner_ in + var __vector = bridge.create_std__vector_Corner_(corners.count) + for __item in corners { + __vector.push_back(__item) + } + return __vector + }(), { () -> bridge.std__optional_std__variant_nitro__NullType__double__ in + if let __unwrappedValue = confidence { + return bridge.create_std__optional_std__variant_nitro__NullType__double__({ () -> bridge.std__variant_nitro__NullType__double_ in + switch __unwrappedValue { + case .first(let __value): + return bridge.create_std__variant_nitro__NullType__double_(margelo.nitro.NullType.null) + case .second(let __value): + return bridge.create_std__variant_nitro__NullType__double_(__value) + } + }().variant) + } else { + return .init() + } + }(), { () -> bridge.std__optional_std__variant_nitro__NullType__double__ in + if let __unwrappedValue = angle { + return bridge.create_std__optional_std__variant_nitro__NullType__double__({ () -> bridge.std__variant_nitro__NullType__double_ in + switch __unwrappedValue { + case .first(let __value): + return bridge.create_std__variant_nitro__NullType__double_(margelo.nitro.NullType.null) + case .second(let __value): + return bridge.create_std__variant_nitro__NullType__double_(__value) + } + }().variant) + } else { + return .init() + } + }(), { () -> bridge.std__vector_std__string_ in + var __vector = bridge.create_std__vector_std__string_(languages.count) + for __item in languages { + __vector.push_back(std.string(__item)) + } + return __vector + }(), std.string(text), { () -> bridge.std__vector_TextElement_ in + var __vector = bridge.create_std__vector_TextElement_(elements.count) + for __item in elements { + __vector.push_back(__item) + } + return __vector + }()) + } + + @inline(__always) + var bounds: BoundingBox { + return self.__bounds + } + + @inline(__always) + var corners: [Corner] { + return self.__corners.map({ __item in __item }) + } + + @inline(__always) + var confidence: Variant_NullType_Double? { + return { () -> Variant_NullType_Double? in + if bridge.has_value_std__optional_std__variant_nitro__NullType__double__(self.__confidence) { + let __unwrapped = bridge.get_std__optional_std__variant_nitro__NullType__double__(self.__confidence) + return { () -> Variant_NullType_Double in + let __variant = bridge.std__variant_nitro__NullType__double_(__unwrapped) + switch __variant.index() { + case 0: + let __actual = __variant.get_0() + return .first(NullType.null) + case 1: + let __actual = __variant.get_1() + return .second(__actual) + default: + fatalError("Variant can never have index \(__variant.index())!") + } + }() + } else { + return nil + } + }() + } + + @inline(__always) + var angle: Variant_NullType_Double? { + return { () -> Variant_NullType_Double? in + if bridge.has_value_std__optional_std__variant_nitro__NullType__double__(self.__angle) { + let __unwrapped = bridge.get_std__optional_std__variant_nitro__NullType__double__(self.__angle) + return { () -> Variant_NullType_Double in + let __variant = bridge.std__variant_nitro__NullType__double_(__unwrapped) + switch __variant.index() { + case 0: + let __actual = __variant.get_0() + return .first(NullType.null) + case 1: + let __actual = __variant.get_1() + return .second(__actual) + default: + fatalError("Variant can never have index \(__variant.index())!") + } + }() + } else { + return nil + } + }() + } + + @inline(__always) + var languages: [String] { + return self.__languages.map({ __item in String(__item) }) + } + + @inline(__always) + var text: String { + return String(self.__text) + } + + @inline(__always) + var elements: [TextElement] { + return self.__elements.map({ __item in __item }) + } +} diff --git a/nitrogen/generated/ios/swift/TextRecognitionLanguage.swift b/nitrogen/generated/ios/swift/TextRecognitionLanguage.swift new file mode 100644 index 0000000..1831fa7 --- /dev/null +++ b/nitrogen/generated/ios/swift/TextRecognitionLanguage.swift @@ -0,0 +1,52 @@ +/// +/// TextRecognitionLanguage.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +/** + * Represents the JS union `TextRecognitionLanguage`, backed by a C++ enum. + */ +public typealias TextRecognitionLanguage = margelo.nitro.visioncameramlkit.TextRecognitionLanguage + +public extension TextRecognitionLanguage { + /** + * Get a TextRecognitionLanguage for the given String value, or + * return `nil` if the given value was invalid/unknown. + */ + init?(fromString string: String) { + switch string { + case "LATIN": + self = .latin + case "CHINESE": + self = .chinese + case "DEVANAGARI": + self = .devanagari + case "JAPANESE": + self = .japanese + case "KOREAN": + self = .korean + default: + return nil + } + } + + /** + * Get the String value this TextRecognitionLanguage represents. + */ + var stringValue: String { + switch self { + case .latin: + return "LATIN" + case .chinese: + return "CHINESE" + case .devanagari: + return "DEVANAGARI" + case .japanese: + return "JAPANESE" + case .korean: + return "KOREAN" + } + } +} diff --git a/nitrogen/generated/ios/swift/TextRecognitionResult.swift b/nitrogen/generated/ios/swift/TextRecognitionResult.swift new file mode 100644 index 0000000..40ff4a7 --- /dev/null +++ b/nitrogen/generated/ios/swift/TextRecognitionResult.swift @@ -0,0 +1,40 @@ +/// +/// TextRecognitionResult.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `TextRecognitionResult`, backed by a C++ struct. + */ +public typealias TextRecognitionResult = margelo.nitro.visioncameramlkit.TextRecognitionResult + +public extension TextRecognitionResult { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `TextRecognitionResult`. + */ + init(text: String, blocks: [TextBlock]) { + self.init(std.string(text), { () -> bridge.std__vector_TextBlock_ in + var __vector = bridge.create_std__vector_TextBlock_(blocks.count) + for __item in blocks { + __vector.push_back(__item) + } + return __vector + }()) + } + + @inline(__always) + var text: String { + return String(self.__text) + } + + @inline(__always) + var blocks: [TextBlock] { + return self.__blocks.map({ __item in __item }) + } +} diff --git a/nitrogen/generated/ios/swift/TextRecognizerOptions.swift b/nitrogen/generated/ios/swift/TextRecognizerOptions.swift new file mode 100644 index 0000000..ec3f15a --- /dev/null +++ b/nitrogen/generated/ios/swift/TextRecognizerOptions.swift @@ -0,0 +1,111 @@ +/// +/// TextRecognizerOptions.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `TextRecognizerOptions`, backed by a C++ struct. + */ +public typealias TextRecognizerOptions = margelo.nitro.visioncameramlkit.TextRecognizerOptions + +public extension TextRecognizerOptions { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `TextRecognizerOptions`. + */ + init(language: TextRecognitionLanguage?, scaleFactor: Double?, invertColors: Bool?, frameProcessInterval: Double?, roi: RegionOfInterest?, orientation: Orientation?) { + self.init({ () -> bridge.std__optional_TextRecognitionLanguage_ in + if let __unwrappedValue = language { + return bridge.create_std__optional_TextRecognitionLanguage_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_double_ in + if let __unwrappedValue = scaleFactor { + return bridge.create_std__optional_double_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_bool_ in + if let __unwrappedValue = invertColors { + return bridge.create_std__optional_bool_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_double_ in + if let __unwrappedValue = frameProcessInterval { + return bridge.create_std__optional_double_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_RegionOfInterest_ in + if let __unwrappedValue = roi { + return bridge.create_std__optional_RegionOfInterest_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_Orientation_ in + if let __unwrappedValue = orientation { + return bridge.create_std__optional_Orientation_(__unwrappedValue) + } else { + return .init() + } + }()) + } + + @inline(__always) + var language: TextRecognitionLanguage? { + return self.__language.value + } + + @inline(__always) + var scaleFactor: Double? { + return { () -> Double? in + if bridge.has_value_std__optional_double_(self.__scaleFactor) { + let __unwrapped = bridge.get_std__optional_double_(self.__scaleFactor) + return __unwrapped + } else { + return nil + } + }() + } + + @inline(__always) + var invertColors: Bool? { + return { () -> Bool? in + if bridge.has_value_std__optional_bool_(self.__invertColors) { + let __unwrapped = bridge.get_std__optional_bool_(self.__invertColors) + return __unwrapped + } else { + return nil + } + }() + } + + @inline(__always) + var frameProcessInterval: Double? { + return { () -> Double? in + if bridge.has_value_std__optional_double_(self.__frameProcessInterval) { + let __unwrapped = bridge.get_std__optional_double_(self.__frameProcessInterval) + return __unwrapped + } else { + return nil + } + }() + } + + @inline(__always) + var roi: RegionOfInterest? { + return self.__roi.value + } + + @inline(__always) + var orientation: Orientation? { + return self.__orientation.value + } +} diff --git a/nitrogen/generated/ios/swift/TextSymbol.swift b/nitrogen/generated/ios/swift/TextSymbol.swift new file mode 100644 index 0000000..d1e03f5 --- /dev/null +++ b/nitrogen/generated/ios/swift/TextSymbol.swift @@ -0,0 +1,66 @@ +/// +/// TextSymbol.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `TextSymbol`, backed by a C++ struct. + */ +public typealias TextSymbol = margelo.nitro.visioncameramlkit.TextSymbol + +public extension TextSymbol { + private typealias bridge = margelo.nitro.visioncameramlkit.bridge.swift + + /** + * Create a new instance of `TextSymbol`. + */ + init(bounds: BoundingBox, corners: [Corner], confidence: Double, angle: Double, languages: [String], text: String) { + self.init(bounds, { () -> bridge.std__vector_Corner_ in + var __vector = bridge.create_std__vector_Corner_(corners.count) + for __item in corners { + __vector.push_back(__item) + } + return __vector + }(), confidence, angle, { () -> bridge.std__vector_std__string_ in + var __vector = bridge.create_std__vector_std__string_(languages.count) + for __item in languages { + __vector.push_back(std.string(__item)) + } + return __vector + }(), std.string(text)) + } + + @inline(__always) + var bounds: BoundingBox { + return self.__bounds + } + + @inline(__always) + var corners: [Corner] { + return self.__corners.map({ __item in __item }) + } + + @inline(__always) + var confidence: Double { + return self.__confidence + } + + @inline(__always) + var angle: Double { + return self.__angle + } + + @inline(__always) + var languages: [String] { + return self.__languages.map({ __item in String(__item) }) + } + + @inline(__always) + var text: String { + return String(self.__text) + } +} diff --git a/nitrogen/generated/ios/swift/Variant_NullType_Double.swift b/nitrogen/generated/ios/swift/Variant_NullType_Double.swift new file mode 100644 index 0000000..8918a71 --- /dev/null +++ b/nitrogen/generated/ios/swift/Variant_NullType_Double.swift @@ -0,0 +1,30 @@ +/// +/// Variant_NullType_Double.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * An Swift enum with associated values representing a Variant/Union type. + * JS type: `null | number` + */ +@frozen +public enum Variant_NullType_Double { + case first(NullType) + case second(Double) +} + +public extension Variant_NullType_Double { + func asType(_ type: T.Type = T.self) -> T? { + switch self { + case .first(let value): return value as? T + case .second(let value): return value as? T + } + } + func isType(_ type: T.Type = T.self) -> Bool { + return self.asType(type) != nil + } +} diff --git a/nitrogen/generated/shared/c++/BarcodeFormat.hpp b/nitrogen/generated/shared/c++/BarcodeFormat.hpp new file mode 100644 index 0000000..8b621db --- /dev/null +++ b/nitrogen/generated/shared/c++/BarcodeFormat.hpp @@ -0,0 +1,128 @@ +/// +/// BarcodeFormat.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +namespace margelo::nitro::visioncameramlkit { + + /** + * An enum which can be represented as a JavaScript union (BarcodeFormat). + */ + enum class BarcodeFormat { + UNKNOWN SWIFT_NAME(unknown) = 0, + ALL_FORMATS SWIFT_NAME(allFormats) = 1, + CODE_128 SWIFT_NAME(code128) = 2, + CODE_39 SWIFT_NAME(code39) = 3, + CODE_93 SWIFT_NAME(code93) = 4, + CODABAR SWIFT_NAME(codabar) = 5, + DATA_MATRIX SWIFT_NAME(dataMatrix) = 6, + EAN_13 SWIFT_NAME(ean13) = 7, + EAN_8 SWIFT_NAME(ean8) = 8, + ITF SWIFT_NAME(itf) = 9, + QR_CODE SWIFT_NAME(qrCode) = 10, + UPC_A SWIFT_NAME(upcA) = 11, + UPC_E SWIFT_NAME(upcE) = 12, + PDF417 SWIFT_NAME(pdf417) = 13, + AZTEC SWIFT_NAME(aztec) = 14, + } CLOSED_ENUM; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ BarcodeFormat <> JS BarcodeFormat (union) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::BarcodeFormat fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + std::string unionValue = JSIConverter::fromJSI(runtime, arg); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("UNKNOWN"): return margelo::nitro::visioncameramlkit::BarcodeFormat::UNKNOWN; + case hashString("ALL_FORMATS"): return margelo::nitro::visioncameramlkit::BarcodeFormat::ALL_FORMATS; + case hashString("CODE_128"): return margelo::nitro::visioncameramlkit::BarcodeFormat::CODE_128; + case hashString("CODE_39"): return margelo::nitro::visioncameramlkit::BarcodeFormat::CODE_39; + case hashString("CODE_93"): return margelo::nitro::visioncameramlkit::BarcodeFormat::CODE_93; + case hashString("CODABAR"): return margelo::nitro::visioncameramlkit::BarcodeFormat::CODABAR; + case hashString("DATA_MATRIX"): return margelo::nitro::visioncameramlkit::BarcodeFormat::DATA_MATRIX; + case hashString("EAN_13"): return margelo::nitro::visioncameramlkit::BarcodeFormat::EAN_13; + case hashString("EAN_8"): return margelo::nitro::visioncameramlkit::BarcodeFormat::EAN_8; + case hashString("ITF"): return margelo::nitro::visioncameramlkit::BarcodeFormat::ITF; + case hashString("QR_CODE"): return margelo::nitro::visioncameramlkit::BarcodeFormat::QR_CODE; + case hashString("UPC_A"): return margelo::nitro::visioncameramlkit::BarcodeFormat::UPC_A; + case hashString("UPC_E"): return margelo::nitro::visioncameramlkit::BarcodeFormat::UPC_E; + case hashString("PDF417"): return margelo::nitro::visioncameramlkit::BarcodeFormat::PDF417; + case hashString("AZTEC"): return margelo::nitro::visioncameramlkit::BarcodeFormat::AZTEC; + default: [[unlikely]] + throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum BarcodeFormat - invalid value!"); + } + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::visioncameramlkit::BarcodeFormat arg) { + switch (arg) { + case margelo::nitro::visioncameramlkit::BarcodeFormat::UNKNOWN: return JSIConverter::toJSI(runtime, "UNKNOWN"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::ALL_FORMATS: return JSIConverter::toJSI(runtime, "ALL_FORMATS"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::CODE_128: return JSIConverter::toJSI(runtime, "CODE_128"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::CODE_39: return JSIConverter::toJSI(runtime, "CODE_39"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::CODE_93: return JSIConverter::toJSI(runtime, "CODE_93"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::CODABAR: return JSIConverter::toJSI(runtime, "CODABAR"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::DATA_MATRIX: return JSIConverter::toJSI(runtime, "DATA_MATRIX"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::EAN_13: return JSIConverter::toJSI(runtime, "EAN_13"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::EAN_8: return JSIConverter::toJSI(runtime, "EAN_8"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::ITF: return JSIConverter::toJSI(runtime, "ITF"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::QR_CODE: return JSIConverter::toJSI(runtime, "QR_CODE"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::UPC_A: return JSIConverter::toJSI(runtime, "UPC_A"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::UPC_E: return JSIConverter::toJSI(runtime, "UPC_E"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::PDF417: return JSIConverter::toJSI(runtime, "PDF417"); + case margelo::nitro::visioncameramlkit::BarcodeFormat::AZTEC: return JSIConverter::toJSI(runtime, "AZTEC"); + default: [[unlikely]] + throw std::invalid_argument("Cannot convert BarcodeFormat to JS - invalid value: " + + std::to_string(static_cast(arg)) + "!"); + } + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isString()) { + return false; + } + std::string unionValue = JSIConverter::fromJSI(runtime, value); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("UNKNOWN"): + case hashString("ALL_FORMATS"): + case hashString("CODE_128"): + case hashString("CODE_39"): + case hashString("CODE_93"): + case hashString("CODABAR"): + case hashString("DATA_MATRIX"): + case hashString("EAN_13"): + case hashString("EAN_8"): + case hashString("ITF"): + case hashString("QR_CODE"): + case hashString("UPC_A"): + case hashString("UPC_E"): + case hashString("PDF417"): + case hashString("AZTEC"): + return true; + default: + return false; + } + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/BarcodeScannerBarcode.hpp b/nitrogen/generated/shared/c++/BarcodeScannerBarcode.hpp new file mode 100644 index 0000000..75e2d10 --- /dev/null +++ b/nitrogen/generated/shared/c++/BarcodeScannerBarcode.hpp @@ -0,0 +1,137 @@ +/// +/// BarcodeScannerBarcode.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `BoundingBox` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BoundingBox; } +// Forward declaration of `Corner` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct Corner; } +// Forward declaration of `BarcodeFormat` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeFormat; } +// Forward declaration of `BarcodeValueTypeName` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeValueTypeName; } + +#include "BoundingBox.hpp" +#include +#include "Corner.hpp" +#include +#include "BarcodeFormat.hpp" +#include "BarcodeValueTypeName.hpp" +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (BarcodeScannerBarcode). + */ + struct BarcodeScannerBarcode final { + public: + std::optional bounds SWIFT_PRIVATE; + std::optional> corners SWIFT_PRIVATE; + double format SWIFT_PRIVATE; + BarcodeFormat formatName SWIFT_PRIVATE; + double valueType SWIFT_PRIVATE; + BarcodeValueTypeName valueTypeName SWIFT_PRIVATE; + std::optional rawValue SWIFT_PRIVATE; + std::optional displayValue SWIFT_PRIVATE; + std::optional> rawBytes SWIFT_PRIVATE; + std::optional> value SWIFT_PRIVATE; + bool isPotential SWIFT_PRIVATE; + + public: + BarcodeScannerBarcode() = default; + explicit BarcodeScannerBarcode(std::optional bounds, std::optional> corners, double format, BarcodeFormat formatName, double valueType, BarcodeValueTypeName valueTypeName, std::optional rawValue, std::optional displayValue, std::optional> rawBytes, std::optional> value, bool isPotential): bounds(bounds), corners(corners), format(format), formatName(formatName), valueType(valueType), valueTypeName(valueTypeName), rawValue(rawValue), displayValue(displayValue), rawBytes(rawBytes), value(value), isPotential(isPotential) {} + + public: + friend bool operator==(const BarcodeScannerBarcode& lhs, const BarcodeScannerBarcode& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ BarcodeScannerBarcode <> JS BarcodeScannerBarcode (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::BarcodeScannerBarcode fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::BarcodeScannerBarcode( + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bounds"))), + JSIConverter>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "corners"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "format"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "formatName"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "valueType"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "valueTypeName"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "rawValue"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "displayValue"))), + JSIConverter>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "rawBytes"))), + JSIConverter>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "value"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "isPotential"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::BarcodeScannerBarcode& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "bounds"), JSIConverter>::toJSI(runtime, arg.bounds)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "corners"), JSIConverter>>::toJSI(runtime, arg.corners)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "format"), JSIConverter::toJSI(runtime, arg.format)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "formatName"), JSIConverter::toJSI(runtime, arg.formatName)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "valueType"), JSIConverter::toJSI(runtime, arg.valueType)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "valueTypeName"), JSIConverter::toJSI(runtime, arg.valueTypeName)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "rawValue"), JSIConverter>::toJSI(runtime, arg.rawValue)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "displayValue"), JSIConverter>::toJSI(runtime, arg.displayValue)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "rawBytes"), JSIConverter>>::toJSI(runtime, arg.rawBytes)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "value"), JSIConverter>>::toJSI(runtime, arg.value)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "isPotential"), JSIConverter::toJSI(runtime, arg.isPotential)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bounds")))) return false; + if (!JSIConverter>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "corners")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "format")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "formatName")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "valueType")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "valueTypeName")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "rawValue")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "displayValue")))) return false; + if (!JSIConverter>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "rawBytes")))) return false; + if (!JSIConverter>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "value")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "isPotential")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/BarcodeScannerOptions.hpp b/nitrogen/generated/shared/c++/BarcodeScannerOptions.hpp new file mode 100644 index 0000000..edec6fd --- /dev/null +++ b/nitrogen/generated/shared/c++/BarcodeScannerOptions.hpp @@ -0,0 +1,116 @@ +/// +/// BarcodeScannerOptions.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `BarcodeFormat` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class BarcodeFormat; } +// Forward declaration of `RegionOfInterest` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct RegionOfInterest; } +// Forward declaration of `Orientation` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class Orientation; } + +#include "BarcodeFormat.hpp" +#include +#include +#include "RegionOfInterest.hpp" +#include "Orientation.hpp" + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (BarcodeScannerOptions). + */ + struct BarcodeScannerOptions final { + public: + std::optional> formats SWIFT_PRIVATE; + std::optional enableAllPotentialBarcodes SWIFT_PRIVATE; + std::optional scaleFactor SWIFT_PRIVATE; + std::optional invertColors SWIFT_PRIVATE; + std::optional frameProcessInterval SWIFT_PRIVATE; + std::optional roi SWIFT_PRIVATE; + std::optional orientation SWIFT_PRIVATE; + + public: + BarcodeScannerOptions() = default; + explicit BarcodeScannerOptions(std::optional> formats, std::optional enableAllPotentialBarcodes, std::optional scaleFactor, std::optional invertColors, std::optional frameProcessInterval, std::optional roi, std::optional orientation): formats(formats), enableAllPotentialBarcodes(enableAllPotentialBarcodes), scaleFactor(scaleFactor), invertColors(invertColors), frameProcessInterval(frameProcessInterval), roi(roi), orientation(orientation) {} + + public: + friend bool operator==(const BarcodeScannerOptions& lhs, const BarcodeScannerOptions& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ BarcodeScannerOptions <> JS BarcodeScannerOptions (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::BarcodeScannerOptions fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::BarcodeScannerOptions( + JSIConverter>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "formats"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "enableAllPotentialBarcodes"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "scaleFactor"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "invertColors"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "frameProcessInterval"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "roi"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "orientation"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::BarcodeScannerOptions& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "formats"), JSIConverter>>::toJSI(runtime, arg.formats)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "enableAllPotentialBarcodes"), JSIConverter>::toJSI(runtime, arg.enableAllPotentialBarcodes)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "scaleFactor"), JSIConverter>::toJSI(runtime, arg.scaleFactor)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "invertColors"), JSIConverter>::toJSI(runtime, arg.invertColors)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "frameProcessInterval"), JSIConverter>::toJSI(runtime, arg.frameProcessInterval)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "roi"), JSIConverter>::toJSI(runtime, arg.roi)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "orientation"), JSIConverter>::toJSI(runtime, arg.orientation)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "formats")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "enableAllPotentialBarcodes")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "scaleFactor")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "invertColors")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "frameProcessInterval")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "roi")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "orientation")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/BarcodeScannerResult.hpp b/nitrogen/generated/shared/c++/BarcodeScannerResult.hpp new file mode 100644 index 0000000..42d50df --- /dev/null +++ b/nitrogen/generated/shared/c++/BarcodeScannerResult.hpp @@ -0,0 +1,85 @@ +/// +/// BarcodeScannerResult.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `BarcodeScannerBarcode` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerBarcode; } + +#include "BarcodeScannerBarcode.hpp" +#include + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (BarcodeScannerResult). + */ + struct BarcodeScannerResult final { + public: + std::vector barcodes SWIFT_PRIVATE; + + public: + BarcodeScannerResult() = default; + explicit BarcodeScannerResult(std::vector barcodes): barcodes(barcodes) {} + + public: + friend bool operator==(const BarcodeScannerResult& lhs, const BarcodeScannerResult& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ BarcodeScannerResult <> JS BarcodeScannerResult (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::BarcodeScannerResult fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::BarcodeScannerResult( + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "barcodes"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::BarcodeScannerResult& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "barcodes"), JSIConverter>::toJSI(runtime, arg.barcodes)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "barcodes")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/BarcodeValueTypeName.hpp b/nitrogen/generated/shared/c++/BarcodeValueTypeName.hpp new file mode 100644 index 0000000..ba08c0d --- /dev/null +++ b/nitrogen/generated/shared/c++/BarcodeValueTypeName.hpp @@ -0,0 +1,120 @@ +/// +/// BarcodeValueTypeName.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +namespace margelo::nitro::visioncameramlkit { + + /** + * An enum which can be represented as a JavaScript union (BarcodeValueTypeName). + */ + enum class BarcodeValueTypeName { + TYPE_UNKNOWN SWIFT_NAME(typeUnknown) = 0, + TYPE_CONTACT_INFO SWIFT_NAME(typeContactInfo) = 1, + TYPE_EMAIL SWIFT_NAME(typeEmail) = 2, + TYPE_ISBN SWIFT_NAME(typeIsbn) = 3, + TYPE_PHONE SWIFT_NAME(typePhone) = 4, + TYPE_PRODUCT SWIFT_NAME(typeProduct) = 5, + TYPE_SMS SWIFT_NAME(typeSms) = 6, + TYPE_TEXT SWIFT_NAME(typeText) = 7, + TYPE_URL SWIFT_NAME(typeUrl) = 8, + TYPE_WIFI SWIFT_NAME(typeWifi) = 9, + TYPE_GEO SWIFT_NAME(typeGeo) = 10, + TYPE_CALENDAR_EVENT SWIFT_NAME(typeCalendarEvent) = 11, + TYPE_DRIVER_LICENSE SWIFT_NAME(typeDriverLicense) = 12, + } CLOSED_ENUM; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ BarcodeValueTypeName <> JS BarcodeValueTypeName (union) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::BarcodeValueTypeName fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + std::string unionValue = JSIConverter::fromJSI(runtime, arg); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("TYPE_UNKNOWN"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_UNKNOWN; + case hashString("TYPE_CONTACT_INFO"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_CONTACT_INFO; + case hashString("TYPE_EMAIL"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_EMAIL; + case hashString("TYPE_ISBN"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_ISBN; + case hashString("TYPE_PHONE"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_PHONE; + case hashString("TYPE_PRODUCT"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_PRODUCT; + case hashString("TYPE_SMS"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_SMS; + case hashString("TYPE_TEXT"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_TEXT; + case hashString("TYPE_URL"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_URL; + case hashString("TYPE_WIFI"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_WIFI; + case hashString("TYPE_GEO"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_GEO; + case hashString("TYPE_CALENDAR_EVENT"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_CALENDAR_EVENT; + case hashString("TYPE_DRIVER_LICENSE"): return margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_DRIVER_LICENSE; + default: [[unlikely]] + throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum BarcodeValueTypeName - invalid value!"); + } + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::visioncameramlkit::BarcodeValueTypeName arg) { + switch (arg) { + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_UNKNOWN: return JSIConverter::toJSI(runtime, "TYPE_UNKNOWN"); + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_CONTACT_INFO: return JSIConverter::toJSI(runtime, "TYPE_CONTACT_INFO"); + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_EMAIL: return JSIConverter::toJSI(runtime, "TYPE_EMAIL"); + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_ISBN: return JSIConverter::toJSI(runtime, "TYPE_ISBN"); + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_PHONE: return JSIConverter::toJSI(runtime, "TYPE_PHONE"); + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_PRODUCT: return JSIConverter::toJSI(runtime, "TYPE_PRODUCT"); + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_SMS: return JSIConverter::toJSI(runtime, "TYPE_SMS"); + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_TEXT: return JSIConverter::toJSI(runtime, "TYPE_TEXT"); + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_URL: return JSIConverter::toJSI(runtime, "TYPE_URL"); + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_WIFI: return JSIConverter::toJSI(runtime, "TYPE_WIFI"); + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_GEO: return JSIConverter::toJSI(runtime, "TYPE_GEO"); + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_CALENDAR_EVENT: return JSIConverter::toJSI(runtime, "TYPE_CALENDAR_EVENT"); + case margelo::nitro::visioncameramlkit::BarcodeValueTypeName::TYPE_DRIVER_LICENSE: return JSIConverter::toJSI(runtime, "TYPE_DRIVER_LICENSE"); + default: [[unlikely]] + throw std::invalid_argument("Cannot convert BarcodeValueTypeName to JS - invalid value: " + + std::to_string(static_cast(arg)) + "!"); + } + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isString()) { + return false; + } + std::string unionValue = JSIConverter::fromJSI(runtime, value); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("TYPE_UNKNOWN"): + case hashString("TYPE_CONTACT_INFO"): + case hashString("TYPE_EMAIL"): + case hashString("TYPE_ISBN"): + case hashString("TYPE_PHONE"): + case hashString("TYPE_PRODUCT"): + case hashString("TYPE_SMS"): + case hashString("TYPE_TEXT"): + case hashString("TYPE_URL"): + case hashString("TYPE_WIFI"): + case hashString("TYPE_GEO"): + case hashString("TYPE_CALENDAR_EVENT"): + case hashString("TYPE_DRIVER_LICENSE"): + return true; + default: + return false; + } + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/BoundingBox.hpp b/nitrogen/generated/shared/c++/BoundingBox.hpp new file mode 100644 index 0000000..c614613 --- /dev/null +++ b/nitrogen/generated/shared/c++/BoundingBox.hpp @@ -0,0 +1,119 @@ +/// +/// BoundingBox.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + + + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (BoundingBox). + */ + struct BoundingBox final { + public: + double x SWIFT_PRIVATE; + double y SWIFT_PRIVATE; + double centerX SWIFT_PRIVATE; + double centerY SWIFT_PRIVATE; + double width SWIFT_PRIVATE; + double height SWIFT_PRIVATE; + double top SWIFT_PRIVATE; + double left SWIFT_PRIVATE; + double bottom SWIFT_PRIVATE; + double right SWIFT_PRIVATE; + + public: + BoundingBox() = default; + explicit BoundingBox(double x, double y, double centerX, double centerY, double width, double height, double top, double left, double bottom, double right): x(x), y(y), centerX(centerX), centerY(centerY), width(width), height(height), top(top), left(left), bottom(bottom), right(right) {} + + public: + friend bool operator==(const BoundingBox& lhs, const BoundingBox& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ BoundingBox <> JS BoundingBox (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::BoundingBox fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::BoundingBox( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "x"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "y"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "centerX"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "centerY"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "top"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "left"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bottom"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "right"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::BoundingBox& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "x"), JSIConverter::toJSI(runtime, arg.x)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "y"), JSIConverter::toJSI(runtime, arg.y)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "centerX"), JSIConverter::toJSI(runtime, arg.centerX)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "centerY"), JSIConverter::toJSI(runtime, arg.centerY)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "width"), JSIConverter::toJSI(runtime, arg.width)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "height"), JSIConverter::toJSI(runtime, arg.height)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "top"), JSIConverter::toJSI(runtime, arg.top)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "left"), JSIConverter::toJSI(runtime, arg.left)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "bottom"), JSIConverter::toJSI(runtime, arg.bottom)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "right"), JSIConverter::toJSI(runtime, arg.right)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "x")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "y")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "centerX")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "centerY")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "top")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "left")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bottom")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "right")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/Corner.hpp b/nitrogen/generated/shared/c++/Corner.hpp new file mode 100644 index 0000000..3665d97 --- /dev/null +++ b/nitrogen/generated/shared/c++/Corner.hpp @@ -0,0 +1,87 @@ +/// +/// Corner.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + + + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (Corner). + */ + struct Corner final { + public: + double x SWIFT_PRIVATE; + double y SWIFT_PRIVATE; + + public: + Corner() = default; + explicit Corner(double x, double y): x(x), y(y) {} + + public: + friend bool operator==(const Corner& lhs, const Corner& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ Corner <> JS Corner (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::Corner fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::Corner( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "x"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "y"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::Corner& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "x"), JSIConverter::toJSI(runtime, arg.x)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "y"), JSIConverter::toJSI(runtime, arg.y)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "x")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "y")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/HybridBarcodeScannerSpec.cpp b/nitrogen/generated/shared/c++/HybridBarcodeScannerSpec.cpp new file mode 100644 index 0000000..da0f630 --- /dev/null +++ b/nitrogen/generated/shared/c++/HybridBarcodeScannerSpec.cpp @@ -0,0 +1,22 @@ +/// +/// HybridBarcodeScannerSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridBarcodeScannerSpec.hpp" + +namespace margelo::nitro::visioncameramlkit { + + void HybridBarcodeScannerSpec::loadHybridMethods() { + // load base methods/properties + HybridObject::loadHybridMethods(); + // load custom methods/properties + registerHybrids(this, [](Prototype& prototype) { + prototype.registerHybridMethod("recognize", &HybridBarcodeScannerSpec::recognize); + prototype.registerHybridMethod("recognizeImage", &HybridBarcodeScannerSpec::recognizeImage); + }); + } + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/shared/c++/HybridBarcodeScannerSpec.hpp b/nitrogen/generated/shared/c++/HybridBarcodeScannerSpec.hpp new file mode 100644 index 0000000..be81b72 --- /dev/null +++ b/nitrogen/generated/shared/c++/HybridBarcodeScannerSpec.hpp @@ -0,0 +1,74 @@ +/// +/// HybridBarcodeScannerSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `BarcodeScannerResult` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerResult; } +// Forward declaration of `HybridFrameSpec` to properly resolve imports. +namespace margelo::nitro::camera { class HybridFrameSpec; } +// Forward declaration of `MLKitBaseArguments` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct MLKitBaseArguments; } + +#include "BarcodeScannerResult.hpp" +#include +#include +#include "MLKitBaseArguments.hpp" +#include +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace margelo::nitro; + + /** + * An abstract base class for `BarcodeScanner` + * Inherit this class to create instances of `HybridBarcodeScannerSpec` in C++. + * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual. + * @example + * ```cpp + * class HybridBarcodeScanner: public HybridBarcodeScannerSpec { + * public: + * HybridBarcodeScanner(...): HybridObject(TAG) { ... } + * // ... + * }; + * ``` + */ + class HybridBarcodeScannerSpec: public virtual HybridObject { + public: + // Constructor + explicit HybridBarcodeScannerSpec(): HybridObject(TAG) { } + + // Destructor + ~HybridBarcodeScannerSpec() override = default; + + public: + // Properties + + + public: + // Methods + virtual BarcodeScannerResult recognize(const std::shared_ptr& frame, const std::optional& args) = 0; + virtual std::shared_ptr> recognizeImage(const std::string& uri) = 0; + + protected: + // Hybrid Setup + void loadHybridMethods() override; + + protected: + // Tag for logging + static constexpr auto TAG = "BarcodeScanner"; + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/shared/c++/HybridTextRecognizerSpec.cpp b/nitrogen/generated/shared/c++/HybridTextRecognizerSpec.cpp new file mode 100644 index 0000000..cabcd68 --- /dev/null +++ b/nitrogen/generated/shared/c++/HybridTextRecognizerSpec.cpp @@ -0,0 +1,22 @@ +/// +/// HybridTextRecognizerSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridTextRecognizerSpec.hpp" + +namespace margelo::nitro::visioncameramlkit { + + void HybridTextRecognizerSpec::loadHybridMethods() { + // load base methods/properties + HybridObject::loadHybridMethods(); + // load custom methods/properties + registerHybrids(this, [](Prototype& prototype) { + prototype.registerHybridMethod("recognize", &HybridTextRecognizerSpec::recognize); + prototype.registerHybridMethod("recognizeImage", &HybridTextRecognizerSpec::recognizeImage); + }); + } + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/shared/c++/HybridTextRecognizerSpec.hpp b/nitrogen/generated/shared/c++/HybridTextRecognizerSpec.hpp new file mode 100644 index 0000000..9e09106 --- /dev/null +++ b/nitrogen/generated/shared/c++/HybridTextRecognizerSpec.hpp @@ -0,0 +1,74 @@ +/// +/// HybridTextRecognizerSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `TextRecognitionResult` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextRecognitionResult; } +// Forward declaration of `HybridFrameSpec` to properly resolve imports. +namespace margelo::nitro::camera { class HybridFrameSpec; } +// Forward declaration of `MLKitBaseArguments` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct MLKitBaseArguments; } + +#include "TextRecognitionResult.hpp" +#include +#include +#include "MLKitBaseArguments.hpp" +#include +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + using namespace margelo::nitro; + + /** + * An abstract base class for `TextRecognizer` + * Inherit this class to create instances of `HybridTextRecognizerSpec` in C++. + * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual. + * @example + * ```cpp + * class HybridTextRecognizer: public HybridTextRecognizerSpec { + * public: + * HybridTextRecognizer(...): HybridObject(TAG) { ... } + * // ... + * }; + * ``` + */ + class HybridTextRecognizerSpec: public virtual HybridObject { + public: + // Constructor + explicit HybridTextRecognizerSpec(): HybridObject(TAG) { } + + // Destructor + ~HybridTextRecognizerSpec() override = default; + + public: + // Properties + + + public: + // Methods + virtual TextRecognitionResult recognize(const std::shared_ptr& frame, const std::optional& args) = 0; + virtual std::shared_ptr> recognizeImage(const std::string& uri) = 0; + + protected: + // Hybrid Setup + void loadHybridMethods() override; + + protected: + // Tag for logging + static constexpr auto TAG = "TextRecognizer"; + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/shared/c++/HybridVisionCameraMLKitSpec.cpp b/nitrogen/generated/shared/c++/HybridVisionCameraMLKitSpec.cpp new file mode 100644 index 0000000..f28b2bd --- /dev/null +++ b/nitrogen/generated/shared/c++/HybridVisionCameraMLKitSpec.cpp @@ -0,0 +1,23 @@ +/// +/// HybridVisionCameraMLKitSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridVisionCameraMLKitSpec.hpp" + +namespace margelo::nitro::visioncameramlkit { + + void HybridVisionCameraMLKitSpec::loadHybridMethods() { + // load base methods/properties + HybridObject::loadHybridMethods(); + // load custom methods/properties + registerHybrids(this, [](Prototype& prototype) { + prototype.registerHybridMethod("isFeatureAvailable", &HybridVisionCameraMLKitSpec::isFeatureAvailable); + prototype.registerHybridMethod("createTextRecognizer", &HybridVisionCameraMLKitSpec::createTextRecognizer); + prototype.registerHybridMethod("createBarcodeScanner", &HybridVisionCameraMLKitSpec::createBarcodeScanner); + }); + } + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/shared/c++/HybridVisionCameraMLKitSpec.hpp b/nitrogen/generated/shared/c++/HybridVisionCameraMLKitSpec.hpp new file mode 100644 index 0000000..dded17f --- /dev/null +++ b/nitrogen/generated/shared/c++/HybridVisionCameraMLKitSpec.hpp @@ -0,0 +1,78 @@ +/// +/// HybridVisionCameraMLKitSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `MLKitFeature` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class MLKitFeature; } +// Forward declaration of `HybridTextRecognizerSpec` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { class HybridTextRecognizerSpec; } +// Forward declaration of `TextRecognizerOptions` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextRecognizerOptions; } +// Forward declaration of `HybridBarcodeScannerSpec` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { class HybridBarcodeScannerSpec; } +// Forward declaration of `BarcodeScannerOptions` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BarcodeScannerOptions; } + +#include "MLKitFeature.hpp" +#include +#include "HybridTextRecognizerSpec.hpp" +#include "TextRecognizerOptions.hpp" +#include "HybridBarcodeScannerSpec.hpp" +#include "BarcodeScannerOptions.hpp" + +namespace margelo::nitro::visioncameramlkit { + + using namespace margelo::nitro; + + /** + * An abstract base class for `VisionCameraMLKit` + * Inherit this class to create instances of `HybridVisionCameraMLKitSpec` in C++. + * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual. + * @example + * ```cpp + * class HybridVisionCameraMLKit: public HybridVisionCameraMLKitSpec { + * public: + * HybridVisionCameraMLKit(...): HybridObject(TAG) { ... } + * // ... + * }; + * ``` + */ + class HybridVisionCameraMLKitSpec: public virtual HybridObject { + public: + // Constructor + explicit HybridVisionCameraMLKitSpec(): HybridObject(TAG) { } + + // Destructor + ~HybridVisionCameraMLKitSpec() override = default; + + public: + // Properties + + + public: + // Methods + virtual bool isFeatureAvailable(MLKitFeature feature) = 0; + virtual std::shared_ptr createTextRecognizer(const TextRecognizerOptions& options) = 0; + virtual std::shared_ptr createBarcodeScanner(const BarcodeScannerOptions& options) = 0; + + protected: + // Hybrid Setup + void loadHybridMethods() override; + + protected: + // Tag for logging + static constexpr auto TAG = "VisionCameraMLKit"; + }; + +} // namespace margelo::nitro::visioncameramlkit diff --git a/nitrogen/generated/shared/c++/MLKitBaseArguments.hpp b/nitrogen/generated/shared/c++/MLKitBaseArguments.hpp new file mode 100644 index 0000000..b225cd4 --- /dev/null +++ b/nitrogen/generated/shared/c++/MLKitBaseArguments.hpp @@ -0,0 +1,85 @@ +/// +/// MLKitBaseArguments.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `Orientation` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class Orientation; } + +#include "Orientation.hpp" +#include + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (MLKitBaseArguments). + */ + struct MLKitBaseArguments final { + public: + std::optional outputOrientation SWIFT_PRIVATE; + + public: + MLKitBaseArguments() = default; + explicit MLKitBaseArguments(std::optional outputOrientation): outputOrientation(outputOrientation) {} + + public: + friend bool operator==(const MLKitBaseArguments& lhs, const MLKitBaseArguments& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ MLKitBaseArguments <> JS MLKitBaseArguments (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::MLKitBaseArguments fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::MLKitBaseArguments( + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "outputOrientation"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::MLKitBaseArguments& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "outputOrientation"), JSIConverter>::toJSI(runtime, arg.outputOrientation)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "outputOrientation")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/MLKitFeature.hpp b/nitrogen/generated/shared/c++/MLKitFeature.hpp new file mode 100644 index 0000000..3ed1d2c --- /dev/null +++ b/nitrogen/generated/shared/c++/MLKitFeature.hpp @@ -0,0 +1,112 @@ +/// +/// MLKitFeature.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +namespace margelo::nitro::visioncameramlkit { + + /** + * An enum which can be represented as a JavaScript union (MLKitFeature). + */ + enum class MLKitFeature { + TEXTRECOGNITION SWIFT_NAME(textrecognition) = 0, + FACEDETECTION SWIFT_NAME(facedetection) = 1, + FACEMESHDETECTION SWIFT_NAME(facemeshdetection) = 2, + POSEDETECTION SWIFT_NAME(posedetection) = 3, + SELFIESEGMENTATION SWIFT_NAME(selfiesegmentation) = 4, + SUBJECTSEGMENTATION SWIFT_NAME(subjectsegmentation) = 5, + DOCUMENTSCANNER SWIFT_NAME(documentscanner) = 6, + BARCODESCANNING SWIFT_NAME(barcodescanning) = 7, + IMAGELABELING SWIFT_NAME(imagelabeling) = 8, + OBJECTDETECTION SWIFT_NAME(objectdetection) = 9, + DIGITALINKRECOGNITION SWIFT_NAME(digitalinkrecognition) = 10, + } CLOSED_ENUM; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ MLKitFeature <> JS MLKitFeature (union) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::MLKitFeature fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + std::string unionValue = JSIConverter::fromJSI(runtime, arg); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("TextRecognition"): return margelo::nitro::visioncameramlkit::MLKitFeature::TEXTRECOGNITION; + case hashString("FaceDetection"): return margelo::nitro::visioncameramlkit::MLKitFeature::FACEDETECTION; + case hashString("FaceMeshDetection"): return margelo::nitro::visioncameramlkit::MLKitFeature::FACEMESHDETECTION; + case hashString("PoseDetection"): return margelo::nitro::visioncameramlkit::MLKitFeature::POSEDETECTION; + case hashString("SelfieSegmentation"): return margelo::nitro::visioncameramlkit::MLKitFeature::SELFIESEGMENTATION; + case hashString("SubjectSegmentation"): return margelo::nitro::visioncameramlkit::MLKitFeature::SUBJECTSEGMENTATION; + case hashString("DocumentScanner"): return margelo::nitro::visioncameramlkit::MLKitFeature::DOCUMENTSCANNER; + case hashString("BarcodeScanning"): return margelo::nitro::visioncameramlkit::MLKitFeature::BARCODESCANNING; + case hashString("ImageLabeling"): return margelo::nitro::visioncameramlkit::MLKitFeature::IMAGELABELING; + case hashString("ObjectDetection"): return margelo::nitro::visioncameramlkit::MLKitFeature::OBJECTDETECTION; + case hashString("DigitalInkRecognition"): return margelo::nitro::visioncameramlkit::MLKitFeature::DIGITALINKRECOGNITION; + default: [[unlikely]] + throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum MLKitFeature - invalid value!"); + } + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::visioncameramlkit::MLKitFeature arg) { + switch (arg) { + case margelo::nitro::visioncameramlkit::MLKitFeature::TEXTRECOGNITION: return JSIConverter::toJSI(runtime, "TextRecognition"); + case margelo::nitro::visioncameramlkit::MLKitFeature::FACEDETECTION: return JSIConverter::toJSI(runtime, "FaceDetection"); + case margelo::nitro::visioncameramlkit::MLKitFeature::FACEMESHDETECTION: return JSIConverter::toJSI(runtime, "FaceMeshDetection"); + case margelo::nitro::visioncameramlkit::MLKitFeature::POSEDETECTION: return JSIConverter::toJSI(runtime, "PoseDetection"); + case margelo::nitro::visioncameramlkit::MLKitFeature::SELFIESEGMENTATION: return JSIConverter::toJSI(runtime, "SelfieSegmentation"); + case margelo::nitro::visioncameramlkit::MLKitFeature::SUBJECTSEGMENTATION: return JSIConverter::toJSI(runtime, "SubjectSegmentation"); + case margelo::nitro::visioncameramlkit::MLKitFeature::DOCUMENTSCANNER: return JSIConverter::toJSI(runtime, "DocumentScanner"); + case margelo::nitro::visioncameramlkit::MLKitFeature::BARCODESCANNING: return JSIConverter::toJSI(runtime, "BarcodeScanning"); + case margelo::nitro::visioncameramlkit::MLKitFeature::IMAGELABELING: return JSIConverter::toJSI(runtime, "ImageLabeling"); + case margelo::nitro::visioncameramlkit::MLKitFeature::OBJECTDETECTION: return JSIConverter::toJSI(runtime, "ObjectDetection"); + case margelo::nitro::visioncameramlkit::MLKitFeature::DIGITALINKRECOGNITION: return JSIConverter::toJSI(runtime, "DigitalInkRecognition"); + default: [[unlikely]] + throw std::invalid_argument("Cannot convert MLKitFeature to JS - invalid value: " + + std::to_string(static_cast(arg)) + "!"); + } + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isString()) { + return false; + } + std::string unionValue = JSIConverter::fromJSI(runtime, value); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("TextRecognition"): + case hashString("FaceDetection"): + case hashString("FaceMeshDetection"): + case hashString("PoseDetection"): + case hashString("SelfieSegmentation"): + case hashString("SubjectSegmentation"): + case hashString("DocumentScanner"): + case hashString("BarcodeScanning"): + case hashString("ImageLabeling"): + case hashString("ObjectDetection"): + case hashString("DigitalInkRecognition"): + return true; + default: + return false; + } + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/Orientation.hpp b/nitrogen/generated/shared/c++/Orientation.hpp new file mode 100644 index 0000000..b0fb591 --- /dev/null +++ b/nitrogen/generated/shared/c++/Orientation.hpp @@ -0,0 +1,84 @@ +/// +/// Orientation.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +namespace margelo::nitro::visioncameramlkit { + + /** + * An enum which can be represented as a JavaScript union (Orientation). + */ + enum class Orientation { + PORTRAIT SWIFT_NAME(portrait) = 0, + PORTRAIT_UPSIDE_DOWN SWIFT_NAME(portraitUpsideDown) = 1, + LANDSCAPE_LEFT SWIFT_NAME(landscapeLeft) = 2, + LANDSCAPE_RIGHT SWIFT_NAME(landscapeRight) = 3, + } CLOSED_ENUM; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ Orientation <> JS Orientation (union) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::Orientation fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + std::string unionValue = JSIConverter::fromJSI(runtime, arg); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("portrait"): return margelo::nitro::visioncameramlkit::Orientation::PORTRAIT; + case hashString("portrait-upside-down"): return margelo::nitro::visioncameramlkit::Orientation::PORTRAIT_UPSIDE_DOWN; + case hashString("landscape-left"): return margelo::nitro::visioncameramlkit::Orientation::LANDSCAPE_LEFT; + case hashString("landscape-right"): return margelo::nitro::visioncameramlkit::Orientation::LANDSCAPE_RIGHT; + default: [[unlikely]] + throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum Orientation - invalid value!"); + } + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::visioncameramlkit::Orientation arg) { + switch (arg) { + case margelo::nitro::visioncameramlkit::Orientation::PORTRAIT: return JSIConverter::toJSI(runtime, "portrait"); + case margelo::nitro::visioncameramlkit::Orientation::PORTRAIT_UPSIDE_DOWN: return JSIConverter::toJSI(runtime, "portrait-upside-down"); + case margelo::nitro::visioncameramlkit::Orientation::LANDSCAPE_LEFT: return JSIConverter::toJSI(runtime, "landscape-left"); + case margelo::nitro::visioncameramlkit::Orientation::LANDSCAPE_RIGHT: return JSIConverter::toJSI(runtime, "landscape-right"); + default: [[unlikely]] + throw std::invalid_argument("Cannot convert Orientation to JS - invalid value: " + + std::to_string(static_cast(arg)) + "!"); + } + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isString()) { + return false; + } + std::string unionValue = JSIConverter::fromJSI(runtime, value); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("portrait"): + case hashString("portrait-upside-down"): + case hashString("landscape-left"): + case hashString("landscape-right"): + return true; + default: + return false; + } + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/RegionOfInterest.hpp b/nitrogen/generated/shared/c++/RegionOfInterest.hpp new file mode 100644 index 0000000..d32427b --- /dev/null +++ b/nitrogen/generated/shared/c++/RegionOfInterest.hpp @@ -0,0 +1,101 @@ +/// +/// RegionOfInterest.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `RegionOfInterestUnit` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class RegionOfInterestUnit; } + +#include "RegionOfInterestUnit.hpp" +#include + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (RegionOfInterest). + */ + struct RegionOfInterest final { + public: + double x SWIFT_PRIVATE; + double y SWIFT_PRIVATE; + double width SWIFT_PRIVATE; + double height SWIFT_PRIVATE; + std::optional unit SWIFT_PRIVATE; + + public: + RegionOfInterest() = default; + explicit RegionOfInterest(double x, double y, double width, double height, std::optional unit): x(x), y(y), width(width), height(height), unit(unit) {} + + public: + friend bool operator==(const RegionOfInterest& lhs, const RegionOfInterest& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ RegionOfInterest <> JS RegionOfInterest (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::RegionOfInterest fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::RegionOfInterest( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "x"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "y"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "unit"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::RegionOfInterest& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "x"), JSIConverter::toJSI(runtime, arg.x)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "y"), JSIConverter::toJSI(runtime, arg.y)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "width"), JSIConverter::toJSI(runtime, arg.width)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "height"), JSIConverter::toJSI(runtime, arg.height)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "unit"), JSIConverter>::toJSI(runtime, arg.unit)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "x")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "y")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "unit")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/RegionOfInterestUnit.hpp b/nitrogen/generated/shared/c++/RegionOfInterestUnit.hpp new file mode 100644 index 0000000..01651f5 --- /dev/null +++ b/nitrogen/generated/shared/c++/RegionOfInterestUnit.hpp @@ -0,0 +1,76 @@ +/// +/// RegionOfInterestUnit.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +namespace margelo::nitro::visioncameramlkit { + + /** + * An enum which can be represented as a JavaScript union (RegionOfInterestUnit). + */ + enum class RegionOfInterestUnit { + NORMALIZED SWIFT_NAME(normalized) = 0, + PIXEL SWIFT_NAME(pixel) = 1, + } CLOSED_ENUM; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ RegionOfInterestUnit <> JS RegionOfInterestUnit (union) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::RegionOfInterestUnit fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + std::string unionValue = JSIConverter::fromJSI(runtime, arg); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("normalized"): return margelo::nitro::visioncameramlkit::RegionOfInterestUnit::NORMALIZED; + case hashString("pixel"): return margelo::nitro::visioncameramlkit::RegionOfInterestUnit::PIXEL; + default: [[unlikely]] + throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum RegionOfInterestUnit - invalid value!"); + } + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::visioncameramlkit::RegionOfInterestUnit arg) { + switch (arg) { + case margelo::nitro::visioncameramlkit::RegionOfInterestUnit::NORMALIZED: return JSIConverter::toJSI(runtime, "normalized"); + case margelo::nitro::visioncameramlkit::RegionOfInterestUnit::PIXEL: return JSIConverter::toJSI(runtime, "pixel"); + default: [[unlikely]] + throw std::invalid_argument("Cannot convert RegionOfInterestUnit to JS - invalid value: " + + std::to_string(static_cast(arg)) + "!"); + } + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isString()) { + return false; + } + std::string unionValue = JSIConverter::fromJSI(runtime, value); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("normalized"): + case hashString("pixel"): + return true; + default: + return false; + } + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/TextBlock.hpp b/nitrogen/generated/shared/c++/TextBlock.hpp new file mode 100644 index 0000000..11b7ac5 --- /dev/null +++ b/nitrogen/generated/shared/c++/TextBlock.hpp @@ -0,0 +1,108 @@ +/// +/// TextBlock.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `BoundingBox` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BoundingBox; } +// Forward declaration of `Corner` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct Corner; } +// Forward declaration of `TextLine` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextLine; } + +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include +#include +#include "TextLine.hpp" + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (TextBlock). + */ + struct TextBlock final { + public: + BoundingBox bounds SWIFT_PRIVATE; + std::vector corners SWIFT_PRIVATE; + std::vector languages SWIFT_PRIVATE; + std::string text SWIFT_PRIVATE; + std::vector lines SWIFT_PRIVATE; + + public: + TextBlock() = default; + explicit TextBlock(BoundingBox bounds, std::vector corners, std::vector languages, std::string text, std::vector lines): bounds(bounds), corners(corners), languages(languages), text(text), lines(lines) {} + + public: + friend bool operator==(const TextBlock& lhs, const TextBlock& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ TextBlock <> JS TextBlock (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::TextBlock fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::TextBlock( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bounds"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "corners"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "languages"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "text"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lines"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::TextBlock& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "bounds"), JSIConverter::toJSI(runtime, arg.bounds)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "corners"), JSIConverter>::toJSI(runtime, arg.corners)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "languages"), JSIConverter>::toJSI(runtime, arg.languages)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "text"), JSIConverter::toJSI(runtime, arg.text)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "lines"), JSIConverter>::toJSI(runtime, arg.lines)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bounds")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "corners")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "languages")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "text")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lines")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/TextElement.hpp b/nitrogen/generated/shared/c++/TextElement.hpp new file mode 100644 index 0000000..50b7531 --- /dev/null +++ b/nitrogen/generated/shared/c++/TextElement.hpp @@ -0,0 +1,119 @@ +/// +/// TextElement.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `BoundingBox` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BoundingBox; } +// Forward declaration of `Corner` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct Corner; } +// Forward declaration of `TextSymbol` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextSymbol; } + +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include +#include +#include +#include +#include +#include "TextSymbol.hpp" + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (TextElement). + */ + struct TextElement final { + public: + BoundingBox bounds SWIFT_PRIVATE; + std::vector corners SWIFT_PRIVATE; + std::optional> confidence SWIFT_PRIVATE; + std::optional> angle SWIFT_PRIVATE; + std::vector languages SWIFT_PRIVATE; + std::string text SWIFT_PRIVATE; + std::vector symbols SWIFT_PRIVATE; + + public: + TextElement() = default; + explicit TextElement(BoundingBox bounds, std::vector corners, std::optional> confidence, std::optional> angle, std::vector languages, std::string text, std::vector symbols): bounds(bounds), corners(corners), confidence(confidence), angle(angle), languages(languages), text(text), symbols(symbols) {} + + public: + friend bool operator==(const TextElement& lhs, const TextElement& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ TextElement <> JS TextElement (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::TextElement fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::TextElement( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bounds"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "corners"))), + JSIConverter>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "confidence"))), + JSIConverter>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "angle"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "languages"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "text"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "symbols"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::TextElement& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "bounds"), JSIConverter::toJSI(runtime, arg.bounds)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "corners"), JSIConverter>::toJSI(runtime, arg.corners)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "confidence"), JSIConverter>>::toJSI(runtime, arg.confidence)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "angle"), JSIConverter>>::toJSI(runtime, arg.angle)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "languages"), JSIConverter>::toJSI(runtime, arg.languages)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "text"), JSIConverter::toJSI(runtime, arg.text)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "symbols"), JSIConverter>::toJSI(runtime, arg.symbols)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bounds")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "corners")))) return false; + if (!JSIConverter>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "confidence")))) return false; + if (!JSIConverter>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "angle")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "languages")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "text")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "symbols")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/TextLine.hpp b/nitrogen/generated/shared/c++/TextLine.hpp new file mode 100644 index 0000000..e559e5c --- /dev/null +++ b/nitrogen/generated/shared/c++/TextLine.hpp @@ -0,0 +1,119 @@ +/// +/// TextLine.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `BoundingBox` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BoundingBox; } +// Forward declaration of `Corner` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct Corner; } +// Forward declaration of `TextElement` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextElement; } + +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include +#include +#include +#include +#include +#include "TextElement.hpp" + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (TextLine). + */ + struct TextLine final { + public: + BoundingBox bounds SWIFT_PRIVATE; + std::vector corners SWIFT_PRIVATE; + std::optional> confidence SWIFT_PRIVATE; + std::optional> angle SWIFT_PRIVATE; + std::vector languages SWIFT_PRIVATE; + std::string text SWIFT_PRIVATE; + std::vector elements SWIFT_PRIVATE; + + public: + TextLine() = default; + explicit TextLine(BoundingBox bounds, std::vector corners, std::optional> confidence, std::optional> angle, std::vector languages, std::string text, std::vector elements): bounds(bounds), corners(corners), confidence(confidence), angle(angle), languages(languages), text(text), elements(elements) {} + + public: + friend bool operator==(const TextLine& lhs, const TextLine& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ TextLine <> JS TextLine (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::TextLine fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::TextLine( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bounds"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "corners"))), + JSIConverter>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "confidence"))), + JSIConverter>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "angle"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "languages"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "text"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "elements"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::TextLine& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "bounds"), JSIConverter::toJSI(runtime, arg.bounds)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "corners"), JSIConverter>::toJSI(runtime, arg.corners)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "confidence"), JSIConverter>>::toJSI(runtime, arg.confidence)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "angle"), JSIConverter>>::toJSI(runtime, arg.angle)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "languages"), JSIConverter>::toJSI(runtime, arg.languages)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "text"), JSIConverter::toJSI(runtime, arg.text)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "elements"), JSIConverter>::toJSI(runtime, arg.elements)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bounds")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "corners")))) return false; + if (!JSIConverter>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "confidence")))) return false; + if (!JSIConverter>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "angle")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "languages")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "text")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "elements")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/TextRecognitionLanguage.hpp b/nitrogen/generated/shared/c++/TextRecognitionLanguage.hpp new file mode 100644 index 0000000..8aa38de --- /dev/null +++ b/nitrogen/generated/shared/c++/TextRecognitionLanguage.hpp @@ -0,0 +1,88 @@ +/// +/// TextRecognitionLanguage.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +namespace margelo::nitro::visioncameramlkit { + + /** + * An enum which can be represented as a JavaScript union (TextRecognitionLanguage). + */ + enum class TextRecognitionLanguage { + LATIN SWIFT_NAME(latin) = 0, + CHINESE SWIFT_NAME(chinese) = 1, + DEVANAGARI SWIFT_NAME(devanagari) = 2, + JAPANESE SWIFT_NAME(japanese) = 3, + KOREAN SWIFT_NAME(korean) = 4, + } CLOSED_ENUM; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ TextRecognitionLanguage <> JS TextRecognitionLanguage (union) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::TextRecognitionLanguage fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + std::string unionValue = JSIConverter::fromJSI(runtime, arg); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("LATIN"): return margelo::nitro::visioncameramlkit::TextRecognitionLanguage::LATIN; + case hashString("CHINESE"): return margelo::nitro::visioncameramlkit::TextRecognitionLanguage::CHINESE; + case hashString("DEVANAGARI"): return margelo::nitro::visioncameramlkit::TextRecognitionLanguage::DEVANAGARI; + case hashString("JAPANESE"): return margelo::nitro::visioncameramlkit::TextRecognitionLanguage::JAPANESE; + case hashString("KOREAN"): return margelo::nitro::visioncameramlkit::TextRecognitionLanguage::KOREAN; + default: [[unlikely]] + throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum TextRecognitionLanguage - invalid value!"); + } + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::visioncameramlkit::TextRecognitionLanguage arg) { + switch (arg) { + case margelo::nitro::visioncameramlkit::TextRecognitionLanguage::LATIN: return JSIConverter::toJSI(runtime, "LATIN"); + case margelo::nitro::visioncameramlkit::TextRecognitionLanguage::CHINESE: return JSIConverter::toJSI(runtime, "CHINESE"); + case margelo::nitro::visioncameramlkit::TextRecognitionLanguage::DEVANAGARI: return JSIConverter::toJSI(runtime, "DEVANAGARI"); + case margelo::nitro::visioncameramlkit::TextRecognitionLanguage::JAPANESE: return JSIConverter::toJSI(runtime, "JAPANESE"); + case margelo::nitro::visioncameramlkit::TextRecognitionLanguage::KOREAN: return JSIConverter::toJSI(runtime, "KOREAN"); + default: [[unlikely]] + throw std::invalid_argument("Cannot convert TextRecognitionLanguage to JS - invalid value: " + + std::to_string(static_cast(arg)) + "!"); + } + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isString()) { + return false; + } + std::string unionValue = JSIConverter::fromJSI(runtime, value); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("LATIN"): + case hashString("CHINESE"): + case hashString("DEVANAGARI"): + case hashString("JAPANESE"): + case hashString("KOREAN"): + return true; + default: + return false; + } + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/TextRecognitionResult.hpp b/nitrogen/generated/shared/c++/TextRecognitionResult.hpp new file mode 100644 index 0000000..45a5d9d --- /dev/null +++ b/nitrogen/generated/shared/c++/TextRecognitionResult.hpp @@ -0,0 +1,90 @@ +/// +/// TextRecognitionResult.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `TextBlock` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct TextBlock; } + +#include +#include "TextBlock.hpp" +#include + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (TextRecognitionResult). + */ + struct TextRecognitionResult final { + public: + std::string text SWIFT_PRIVATE; + std::vector blocks SWIFT_PRIVATE; + + public: + TextRecognitionResult() = default; + explicit TextRecognitionResult(std::string text, std::vector blocks): text(text), blocks(blocks) {} + + public: + friend bool operator==(const TextRecognitionResult& lhs, const TextRecognitionResult& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ TextRecognitionResult <> JS TextRecognitionResult (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::TextRecognitionResult fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::TextRecognitionResult( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "text"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "blocks"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::TextRecognitionResult& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "text"), JSIConverter::toJSI(runtime, arg.text)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "blocks"), JSIConverter>::toJSI(runtime, arg.blocks)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "text")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "blocks")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/TextRecognizerOptions.hpp b/nitrogen/generated/shared/c++/TextRecognizerOptions.hpp new file mode 100644 index 0000000..ad501f2 --- /dev/null +++ b/nitrogen/generated/shared/c++/TextRecognizerOptions.hpp @@ -0,0 +1,111 @@ +/// +/// TextRecognizerOptions.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `TextRecognitionLanguage` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class TextRecognitionLanguage; } +// Forward declaration of `RegionOfInterest` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct RegionOfInterest; } +// Forward declaration of `Orientation` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { enum class Orientation; } + +#include "TextRecognitionLanguage.hpp" +#include +#include "RegionOfInterest.hpp" +#include "Orientation.hpp" + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (TextRecognizerOptions). + */ + struct TextRecognizerOptions final { + public: + std::optional language SWIFT_PRIVATE; + std::optional scaleFactor SWIFT_PRIVATE; + std::optional invertColors SWIFT_PRIVATE; + std::optional frameProcessInterval SWIFT_PRIVATE; + std::optional roi SWIFT_PRIVATE; + std::optional orientation SWIFT_PRIVATE; + + public: + TextRecognizerOptions() = default; + explicit TextRecognizerOptions(std::optional language, std::optional scaleFactor, std::optional invertColors, std::optional frameProcessInterval, std::optional roi, std::optional orientation): language(language), scaleFactor(scaleFactor), invertColors(invertColors), frameProcessInterval(frameProcessInterval), roi(roi), orientation(orientation) {} + + public: + friend bool operator==(const TextRecognizerOptions& lhs, const TextRecognizerOptions& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ TextRecognizerOptions <> JS TextRecognizerOptions (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::TextRecognizerOptions fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::TextRecognizerOptions( + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "language"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "scaleFactor"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "invertColors"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "frameProcessInterval"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "roi"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "orientation"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::TextRecognizerOptions& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "language"), JSIConverter>::toJSI(runtime, arg.language)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "scaleFactor"), JSIConverter>::toJSI(runtime, arg.scaleFactor)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "invertColors"), JSIConverter>::toJSI(runtime, arg.invertColors)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "frameProcessInterval"), JSIConverter>::toJSI(runtime, arg.frameProcessInterval)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "roi"), JSIConverter>::toJSI(runtime, arg.roi)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "orientation"), JSIConverter>::toJSI(runtime, arg.orientation)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "language")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "scaleFactor")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "invertColors")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "frameProcessInterval")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "roi")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "orientation")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/TextSymbol.hpp b/nitrogen/generated/shared/c++/TextSymbol.hpp new file mode 100644 index 0000000..a5a8b15 --- /dev/null +++ b/nitrogen/generated/shared/c++/TextSymbol.hpp @@ -0,0 +1,109 @@ +/// +/// TextSymbol.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `BoundingBox` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct BoundingBox; } +// Forward declaration of `Corner` to properly resolve imports. +namespace margelo::nitro::visioncameramlkit { struct Corner; } + +#include "BoundingBox.hpp" +#include "Corner.hpp" +#include +#include + +namespace margelo::nitro::visioncameramlkit { + + /** + * A struct which can be represented as a JavaScript object (TextSymbol). + */ + struct TextSymbol final { + public: + BoundingBox bounds SWIFT_PRIVATE; + std::vector corners SWIFT_PRIVATE; + double confidence SWIFT_PRIVATE; + double angle SWIFT_PRIVATE; + std::vector languages SWIFT_PRIVATE; + std::string text SWIFT_PRIVATE; + + public: + TextSymbol() = default; + explicit TextSymbol(BoundingBox bounds, std::vector corners, double confidence, double angle, std::vector languages, std::string text): bounds(bounds), corners(corners), confidence(confidence), angle(angle), languages(languages), text(text) {} + + public: + friend bool operator==(const TextSymbol& lhs, const TextSymbol& rhs) = default; + }; + +} // namespace margelo::nitro::visioncameramlkit + +namespace margelo::nitro { + + // C++ TextSymbol <> JS TextSymbol (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::visioncameramlkit::TextSymbol fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::visioncameramlkit::TextSymbol( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bounds"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "corners"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "confidence"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "angle"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "languages"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "text"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::visioncameramlkit::TextSymbol& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "bounds"), JSIConverter::toJSI(runtime, arg.bounds)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "corners"), JSIConverter>::toJSI(runtime, arg.corners)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "confidence"), JSIConverter::toJSI(runtime, arg.confidence)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "angle"), JSIConverter::toJSI(runtime, arg.angle)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "languages"), JSIConverter>::toJSI(runtime, arg.languages)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "text"), JSIConverter::toJSI(runtime, arg.text)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bounds")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "corners")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "confidence")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "angle")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "languages")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "text")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/package.json b/package.json index e0def84..454da80 100644 --- a/package.json +++ b/package.json @@ -27,10 +27,13 @@ "react-native.config.js", "!ios/build", "!android/build", + "!android/app/build", "!android/gradle", "!android/gradlew", "!android/gradlew.bat", "!android/local.properties", + "!**/*.test.*", + "!**/*.spec.*", "!**/__tests__", "!**/__fixtures__", "!**/__mocks__", @@ -128,6 +131,10 @@ "workspaces": [ "example" ], + "resolutions": { + "@babel/plugin-transform-modules-systemjs": "7.29.8", + "undici": "6.28.0" + }, "packageManager": "yarn@4.11.0", "react-native-builder-bob": { "source": "src", @@ -147,14 +154,6 @@ ] ] }, - "codegenConfig": { - "name": "VisionCameraMLKitSpec", - "type": "modules", - "jsSrcsDir": "src", - "android": { - "javaPackageName": "com.visioncameramlkit" - } - }, "prettier": { "quoteProps": "consistent", "singleQuote": true, @@ -191,7 +190,29 @@ "modulePathIgnorePatterns": [ "/example/node_modules", "/lib/" - ] + ], + "collectCoverageFrom": [ + "src/**/*.{ts,tsx}", + "plugin/**/*.js", + "!src/**/*.test.{ts,tsx}", + "!src/**/__tests__/**", + "!src/**/types.ts", + "!src/specs/**", + "!plugin/**/__tests__/**" + ], + "coverageThreshold": { + "global": { + "branches": 80, + "functions": 80, + "lines": 80, + "statements": 80 + } + } + }, + "codegenConfig": { + "name": "VisionCameraMLKitSpec", + "type": "modules", + "jsSrcsDir": "src/specs" }, "create-react-native-library": { "languages": "kotlin-objc", diff --git a/react-native.config.js b/react-native.config.js new file mode 100644 index 0000000..21645be --- /dev/null +++ b/react-native.config.js @@ -0,0 +1,16 @@ +// https://github.com/react-native-community/cli/blob/main/docs/dependencies.md + +module.exports = { + dependency: { + platforms: { + /** + * @type {import('@react-native-community/cli-types').IOSDependencyParams} + */ + ios: {}, + /** + * @type {import('@react-native-community/cli-types').AndroidDependencyParams} + */ + android: {}, + }, + }, +}; diff --git a/src/VisionCameraMLKitSpec.ts b/src/VisionCameraMLKitSpec.ts deleted file mode 100644 index a4ea67c..0000000 --- a/src/VisionCameraMLKitSpec.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { TurboModule } from 'react-native'; -import { TurboModuleRegistry } from 'react-native'; - -export interface Spec extends TurboModule { - processImage(feature: string, path: string, options: object): Promise; -} - -export default TurboModuleRegistry.getEnforcing( - 'VisionCameraMLKitModule' -); diff --git a/src/__tests__/NativeBridge.test.ts b/src/__tests__/NativeBridge.test.ts deleted file mode 100644 index dfeb783..0000000 --- a/src/__tests__/NativeBridge.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { MLKIT_FEATURE_KEYS } from '../core/constants'; -import type { MLKitFeature } from '../core/types'; -import { INVALID_URI_ERROR, MISSING_MODULE_ERROR } from '../shared/constants'; - -const feature: MLKitFeature = MLKIT_FEATURE_KEYS.TEXT_RECOGNITION; -const createBridge = ( - platform: 'ios' | 'android', - moduleMock: { processImage?: jest.Mock } | undefined -) => { - jest.resetModules(); - jest.doMock('react-native', () => ({ - Platform: { - OS: platform, - select: (options: Record) => - options[platform] ?? options.default, - }, - })); - jest.doMock('../VisionCameraMLKitSpec', () => ({ - __esModule: true, - default: moduleMock, - })); - - return require('../core/NativeBridge') - .NativeBridge as typeof import('../core/NativeBridge').NativeBridge; -}; - -describe('NativeBridge', () => { - afterEach(() => { - jest.clearAllMocks(); - jest.resetModules(); - }); - - it('throws when the native module is missing', async () => { - const NativeBridge = createBridge('ios', undefined); - - await expect( - NativeBridge.processImage(feature, 'file://image.jpg') - ).rejects.toThrow(MISSING_MODULE_ERROR); - }); - - it('rejects invalid image URIs', async () => { - const processImage = jest.fn().mockResolvedValue({}); - const NativeBridge = createBridge('android', { processImage }); - - await expect(NativeBridge.processImage(feature, ' ')).rejects.toThrow( - INVALID_URI_ERROR - ); - expect(processImage).not.toHaveBeenCalled(); - }); - - it('strips file prefixes on iOS', async () => { - const result = { ok: true }; - const processImage = jest.fn().mockResolvedValue(result); - const NativeBridge = createBridge('ios', { processImage }); - const response = await NativeBridge.processImage( - feature, - 'file://path/to/image.jpg', - { orientation: 'portrait' } - ); - - expect(processImage).toHaveBeenCalledWith(feature, 'path/to/image.jpg', { - orientation: 'portrait', - }); - expect(response).toBe(result); - }); - - it('adds file prefixes on Android for bare paths', async () => { - const processImage = jest.fn().mockResolvedValue({}); - const NativeBridge = createBridge('android', { processImage }); - - await NativeBridge.processImage(feature, '/tmp/image.jpg'); - - expect(processImage).toHaveBeenCalledWith( - feature, - 'file:///tmp/image.jpg', - {} - ); - }); -}); diff --git a/src/__tests__/PluginFactory.test.ts b/src/__tests__/PluginFactory.test.ts new file mode 100644 index 0000000..36ab917 --- /dev/null +++ b/src/__tests__/PluginFactory.test.ts @@ -0,0 +1,86 @@ +import { MLKIT_FEATURE_KEYS } from '../core/constants'; + +const loadPluginFactory = () => { + jest.resetModules(); + const isFeatureAvailable = jest.fn(() => true); + + jest.doMock('react-native', () => ({ + Platform: { + OS: 'ios', + select: (options: Record) => + options.ios ?? options.default, + }, + })); + jest.doMock('../core/VisionCameraMLKit', () => ({ + VisionCameraMLKit: { + isFeatureAvailable, + createTextRecognizer: jest.fn(), + createBarcodeScanner: jest.fn(), + }, + })); + + return { + PluginFactory: require('../core/PluginFactory') + .PluginFactory as typeof import('../core/PluginFactory').PluginFactory, + isFeatureAvailable, + }; +}; + +describe('PluginFactory.initPlugin', () => { + afterEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + }); + + it.each([null, [], 'not-an-object', 42])( + 'rejects invalid options before reading roi: %p', + (options) => { + const { PluginFactory, isFeatureAvailable } = loadPluginFactory(); + + expect(() => + PluginFactory.initPlugin(MLKIT_FEATURE_KEYS.TEXT_RECOGNITION, options) + ).toThrow('Invalid MLKit plugin options: expected a plain object.'); + expect(isFeatureAvailable).toHaveBeenCalledWith( + MLKIT_FEATURE_KEYS.TEXT_RECOGNITION + ); + } + ); + + it.each([ + { scaleFactor: Number.NaN }, + { scaleFactor: Number.POSITIVE_INFINITY }, + { scaleFactor: '0.9' }, + { invertColors: 'true' }, + { frameProcessInterval: -1 }, + { frameProcessInterval: 1.5 }, + { orientation: 'sideways' }, + ])('rejects invalid base options: %p', (options) => { + const { PluginFactory } = loadPluginFactory(); + + expect(() => + PluginFactory.initPlugin(MLKIT_FEATURE_KEYS.TEXT_RECOGNITION, options) + ).toThrow('Invalid MLKit plugin option'); + }); + + it('rejects invalid text-recognition language values', () => { + const { PluginFactory } = loadPluginFactory(); + + expect(() => + PluginFactory.initPlugin(MLKIT_FEATURE_KEYS.TEXT_RECOGNITION, { + language: 'KLINGON', + }) + ).toThrow('Invalid MLKit plugin option "language".'); + }); + + it.each([ + { formats: 'QR_CODE' }, + { formats: ['NOT_A_FORMAT'] }, + { enableAllPotentialBarcodes: 1 }, + ])('rejects invalid barcode options: %p', (options) => { + const { PluginFactory } = loadPluginFactory(); + + expect(() => + PluginFactory.initPlugin(MLKIT_FEATURE_KEYS.BARCODE_SCANNING, options) + ).toThrow('Invalid MLKit plugin option'); + }); +}); diff --git a/src/__tests__/frameProcessingHooks.test.ts b/src/__tests__/frameProcessingHooks.test.ts new file mode 100644 index 0000000..4c0fe51 --- /dev/null +++ b/src/__tests__/frameProcessingHooks.test.ts @@ -0,0 +1,94 @@ +import type { Frame } from 'react-native-vision-camera'; +import { MLKIT_FEATURE_KEYS } from '../core/constants'; +import type { TextRecognitionResult } from '../features/text-recognition/types'; +import type { BarcodeScanningResult } from '../features/barcode-scanning/types'; + +const loadFrameProcessingHooks = () => { + jest.resetModules(); + + const textResult: TextRecognitionResult = { + text: 'Olá, 世界', + blocks: [], + }; + const barcodeResult: BarcodeScanningResult = { barcodes: [] }; + const recognizeText = jest.fn(() => textResult); + const recognizeBarcodes = jest.fn(() => barcodeResult); + const useMLKitPlugin = jest.fn((feature: string) => + feature === MLKIT_FEATURE_KEYS.TEXT_RECOGNITION + ? { recognize: recognizeText } + : { recognize: recognizeBarcodes } + ); + const useMemo = jest.fn((factory: () => unknown) => factory()); + + jest.doMock('react', () => ({ useMemo })); + jest.doMock('../hooks/useMLKitPlugin', () => ({ useMLKitPlugin })); + jest.doMock('../core/PluginFactory', () => ({ + PluginFactory: { initPlugin: jest.fn() }, + })); + + return { + useTextRecognition: require('../features/text-recognition') + .useTextRecognition as typeof import('../features/text-recognition').useTextRecognition, + useBarcodeScanning: require('../features/barcode-scanning') + .useBarcodeScanning as typeof import('../features/barcode-scanning').useBarcodeScanning, + recognizeText, + recognizeBarcodes, + textResult, + barcodeResult, + useMLKitPlugin, + useMemo, + }; +}; + +describe('frame processing hooks', () => { + afterEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + }); + + it('creates a text recognizer and forwards frame-processing arguments', () => { + const { + useTextRecognition, + recognizeText, + textResult, + useMLKitPlugin, + useMemo, + } = loadFrameProcessingHooks(); + const options = { language: 'LATIN' as const }; + const frame = { width: 1920, height: 1080 } as Frame; + const args = { outputOrientation: 'landscape-right' as const }; + + const plugin = useTextRecognition(options); + const result = plugin.textRecognition(frame, args); + + expect(useMLKitPlugin).toHaveBeenCalledWith( + MLKIT_FEATURE_KEYS.TEXT_RECOGNITION, + options + ); + expect(useMemo).toHaveBeenCalledWith(expect.any(Function), [ + { recognize: recognizeText }, + ]); + expect(recognizeText).toHaveBeenCalledWith(frame, args); + expect(result).toBe(textResult); + }); + + it('creates a barcode scanner with defaults and supplies empty arguments', () => { + const { + useBarcodeScanning, + recognizeBarcodes, + barcodeResult, + useMLKitPlugin, + } = loadFrameProcessingHooks(); + const frame = { width: 640, height: 480 } as Frame; + + const plugin = useBarcodeScanning(); + const result = plugin.barcodeScanning(frame); + + expect(useMLKitPlugin).toHaveBeenCalledWith( + MLKIT_FEATURE_KEYS.BARCODE_SCANNING, + {} + ); + expect(recognizeBarcodes).toHaveBeenCalledWith(frame, {}); + expect(result).toBe(barcodeResult); + }); +}); diff --git a/src/__tests__/staticImageNitro.test.ts b/src/__tests__/staticImageNitro.test.ts new file mode 100644 index 0000000..1f22644 --- /dev/null +++ b/src/__tests__/staticImageNitro.test.ts @@ -0,0 +1,259 @@ +import type { + TextRecognitionImageOptions, + TextRecognitionResult, +} from '../features/text-recognition'; +import type { BarcodeScanningImageOptions } from '../features/barcode-scanning'; +import type { BarcodeScannerResult } from '../specs/BarcodeScannerTypes'; + +type StaticImageApis = Pick< + typeof import('../features/text-recognition') & + typeof import('../features/barcode-scanning'), + 'processImageTextRecognition' | 'processImageBarcodeScanning' +>; + +type StaticTextRecognizer = { + recognizeImage(uri: string): Promise; + dispose(): void; +}; + +type StaticBarcodeScanner = { + recognizeImage(uri: string): Promise; + dispose(): void; +}; + +type StaticNitroRoot = { + isFeatureAvailable(feature: string): boolean; + createTextRecognizer( + options: TextRecognitionImageOptions + ): StaticTextRecognizer; + createBarcodeScanner( + options: BarcodeScanningImageOptions + ): StaticBarcodeScanner; +}; + +const loadStaticImageApis = () => { + jest.resetModules(); + + const recognizeTextImage = jest.fn< + Promise, + [uri: string] + >(); + const recognizeBarcodeImage = jest.fn< + Promise, + [uri: string] + >(); + const disposeTextRecognizer = jest.fn(); + const disposeBarcodeScanner = jest.fn(); + const textRecognizer: StaticTextRecognizer = { + recognizeImage: recognizeTextImage, + dispose: disposeTextRecognizer, + }; + const barcodeScanner: StaticBarcodeScanner = { + recognizeImage: recognizeBarcodeImage, + dispose: disposeBarcodeScanner, + }; + const staticNitroRoot = { + isFeatureAvailable: jest.fn(() => true), + createTextRecognizer: jest.fn< + StaticTextRecognizer, + [options: TextRecognitionImageOptions] + >(() => textRecognizer), + createBarcodeScanner: jest.fn< + StaticBarcodeScanner, + [options: BarcodeScanningImageOptions] + >(() => barcodeScanner), + } satisfies StaticNitroRoot; + const { createTextRecognizer, createBarcodeScanner } = staticNitroRoot; + const legacyNativeModuleAccess = jest.fn(() => { + throw new Error( + 'Static image processing must not use a legacy native module' + ); + }); + + jest.doMock('../core/VisionCameraMLKit', () => ({ + __esModule: true, + VisionCameraMLKit: staticNitroRoot, + })); + jest.doMock('react-native', () => ({ + Platform: { + OS: 'ios', + select: (options: Record) => + options.ios ?? options.default, + }, + NativeModules: new Proxy( + {}, + { + get: legacyNativeModuleAccess, + } + ), + TurboModuleRegistry: { + getEnforcing: legacyNativeModuleAccess, + }, + })); + + const textRecognition = require('../features/text-recognition') as Pick< + StaticImageApis, + 'processImageTextRecognition' + >; + const barcodeScanning = require('../features/barcode-scanning') as Pick< + StaticImageApis, + 'processImageBarcodeScanning' + >; + + return { + ...textRecognition, + ...barcodeScanning, + createTextRecognizer, + createBarcodeScanner, + recognizeTextImage, + recognizeBarcodeImage, + disposeTextRecognizer, + disposeBarcodeScanner, + legacyNativeModuleAccess, + }; +}; + +describe('static image recognition through Nitro', () => { + afterEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + }); + + it('creates a configured text recognizer and recognizes the image URI', async () => { + const { + processImageTextRecognition, + createTextRecognizer, + recognizeTextImage, + disposeTextRecognizer, + legacyNativeModuleAccess, + } = loadStaticImageApis(); + const result: TextRecognitionResult = { text: 'Olá, 世界', blocks: [] }; + const options: TextRecognitionImageOptions = { + language: 'JAPANESE', + orientation: 'landscape-right', + scaleFactor: 0.9, + invertColors: true, + roi: { + x: 0.1, + y: 0.2, + width: 0.5, + height: 0.4, + unit: 'normalized', + }, + }; + recognizeTextImage.mockResolvedValue(result); + + const response = await processImageTextRecognition( + 'file:///tmp/receipt-日本語.jpg', + options + ); + + expect(createTextRecognizer).toHaveBeenCalledWith(options); + expect(recognizeTextImage).toHaveBeenCalledWith( + 'file:///tmp/receipt-日本語.jpg' + ); + expect(legacyNativeModuleAccess).not.toHaveBeenCalled(); + expect(disposeTextRecognizer).toHaveBeenCalledTimes(1); + expect(response).toBe(result); + }); + + it('creates a configured barcode scanner and recognizes the image URI', async () => { + const { + processImageBarcodeScanning, + createBarcodeScanner, + recognizeBarcodeImage, + disposeBarcodeScanner, + legacyNativeModuleAccess, + } = loadStaticImageApis(); + const result: BarcodeScannerResult = { barcodes: [] }; + const options: BarcodeScanningImageOptions = { + formats: ['QR_CODE', 'EAN_13'], + enableAllPotentialBarcodes: true, + orientation: 'portrait', + scaleFactor: 0.95, + invertColors: true, + roi: { x: 20, y: 40, width: 200, height: 100, unit: 'pixel' }, + }; + recognizeBarcodeImage.mockResolvedValue(result); + + const response = await processImageBarcodeScanning( + 'file:///tmp/qr-code.png', + options + ); + + expect(createBarcodeScanner).toHaveBeenCalledWith(options); + expect(recognizeBarcodeImage).toHaveBeenCalledWith( + 'file:///tmp/qr-code.png' + ); + expect(legacyNativeModuleAccess).not.toHaveBeenCalled(); + expect(disposeBarcodeScanner).toHaveBeenCalledTimes(1); + expect(response).toEqual(result); + }); + + it('configures both recognizers with empty defaults when options are omitted', async () => { + const { + processImageTextRecognition, + processImageBarcodeScanning, + createTextRecognizer, + createBarcodeScanner, + recognizeTextImage, + recognizeBarcodeImage, + disposeTextRecognizer, + disposeBarcodeScanner, + legacyNativeModuleAccess, + } = loadStaticImageApis(); + recognizeTextImage.mockResolvedValue({ text: '', blocks: [] }); + recognizeBarcodeImage.mockResolvedValue({ barcodes: [] }); + + await processImageTextRecognition('file:///tmp/empty-text.png'); + await processImageBarcodeScanning('file:///tmp/empty-barcode.png'); + + expect(createTextRecognizer).toHaveBeenCalledWith({}); + expect(createBarcodeScanner).toHaveBeenCalledWith({}); + expect(recognizeTextImage).toHaveBeenCalledWith( + 'file:///tmp/empty-text.png' + ); + expect(recognizeBarcodeImage).toHaveBeenCalledWith( + 'file:///tmp/empty-barcode.png' + ); + expect(legacyNativeModuleAccess).not.toHaveBeenCalled(); + expect(disposeTextRecognizer).toHaveBeenCalledTimes(1); + expect(disposeBarcodeScanner).toHaveBeenCalledTimes(1); + }); + + it('propagates asynchronous recognizer errors without falling back to the legacy bridge', async () => { + const { + processImageTextRecognition, + recognizeTextImage, + disposeTextRecognizer, + legacyNativeModuleAccess, + } = loadStaticImageApis(); + const failure = new Error('The image could not be read'); + recognizeTextImage.mockRejectedValue(failure); + + await expect( + processImageTextRecognition('file:///tmp/unreadable.jpg') + ).rejects.toBe(failure); + + expect(legacyNativeModuleAccess).not.toHaveBeenCalled(); + expect(disposeTextRecognizer).toHaveBeenCalledTimes(1); + }); + + it('propagates asynchronous barcode scanner errors without falling back to the legacy bridge', async () => { + const { + processImageBarcodeScanning, + recognizeBarcodeImage, + disposeBarcodeScanner, + legacyNativeModuleAccess, + } = loadStaticImageApis(); + const failure = new Error('The barcode image could not be read'); + recognizeBarcodeImage.mockRejectedValue(failure); + + await expect( + processImageBarcodeScanning('file:///tmp/unreadable-barcode.jpg') + ).rejects.toBe(failure); + + expect(legacyNativeModuleAccess).not.toHaveBeenCalled(); + expect(disposeBarcodeScanner).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/__tests__/useMLKitPlugin.test.ts b/src/__tests__/useMLKitPlugin.test.ts new file mode 100644 index 0000000..b0e58e9 --- /dev/null +++ b/src/__tests__/useMLKitPlugin.test.ts @@ -0,0 +1,57 @@ +import { MLKIT_FEATURE_KEYS } from '../core/constants'; + +const loadUseMLKitPlugin = () => { + jest.resetModules(); + + const initPlugin = jest.fn(); + const useMemo = jest.fn((factory: () => unknown) => factory()); + + jest.doMock('react', () => ({ useMemo })); + jest.doMock('../core/PluginFactory', () => ({ + PluginFactory: { initPlugin }, + })); + + return { + useMLKitPlugin: require('../hooks/useMLKitPlugin') + .useMLKitPlugin as typeof import('../hooks/useMLKitPlugin').useMLKitPlugin, + initPlugin, + useMemo, + }; +}; + +describe('useMLKitPlugin', () => { + afterEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + }); + + it('initializes the requested plugin with feature and options dependencies', () => { + const { useMLKitPlugin, initPlugin, useMemo } = loadUseMLKitPlugin(); + const plugin = { recognize: jest.fn() }; + const options = { scaleFactor: 0.9 }; + initPlugin.mockReturnValue(plugin); + + const result = useMLKitPlugin(MLKIT_FEATURE_KEYS.TEXT_RECOGNITION, options); + + expect(initPlugin).toHaveBeenCalledWith( + MLKIT_FEATURE_KEYS.TEXT_RECOGNITION, + options + ); + expect(useMemo).toHaveBeenCalledWith(expect.any(Function), [ + MLKIT_FEATURE_KEYS.TEXT_RECOGNITION, + options, + ]); + expect(result).toBe(plugin); + }); + + it('uses empty options when the caller omits them', () => { + const { useMLKitPlugin, initPlugin } = loadUseMLKitPlugin(); + + useMLKitPlugin(MLKIT_FEATURE_KEYS.BARCODE_SCANNING); + + expect(initPlugin).toHaveBeenCalledWith( + MLKIT_FEATURE_KEYS.BARCODE_SCANNING, + {} + ); + }); +}); diff --git a/src/__tests__/validateRegionOfInterest.test.ts b/src/__tests__/validateRegionOfInterest.test.ts index 4ce81f4..5ae5405 100644 --- a/src/__tests__/validateRegionOfInterest.test.ts +++ b/src/__tests__/validateRegionOfInterest.test.ts @@ -2,6 +2,60 @@ import { validateRegionOfInterest } from '../core/validateRegionOfInterest'; import type { RegionOfInterest } from '../core/types'; describe('validateRegionOfInterest', () => { + it.each([null, [], 'not-an-object', 42])( + 'rejects a non-object ROI: %p', + (roi) => { + expect(() => validateRegionOfInterest(roi)).toThrow( + 'Invalid RegionOfInterest: expected a plain object.' + ); + } + ); + + it.each(['x', 'y', 'width', 'height'] as const)( + 'rejects an ROI missing its own %s field', + (missingField) => { + const roi = { x: 0, y: 0, width: 1, height: 1 }; + delete roi[missingField]; + + expect(() => validateRegionOfInterest(roi)).toThrow( + 'Invalid RegionOfInterest: x, y, width, and height must be own finite numbers.' + ); + } + ); + + it.each([ + { x: Number.NaN, y: 0, width: 1, height: 1 }, + { x: 0, y: Number.POSITIVE_INFINITY, width: 1, height: 1 }, + { x: 0, y: 0, width: Number.NEGATIVE_INFINITY, height: 1 }, + { x: 0, y: 0, width: 1, height: Number.NaN }, + ])('rejects non-finite ROI dimensions: %p', (roi) => { + expect(() => validateRegionOfInterest(roi)).toThrow( + 'Invalid RegionOfInterest: x, y, width, and height must be own finite numbers.' + ); + }); + + it('rejects an unsupported ROI unit', () => { + expect(() => + validateRegionOfInterest({ + x: 0, + y: 0, + width: 1, + height: 1, + unit: 'view', + }) + ).toThrow( + 'Invalid RegionOfInterest: unit must be "normalized" or "pixel" when provided.' + ); + }); + + it('rejects fields inherited from another object', () => { + const inheritedRoi = Object.create({ x: 0, y: 0, width: 1, height: 1 }); + + expect(() => validateRegionOfInterest(inheritedRoi)).toThrow( + 'Invalid RegionOfInterest: expected a plain object.' + ); + }); + it('accepts a valid normalized region', () => { expect(() => validateRegionOfInterest({ x: 0.25, y: 0.25, width: 0.5, height: 0.5 }) diff --git a/src/core/NativeBridge.ts b/src/core/NativeBridge.ts deleted file mode 100644 index 26f8e58..0000000 --- a/src/core/NativeBridge.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Platform } from 'react-native'; -import VisionCameraMLKitModule from '../VisionCameraMLKitSpec'; -import { INVALID_URI_ERROR, MISSING_MODULE_ERROR } from '../shared/constants'; -import { validateRegionOfInterest } from './validateRegionOfInterest'; -import type { ImageProcessingBaseOptions, MLKitFeature } from './types'; - -/** - * Bridge class for native MLKit image processing - */ -export class NativeBridge { - /** - * Process a static image using the specified MLKit feature. - * @param {MLKitFeature} feature - The MLKit feature to use. - * @param {string} uri - The file path or URI of the image. - * @param {object} options - Additional options for processing. - * @returns {Promise} Promise resolving to the processing result. - */ - static async processImage( - feature: MLKitFeature, - uri: string, - options: ImageProcessingBaseOptions = {} - ): Promise { - if ( - !VisionCameraMLKitModule || - typeof VisionCameraMLKitModule.processImage !== 'function' - ) { - throw new Error(MISSING_MODULE_ERROR); - } - const normalizedUri = this.validateAndNormalizeUri(uri); - - if (options.roi) { - validateRegionOfInterest(options.roi); - } - - return await VisionCameraMLKitModule.processImage( - feature, - normalizedUri, - options - ); - } - - /** - * Validates and normalizes the image URI for different platforms. - * @param {string} uri - The input URI. - * @returns {string} The normalized URI. - * @throws {Error} If the URI is invalid. - */ - private static validateAndNormalizeUri(uri: string): string { - if (!uri || typeof uri !== 'string') { - throw new Error(INVALID_URI_ERROR); - } - - // Check for basic URI validity - if (uri.trim().length === 0) { - throw new Error(INVALID_URI_ERROR); - } - - // Normalize the URI for different platforms - let processUri = uri; - - if (Platform.OS === 'ios') { - // iOS: Remove file:// prefix if present - processUri = uri.replace('file://', ''); - } else { - // Android: Ensure proper file:// prefix for absolute file paths without any scheme - const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(processUri); - if (!hasScheme) { - processUri = `file://${processUri}`; - } - } - - return processUri; - } -} diff --git a/src/core/PluginFactory.ts b/src/core/PluginFactory.ts index 27c5e5f..261a0fd 100644 --- a/src/core/PluginFactory.ts +++ b/src/core/PluginFactory.ts @@ -7,6 +7,117 @@ import { validateRegionOfInterest } from './validateRegionOfInterest'; import { VisionCameraMLKit } from './VisionCameraMLKit'; import type { MLKitBaseOptions, MLKitFeature } from './types'; +const isPlainObject = (value: unknown): value is Record => { + if (typeof value !== 'object' || value === null || Array.isArray(value)) { + return false; + } + + const prototype = Object.getPrototypeOf(value); + return prototype === Object.prototype || prototype === null; +}; + +const ORIENTATIONS = new Set([ + 'portrait', + 'portrait-upside-down', + 'landscape-left', + 'landscape-right', +]); +const TEXT_LANGUAGES = new Set([ + 'LATIN', + 'CHINESE', + 'DEVANAGARI', + 'JAPANESE', + 'KOREAN', +]); +const BARCODE_FORMATS = new Set([ + 'UNKNOWN', + 'ALL_FORMATS', + 'CODE_128', + 'CODE_39', + 'CODE_93', + 'CODABAR', + 'DATA_MATRIX', + 'EAN_13', + 'EAN_8', + 'ITF', + 'QR_CODE', + 'UPC_A', + 'UPC_E', + 'PDF417', + 'AZTEC', +]); + +const hasDefinedOption = ( + options: Record, + key: string +): boolean => + Object.prototype.hasOwnProperty.call(options, key) && + options[key] !== undefined; + +const invalidOption = (key: string): never => { + throw new Error(`Invalid MLKit plugin option "${key}".`); +}; + +const validateBaseOptions = (options: Record): void => { + if ( + hasDefinedOption(options, 'scaleFactor') && + (typeof options.scaleFactor !== 'number' || + !Number.isFinite(options.scaleFactor)) + ) { + invalidOption('scaleFactor'); + } + if ( + hasDefinedOption(options, 'invertColors') && + typeof options.invertColors !== 'boolean' + ) { + invalidOption('invertColors'); + } + if ( + hasDefinedOption(options, 'frameProcessInterval') && + (typeof options.frameProcessInterval !== 'number' || + !Number.isSafeInteger(options.frameProcessInterval) || + options.frameProcessInterval < 0) + ) { + invalidOption('frameProcessInterval'); + } + if ( + hasDefinedOption(options, 'orientation') && + !ORIENTATIONS.has(options.orientation as string) + ) { + invalidOption('orientation'); + } +}; + +const validateFeatureOptions = ( + feature: MLKitFeature, + options: Record +): void => { + if ( + feature === MLKIT_FEATURE_KEYS.TEXT_RECOGNITION && + hasDefinedOption(options, 'language') && + !TEXT_LANGUAGES.has(options.language as string) + ) { + invalidOption('language'); + } + if (feature !== MLKIT_FEATURE_KEYS.BARCODE_SCANNING) return; + + if ( + hasDefinedOption(options, 'formats') && + (!Array.isArray(options.formats) || + !options.formats.every( + (format) => typeof format === 'string' && BARCODE_FORMATS.has(format) + )) + ) { + invalidOption('formats'); + } + if ( + hasDefinedOption(options, 'enableAllPotentialBarcodes') && + typeof options.enableAllPotentialBarcodes !== 'boolean' + ) { + invalidOption('enableAllPotentialBarcodes'); + } +}; + /** * Factory class for managing MLKit Vision Camera plugins */ @@ -69,24 +180,36 @@ buildscript { /** * Initializes the Vision Camera plugin for a given feature. * @param {MLKitFeature} feature - The MLKit feature to initialize. - * @param {MLKitBaseOptions} options - Options for the plugin. + * @param {unknown} options - Options for the plugin. * @returns The initialized plugin. */ - static initPlugin(feature: MLKitFeature, options: MLKitBaseOptions) { + static initPlugin(feature: MLKitFeature, options: unknown) { PluginFactory.assertFeatureAvailable(feature); - if (options.roi) { + if (!isPlainObject(options)) { + throw new Error('Invalid MLKit plugin options: expected a plain object.'); + } + + validateBaseOptions(options); + validateFeatureOptions(feature, options); + + if ( + Object.prototype.hasOwnProperty.call(options, 'roi') && + options.roi !== undefined + ) { validateRegionOfInterest(options.roi); } + const validatedOptions = options as MLKitBaseOptions; + switch (feature) { case MLKIT_FEATURE_KEYS.TEXT_RECOGNITION: return VisionCameraMLKit.createTextRecognizer( - options as TextRecognitionOptions + validatedOptions as TextRecognitionOptions ); case MLKIT_FEATURE_KEYS.BARCODE_SCANNING: return VisionCameraMLKit.createBarcodeScanner( - options as BarcodeScanningOptions + validatedOptions as BarcodeScanningOptions ); default: throw new Error(PluginFactory.getFeatureErrorMessage(feature)); diff --git a/src/core/normalizeImageUri.test.ts b/src/core/normalizeImageUri.test.ts new file mode 100644 index 0000000..9b02041 --- /dev/null +++ b/src/core/normalizeImageUri.test.ts @@ -0,0 +1,58 @@ +import { INVALID_URI_ERROR } from '../shared/constants'; + +const loadNormalizeImageUri = (platform: 'ios' | 'android') => { + jest.resetModules(); + jest.doMock('react-native', () => ({ + Platform: { + OS: platform, + select: (options: Record) => + options[platform] ?? options.default, + }, + })); + + return require('./normalizeImageUri') + .normalizeImageUri as typeof import('./normalizeImageUri').normalizeImageUri; +}; + +describe('normalizeImageUri', () => { + afterEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + }); + + it.each([undefined, null, '', ' '])('rejects an invalid URI: %p', (uri) => { + const normalizeImageUri = loadNormalizeImageUri('ios'); + + expect(() => normalizeImageUri(uri)).toThrow(INVALID_URI_ERROR); + }); + + it('preserves file URLs on iOS so native URL parsing decodes them safely', () => { + const normalizeImageUri = loadNormalizeImageUri('ios'); + + expect(normalizeImageUri('file:///tmp/My%20Image.jpg')).toBe( + 'file:///tmp/My%20Image.jpg' + ); + }); + + it('does not strip a file prefix embedded later in an iOS URI', () => { + const normalizeImageUri = loadNormalizeImageUri('ios'); + const uri = 'https://example.com/redirect?target=file://image.jpg'; + + expect(normalizeImageUri(uri)).toBe(uri); + }); + + it('adds the file prefix to Android bare paths', () => { + const normalizeImageUri = loadNormalizeImageUri('android'); + + expect(normalizeImageUri('/tmp/image.jpg')).toBe('file:///tmp/image.jpg'); + }); + + it.each(['file:///tmp/image.jpg', 'content://media/image/1'])( + 'preserves an existing Android scheme: %s', + (uri) => { + const normalizeImageUri = loadNormalizeImageUri('android'); + + expect(normalizeImageUri(uri)).toBe(uri); + } + ); +}); diff --git a/src/core/normalizeImageUri.ts b/src/core/normalizeImageUri.ts new file mode 100644 index 0000000..b1c2a81 --- /dev/null +++ b/src/core/normalizeImageUri.ts @@ -0,0 +1,18 @@ +import { Platform } from 'react-native'; +import { INVALID_URI_ERROR } from '../shared/constants'; + +/** + * Validates and normalizes an image URI for the static Nitro APIs. + */ +export const normalizeImageUri = (uri: unknown): string => { + if (typeof uri !== 'string' || uri.trim().length === 0) { + throw new Error(INVALID_URI_ERROR); + } + + if (Platform.OS === 'ios') { + return uri; + } + + const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(uri); + return hasScheme ? uri : `file://${uri}`; +}; diff --git a/src/core/types.ts b/src/core/types.ts index ff24836..6fe1717 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -110,6 +110,13 @@ export type MLKitBaseArguments = { }; export type ImageProcessingBaseOptions = { + /** + * Optional image downscaling for performance optimization. + * Clamped to the range [0.9, 1.0] to preserve ML accuracy. + * Example: 0.9 reduces width/height by ~10% (~19% fewer pixels). + * @default 1.0 + */ + scaleFactor?: number; /** * Override the image orientation for processing. * When omitted, the orientation is automatically detected from the image's diff --git a/src/core/validateRegionOfInterest.ts b/src/core/validateRegionOfInterest.ts index 2d4abba..22f2fe2 100644 --- a/src/core/validateRegionOfInterest.ts +++ b/src/core/validateRegionOfInterest.ts @@ -1,4 +1,31 @@ -import type { RegionOfInterest } from './types'; +const hasOwnProperty = (value: object, property: string): boolean => + Object.prototype.hasOwnProperty.call(value, property); + +const isPlainObject = (value: unknown): value is Record => { + if (typeof value !== 'object' || value === null || Array.isArray(value)) { + return false; + } + + const prototype = Object.getPrototypeOf(value); + return prototype === Object.prototype || prototype === null; +}; + +type RegionOfInterestDimensions = Record & { + x: number; + y: number; + width: number; + height: number; +}; + +const hasOwnFiniteDimensions = ( + roi: Record +): roi is RegionOfInterestDimensions => + ['x', 'y', 'width', 'height'].every( + (property) => + hasOwnProperty(roi, property) && + typeof roi[property] === 'number' && + Number.isFinite(roi[property]) + ); /** * Validates a {@link RegionOfInterest} before it crosses into native code. @@ -6,8 +33,25 @@ import type { RegionOfInterest } from './types'; * fail deep inside the crop step. * @throws {Error} If the region is invalid for its unit. */ -export const validateRegionOfInterest = (roi: RegionOfInterest): void => { - const { x, y, width, height, unit = 'normalized' } = roi; +export const validateRegionOfInterest = (roi: unknown): void => { + if (!isPlainObject(roi)) { + throw new Error('Invalid RegionOfInterest: expected a plain object.'); + } + + if (!hasOwnFiniteDimensions(roi)) { + throw new Error( + 'Invalid RegionOfInterest: x, y, width, and height must be own finite numbers.' + ); + } + + const unit = hasOwnProperty(roi, 'unit') ? roi.unit : undefined; + if (unit !== undefined && unit !== 'normalized' && unit !== 'pixel') { + throw new Error( + 'Invalid RegionOfInterest: unit must be "normalized" or "pixel" when provided.' + ); + } + + const { x, y, width, height } = roi; if (x < 0 || y < 0) { throw new Error( @@ -21,7 +65,10 @@ export const validateRegionOfInterest = (roi: RegionOfInterest): void => { ); } - if (unit === 'normalized' && (x + width > 1 || y + height > 1)) { + if ( + (unit === undefined || unit === 'normalized') && + (x + width > 1 || y + height > 1) + ) { throw new Error( `Invalid RegionOfInterest: normalized x + width and y + height must be <= 1 (got x=${x}, width=${width}, y=${y}, height=${height}).` ); diff --git a/src/features/barcode-scanning/index.ts b/src/features/barcode-scanning/index.ts index 520e222..8a55074 100644 --- a/src/features/barcode-scanning/index.ts +++ b/src/features/barcode-scanning/index.ts @@ -1,9 +1,11 @@ import { useMemo } from 'react'; import type { Frame } from 'react-native-vision-camera'; -import { NativeBridge } from '../../core/NativeBridge'; import { MLKIT_FEATURE_KEYS } from '../../core/constants'; +import { normalizeImageUri } from '../../core/normalizeImageUri'; +import { PluginFactory } from '../../core/PluginFactory'; import { useMLKitPlugin } from '../../hooks/useMLKitPlugin'; import type { BarcodeScanner } from '../../specs/BarcodeScanner.nitro'; +import { mapStaticBarcodeResult } from './mapStaticBarcodeResult'; import type { BarcodeScanningArguments, BarcodeScanningImageOptions, @@ -18,11 +20,18 @@ export const processImageBarcodeScanning = async ( uri: string, options: BarcodeScanningImageOptions = {} ): Promise => { - return await NativeBridge.processImage( + const scanner = PluginFactory.initPlugin( MLKIT_FEATURE_KEYS.BARCODE_SCANNING, - uri, options - ); + ) as BarcodeScanner; + + try { + return mapStaticBarcodeResult( + await scanner.recognizeImage(normalizeImageUri(uri)) + ); + } finally { + scanner.dispose(); + } }; /** diff --git a/src/features/barcode-scanning/mapStaticBarcodeResult.test.ts b/src/features/barcode-scanning/mapStaticBarcodeResult.test.ts new file mode 100644 index 0000000..70e5470 --- /dev/null +++ b/src/features/barcode-scanning/mapStaticBarcodeResult.test.ts @@ -0,0 +1,149 @@ +import type { BarcodeParsedValue, BarcodeScanningResult } from './types'; +import type { BarcodeScannerResult } from '../../specs/BarcodeScannerTypes'; +import { mapStaticBarcodeResult } from './mapStaticBarcodeResult'; + +const barcode = { + format: 256, + formatName: 'QR_CODE' as const, + valueType: 9, + valueTypeName: 'TYPE_WIFI' as const, + isPotential: false, +}; + +const parsedValueError = 'Invalid barcode parsed value'; + +const resultWithValue = ( + value: BarcodeScannerResult['barcodes'][number]['value'] +): BarcodeScannerResult => ({ + barcodes: [{ ...barcode, value }], +}); + +const validParsedValues: readonly BarcodeParsedValue[] = [ + { type: 'TYPE_UNKNOWN', data: { rawValue: 'unknown' } }, + { + type: 'TYPE_CONTACT_INFO', + data: { + name: { first: 'Ada' }, + organization: 'Analytical Engines', + phones: [{ number: '+15551234567', type: 'WORK', typeName: 'WORK' }], + emails: [{ address: 'ada@example.com', type: 1, typeName: 'WORK' }], + addresses: [{ addressLines: ['1 Logic Lane'], type: 'HOME' }], + urls: ['https://example.com'], + }, + }, + { type: 'TYPE_EMAIL', data: { address: 'ada@example.com', type: 'WORK' } }, + { type: 'TYPE_ISBN', data: { isbn: '9780132350884' } }, + { type: 'TYPE_PHONE', data: { number: '+15551234567', type: 1 } }, + { type: 'TYPE_PRODUCT', data: { product: '012345678905' } }, + { type: 'TYPE_SMS', data: { message: 'Hello', phoneNumber: '+15551234567' } }, + { type: 'TYPE_TEXT', data: { text: 'Hello' } }, + { type: 'TYPE_URL', data: { title: 'Example', url: 'https://example.com' } }, + { + type: 'TYPE_WIFI', + data: { ssid: 'guest-network', password: null, encryptionType: 2 }, + }, + { type: 'TYPE_GEO', data: { lat: -23.55052, lng: -46.633308 } }, + { + type: 'TYPE_CALENDAR_EVENT', + data: { + summary: 'Launch', + start: { year: 2026, month: 8, day: 2, isUtc: true }, + end: null, + }, + }, + { + type: 'TYPE_DRIVER_LICENSE', + data: { documentType: 'DL', licenseNumber: 'D1234567', gender: 'F' }, + }, +]; + +describe('mapStaticBarcodeResult', () => { + it.each(validParsedValues)('maps the valid $type parsed value', (value) => { + expect(mapStaticBarcodeResult(resultWithValue(value))).toEqual({ + barcodes: [ + { + ...barcode, + rawValue: null, + displayValue: null, + rawBytes: null, + value, + }, + ], + } satisfies BarcodeScanningResult); + }); + + it.each([ + ['a Wi-Fi string field', { type: 'TYPE_WIFI', data: { ssid: 7 } }], + [ + 'a calendar date-time field', + { type: 'TYPE_CALENDAR_EVENT', data: { start: { year: '2026' } } }, + ], + [ + 'a contact name field', + { type: 'TYPE_CONTACT_INFO', data: { name: { first: 7 } } }, + ], + [ + 'a contact phone field', + { type: 'TYPE_CONTACT_INFO', data: { phones: [{ number: 7 }] } }, + ], + [ + 'a contact email field', + { type: 'TYPE_CONTACT_INFO', data: { emails: [{ address: 7 }] } }, + ], + [ + 'a contact address field', + { + type: 'TYPE_CONTACT_INFO', + data: { addresses: [{ addressLines: ['1 Logic Lane', 7] }] }, + }, + ], + ])('throws for %s with the wrong type', (_description, value) => { + expect(() => mapStaticBarcodeResult(resultWithValue(value))).toThrow( + parsedValueError + ); + }); + + it.each([ + [ + 'a type/data mismatch', + { type: 'TYPE_WIFI', data: { text: 'not Wi-Fi data' } }, + ], + ['an unknown discriminator', { type: 'TYPE_CUSTOM', data: {} }], + ['a non-record payload', { type: 'TYPE_WIFI', data: null }], + ['a non-string discriminator', { type: { name: 'TYPE_WIFI' }, data: {} }], + ])('throws for %s', (_description, value) => { + expect(() => mapStaticBarcodeResult(resultWithValue(value))).toThrow( + parsedValueError + ); + }); + + it('leaves an absent parsed value absent', () => { + expect(mapStaticBarcodeResult({ barcodes: [{ ...barcode }] })).toEqual({ + barcodes: [ + { + ...barcode, + rawValue: null, + displayValue: null, + rawBytes: null, + }, + ], + }); + }); + + it('normalizes explicitly undefined static fields to the public null shape', () => { + const result: BarcodeScannerResult = { + barcodes: [{ ...barcode, rawValue: undefined, displayValue: undefined }], + }; + + expect(mapStaticBarcodeResult(result)).toEqual({ + barcodes: [ + { + ...barcode, + rawValue: null, + displayValue: null, + rawBytes: null, + }, + ], + }); + }); +}); diff --git a/src/features/barcode-scanning/mapStaticBarcodeResult.ts b/src/features/barcode-scanning/mapStaticBarcodeResult.ts new file mode 100644 index 0000000..8cf176a --- /dev/null +++ b/src/features/barcode-scanning/mapStaticBarcodeResult.ts @@ -0,0 +1,294 @@ +import type { + BarcodeAddressData, + BarcodeCalendarDateTime, + BarcodeCalendarEventData, + BarcodeContactInfoData, + BarcodeData, + BarcodeDriverLicenseData, + BarcodeEmailData, + BarcodeGeoData, + BarcodeIsbnData, + BarcodeParsedValue, + BarcodePersonName, + BarcodePhoneData, + BarcodeProductData, + BarcodeScanningResult, + BarcodeSmsData, + BarcodeTextData, + BarcodeUnknownData, + BarcodeUrlData, + BarcodeWifiData, +} from './types'; +import type { + BarcodeScannerBarcode, + BarcodeScannerResult, +} from '../../specs/BarcodeScannerTypes'; + +const parsedValueError = 'Invalid barcode parsed value'; + +type ValueValidator = (value: unknown) => boolean; + +const isRecord = (value: unknown): value is Record => + typeof value === 'object' && value !== null && !Array.isArray(value); + +const isNullableString: ValueValidator = (value) => + value === null || typeof value === 'string'; + +const isNullableNumber: ValueValidator = (value) => + value === null || typeof value === 'number'; + +const isNullableBoolean: ValueValidator = (value) => + value === null || typeof value === 'boolean'; + +const isNullableStringOrNumber: ValueValidator = (value) => + value === null || typeof value === 'string' || typeof value === 'number'; + +const isOptional = + (validator: ValueValidator): ValueValidator => + (value) => + value === undefined || validator(value); + +const isNullableRecord = + (validator: ValueValidator): ValueValidator => + (value) => + value === null || validator(value); + +const isArrayOf = + (validator: ValueValidator): ValueValidator => + (value) => + Array.isArray(value) && value.every(validator); + +const hasValidProperties = ( + value: Record, + validators: Record +): boolean => + Object.keys(value).every((key) => { + const validator = validators[key]; + return validator !== undefined && validator(value[key]); + }); + +const hasSchema = ( + value: unknown, + validators: Record +): value is Record => + isRecord(value) && hasValidProperties(value, validators); + +const isStringArray = isArrayOf((value) => typeof value === 'string'); + +const isCalendarDateTime = (value: unknown): value is BarcodeCalendarDateTime => + hasSchema(value, { + year: isOptional(isNullableNumber), + month: isOptional(isNullableNumber), + day: isOptional(isNullableNumber), + hours: isOptional(isNullableNumber), + minutes: isOptional(isNullableNumber), + seconds: isOptional(isNullableNumber), + isUtc: isOptional(isNullableBoolean), + rawValue: isOptional(isNullableString), + }); + +const isPersonName = (value: unknown): value is BarcodePersonName => + hasSchema(value, { + formattedName: isOptional(isNullableString), + pronunciation: isOptional(isNullableString), + prefix: isOptional(isNullableString), + first: isOptional(isNullableString), + middle: isOptional(isNullableString), + last: isOptional(isNullableString), + suffix: isOptional(isNullableString), + }); + +const isPhoneData = (value: unknown): value is BarcodePhoneData => + hasSchema(value, { + number: isOptional(isNullableString), + type: isOptional(isNullableStringOrNumber), + typeName: isOptional(isNullableString), + }); + +const isEmailData = (value: unknown): value is BarcodeEmailData => + hasSchema(value, { + address: isOptional(isNullableString), + subject: isOptional(isNullableString), + body: isOptional(isNullableString), + type: isOptional(isNullableStringOrNumber), + typeName: isOptional(isNullableString), + }); + +const isAddressData = (value: unknown): value is BarcodeAddressData => + hasSchema(value, { + addressLines: isOptional(isNullableRecord(isStringArray)), + type: isOptional(isNullableStringOrNumber), + typeName: isOptional(isNullableString), + }); + +const isWifiData = (value: unknown): value is BarcodeWifiData => + hasSchema(value, { + ssid: isOptional(isNullableString), + password: isOptional(isNullableString), + encryptionType: isOptional(isNullableNumber), + encryptionTypeName: isOptional(isNullableString), + type: isOptional(isNullableStringOrNumber), + typeName: isOptional(isNullableString), + }); + +const isUrlData = (value: unknown): value is BarcodeUrlData => + hasSchema(value, { + title: isOptional(isNullableString), + url: isOptional(isNullableString), + }); + +const isSmsData = (value: unknown): value is BarcodeSmsData => + hasSchema(value, { + message: isOptional(isNullableString), + phoneNumber: isOptional(isNullableString), + }); + +const isGeoData = (value: unknown): value is BarcodeGeoData => + hasSchema(value, { + lat: isOptional(isNullableNumber), + lng: isOptional(isNullableNumber), + }); + +const isCalendarEventData = ( + value: unknown +): value is BarcodeCalendarEventData => + hasSchema(value, { + summary: isOptional(isNullableString), + description: isOptional(isNullableString), + location: isOptional(isNullableString), + organizer: isOptional(isNullableString), + status: isOptional(isNullableString), + start: isOptional(isNullableRecord(isCalendarDateTime)), + end: isOptional(isNullableRecord(isCalendarDateTime)), + }); + +const isContactInfoData = (value: unknown): value is BarcodeContactInfoData => + hasSchema(value, { + name: isOptional(isNullableRecord(isPersonName)), + organization: isOptional(isNullableString), + title: isOptional(isNullableString), + phones: isOptional(isNullableRecord(isArrayOf(isPhoneData))), + emails: isOptional(isNullableRecord(isArrayOf(isEmailData))), + addresses: isOptional(isNullableRecord(isArrayOf(isAddressData))), + urls: isOptional(isNullableRecord(isStringArray)), + }); + +const isDriverLicenseData = ( + value: unknown +): value is BarcodeDriverLicenseData => + hasSchema(value, { + documentType: isOptional(isNullableString), + licenseNumber: isOptional(isNullableString), + firstName: isOptional(isNullableString), + middleName: isOptional(isNullableString), + lastName: isOptional(isNullableString), + gender: isOptional(isNullableStringOrNumber), + addressStreet: isOptional(isNullableString), + addressCity: isOptional(isNullableString), + addressState: isOptional(isNullableString), + addressZip: isOptional(isNullableString), + birthDate: isOptional(isNullableString), + issueDate: isOptional(isNullableString), + expiryDate: isOptional(isNullableString), + issuingCountry: isOptional(isNullableString), + }); + +const isTextData = (value: unknown): value is BarcodeTextData => + hasSchema(value, { text: isOptional(isNullableString) }); + +const isProductData = (value: unknown): value is BarcodeProductData => + hasSchema(value, { product: isOptional(isNullableString) }); + +const isIsbnData = (value: unknown): value is BarcodeIsbnData => + hasSchema(value, { isbn: isOptional(isNullableString) }); + +const isUnknownData = (value: unknown): value is BarcodeUnknownData => + hasSchema(value, { rawValue: isOptional(isNullableString) }); + +const invalidParsedValue = (): never => { + throw new Error(parsedValueError); +}; + +const mapParsedValue = (value: unknown): BarcodeParsedValue => { + if (!isRecord(value) || !isRecord(value.data)) { + return invalidParsedValue(); + } + + switch (value.type) { + case 'TYPE_WIFI': + return isWifiData(value.data) + ? { type: 'TYPE_WIFI', data: { ...value.data } } + : invalidParsedValue(); + case 'TYPE_URL': + return isUrlData(value.data) + ? { type: 'TYPE_URL', data: { ...value.data } } + : invalidParsedValue(); + case 'TYPE_SMS': + return isSmsData(value.data) + ? { type: 'TYPE_SMS', data: { ...value.data } } + : invalidParsedValue(); + case 'TYPE_EMAIL': + return isEmailData(value.data) + ? { type: 'TYPE_EMAIL', data: { ...value.data } } + : invalidParsedValue(); + case 'TYPE_PHONE': + return isPhoneData(value.data) + ? { type: 'TYPE_PHONE', data: { ...value.data } } + : invalidParsedValue(); + case 'TYPE_GEO': + return isGeoData(value.data) + ? { type: 'TYPE_GEO', data: { ...value.data } } + : invalidParsedValue(); + case 'TYPE_CALENDAR_EVENT': + return isCalendarEventData(value.data) + ? { type: 'TYPE_CALENDAR_EVENT', data: { ...value.data } } + : invalidParsedValue(); + case 'TYPE_CONTACT_INFO': + return isContactInfoData(value.data) + ? { type: 'TYPE_CONTACT_INFO', data: { ...value.data } } + : invalidParsedValue(); + case 'TYPE_DRIVER_LICENSE': + return isDriverLicenseData(value.data) + ? { type: 'TYPE_DRIVER_LICENSE', data: { ...value.data } } + : invalidParsedValue(); + case 'TYPE_TEXT': + return isTextData(value.data) + ? { type: 'TYPE_TEXT', data: { ...value.data } } + : invalidParsedValue(); + case 'TYPE_PRODUCT': + return isProductData(value.data) + ? { type: 'TYPE_PRODUCT', data: { ...value.data } } + : invalidParsedValue(); + case 'TYPE_ISBN': + return isIsbnData(value.data) + ? { type: 'TYPE_ISBN', data: { ...value.data } } + : invalidParsedValue(); + case 'TYPE_UNKNOWN': + return isUnknownData(value.data) + ? { type: 'TYPE_UNKNOWN', data: { ...value.data } } + : invalidParsedValue(); + default: + return invalidParsedValue(); + } +}; + +const mapStaticBarcode = (barcode: BarcodeScannerBarcode): BarcodeData => { + const { value, ...barcodeData } = barcode; + + return { + ...barcodeData, + rawValue: barcode.rawValue ?? null, + displayValue: barcode.displayValue ?? null, + rawBytes: barcode.rawBytes ?? null, + ...(value === undefined ? {} : { value: mapParsedValue(value) }), + }; +}; + +/** + * Converts the codegen-safe static Nitro result to the public barcode result. + */ +export const mapStaticBarcodeResult = ( + result: BarcodeScannerResult +): BarcodeScanningResult => ({ + barcodes: result.barcodes.map(mapStaticBarcode), +}); diff --git a/src/features/barcode-scanning/types.ts b/src/features/barcode-scanning/types.ts index b718ed1..c854489 100644 --- a/src/features/barcode-scanning/types.ts +++ b/src/features/barcode-scanning/types.ts @@ -41,8 +41,6 @@ export type BarcodeScanningImageOptions = ImageProcessingBaseOptions & { * For potential barcodes, `rawValue` and `rawBytes` may be null. */ enableAllPotentialBarcodes?: boolean; - /** Optional image downscaling for performance optimization. Clamped to [0.9, 1.0]. */ - scaleFactor?: number; }; export type BarcodeScanningArguments = MLKitBaseArguments & {}; diff --git a/src/features/text-recognition/index.ts b/src/features/text-recognition/index.ts index 8b72bf5..726efe6 100644 --- a/src/features/text-recognition/index.ts +++ b/src/features/text-recognition/index.ts @@ -1,7 +1,8 @@ import { useMemo } from 'react'; import type { Frame } from 'react-native-vision-camera'; -import { NativeBridge } from '../../core/NativeBridge'; import { MLKIT_FEATURE_KEYS } from '../../core/constants'; +import { normalizeImageUri } from '../../core/normalizeImageUri'; +import { PluginFactory } from '../../core/PluginFactory'; import { useMLKitPlugin } from '../../hooks/useMLKitPlugin'; import type { TextRecognizer } from '../../specs/TextRecognizer.nitro'; import type { @@ -21,11 +22,16 @@ export const processImageTextRecognition = async ( uri: string, options: TextRecognitionImageOptions = {} ): Promise => { - return await NativeBridge.processImage( + const recognizer = PluginFactory.initPlugin( MLKIT_FEATURE_KEYS.TEXT_RECOGNITION, - uri, options - ); + ) as TextRecognizer; + + try { + return await recognizer.recognizeImage(normalizeImageUri(uri)); + } finally { + recognizer.dispose(); + } }; /** diff --git a/src/shared/constants.ts b/src/shared/constants.ts index b94d1ff..1529311 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -1,13 +1,6 @@ -import { Platform } from 'react-native'; import { MLKIT_FEATURE_KEYS } from '../core/constants'; import type { MLKitFeature } from '../core/types'; -export const LINKING_ERROR = - `The package 'react-native-vision-camera-mlkit' doesn't seem to be linked. Make sure: \n\n` + - Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) + - '- You rebuilt the app after installing the package\n' + - '- You are not using Expo Go\n'; - const TEXT_RECOGNITION_ERROR = 'Text Recognition feature is not enabled. Please enable it in your Podfile (iOS) or build.gradle (Android) and rebuild your app.'; @@ -65,6 +58,3 @@ export const IMAGE_PROCESSING_FAILED_ERROR = export const UNSUPPORTED_IMAGE_FORMAT_ERROR = 'Unsupported image format. Only JPEG, PNG, and WebP are supported.'; - -export const MISSING_MODULE_ERROR = - 'VisionCameraMLKit native module is not available. Ensure it is linked, pods are installed, and the app was rebuilt.'; diff --git a/src/specs/BarcodeScanner.nitro.ts b/src/specs/BarcodeScanner.nitro.ts index dfc9acf..558526c 100644 --- a/src/specs/BarcodeScanner.nitro.ts +++ b/src/specs/BarcodeScanner.nitro.ts @@ -17,4 +17,9 @@ export interface BarcodeScanner frame: Frame, args?: BarcodeScanningArguments ): BarcodeScannerResult; + + /** + * Runs ML Kit barcode scanning on a static image URI. + */ + recognizeImage(uri: string): Promise; } diff --git a/src/specs/BarcodeScannerTypes.ts b/src/specs/BarcodeScannerTypes.ts index 216f7c5..dc3ddf7 100644 --- a/src/specs/BarcodeScannerTypes.ts +++ b/src/specs/BarcodeScannerTypes.ts @@ -1,4 +1,5 @@ import type { BoundingBox, Corner } from '../core/types'; +import type { AnyMap } from 'react-native-nitro-modules'; import type { BarcodeFormat, BarcodeValueTypeName, @@ -53,6 +54,12 @@ export interface BarcodeScannerBarcode { */ rawBytes?: number[]; + /** + * Parsed ML Kit barcode payload. This stays untyped at the Nitro boundary + * because its public representation is a discriminated union. + */ + value?: AnyMap; + /** * Whether this is an undecoded potential barcode. */ diff --git a/src/specs/MLKitVisionTypes.ts b/src/specs/MLKitVisionTypes.ts index f0e7b65..4ac9d8d 100644 --- a/src/specs/MLKitVisionTypes.ts +++ b/src/specs/MLKitVisionTypes.ts @@ -1,6 +1,6 @@ import type { BarcodeFormat } from '../features/barcode-scanning/types'; import type { TextRecognitionLanguage } from '../features/text-recognition/types'; -import type { RegionOfInterest } from '../core/types'; +import type { Orientation, RegionOfInterest } from '../core/types'; /** * ML Kit features that can be compiled into this package. @@ -43,6 +43,11 @@ export interface MLKitRecognizerOptions { * full source frame coordinates. */ roi?: RegionOfInterest; + + /** + * Override the source image orientation when recognizing a static image. + */ + orientation?: Orientation; } /** diff --git a/src/specs/TextRecognizer.nitro.ts b/src/specs/TextRecognizer.nitro.ts index 4109dce..623d2d9 100644 --- a/src/specs/TextRecognizer.nitro.ts +++ b/src/specs/TextRecognizer.nitro.ts @@ -19,4 +19,9 @@ export interface TextRecognizer frame: Frame, args?: TextRecognitionArguments ): TextRecognitionResult; + + /** + * Runs ML Kit text recognition on a static image URI. + */ + recognizeImage(uri: string): Promise; } diff --git a/turbo.json b/turbo.json index c4d78c4..a52d06e 100644 --- a/turbo.json +++ b/turbo.json @@ -23,7 +23,8 @@ "env": [ "RCT_NEW_ARCH_ENABLED", "RCT_USE_RN_DEP", - "RCT_USE_PREBUILT_RNCORE" + "RCT_USE_PREBUILT_RNCORE", + "IOS_DESTINATION" ], "inputs": [ "package.json", diff --git a/yarn.lock b/yarn.lock index f898472..6db7427 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,19 +5,19 @@ __metadata: version: 8 cacheKey: 10c0 -"@ark/schema@npm:0.56.0": - version: 0.56.0 - resolution: "@ark/schema@npm:0.56.0" +"@ark/schema@npm:0.56.2": + version: 0.56.2 + resolution: "@ark/schema@npm:0.56.2" dependencies: - "@ark/util": "npm:0.56.0" - checksum: 10c0/89fb7e4e1304ea9b34c834a8cb34bc05201b4e3a1b26277f7d74b9505a2d7ed398bb55a77e491e7a14a7f0807424a5467e1d32ee725ee2f7b9d158ae30d8121a + "@ark/util": "npm:0.56.2" + checksum: 10c0/0825c34969dcf3ff199a50b663eb792469f1c78b45f7dea565b2101fda9ab4e91eb5e088e0a5b6a667a05ddd1cc8b1bcbd6369ab2337655c40e680022fa00892 languageName: node linkType: hard -"@ark/util@npm:0.56.0": - version: 0.56.0 - resolution: "@ark/util@npm:0.56.0" - checksum: 10c0/dfef779c90f9f814ba97069c63bf91fa67569b87c5cd925d0e2d96b91aa677c019ed1dd5ff95332d9bb0598f785057439074b8cbfcaa2578770616e260fc5761 +"@ark/util@npm:0.56.2": + version: 0.56.2 + resolution: "@ark/util@npm:0.56.2" + checksum: 10c0/dc5757fa7281ec2f202c7b6df8ff3bb154ee6a027eb5c69e7bbc30596fe7385109347c6124bee1e9c3a30c8def3dee7aaaa963103abd4c91dfb4bb1433a537ca languageName: node linkType: hard @@ -43,14 +43,14 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.27.2, @babel/compat-data@npm:^7.27.7, @babel/compat-data@npm:^7.28.5": +"@babel/compat-data@npm:^7.27.2, @babel/compat-data@npm:^7.28.5": version: 7.28.5 resolution: "@babel/compat-data@npm:7.28.5" checksum: 10c0/702a25de73087b0eba325c1d10979eed7c9b6662677386ba7b5aa6eace0fc0676f78343bae080a0176ae26f58bd5535d73b9d0fbb547fef377692e8b249353a7 languageName: node linkType: hard -"@babel/compat-data@npm:^7.29.7": +"@babel/compat-data@npm:^7.28.6, @babel/compat-data@npm:^7.29.7": version: 7.29.7 resolution: "@babel/compat-data@npm:7.29.7" checksum: 10c0/47913f05e08a45a1c9df38c02b4b49e391005085b489432647a1abe112e5d9c75e3be8ea5972b7f6da4ec5d1339922ceb9ea02b8a25d4ed1cb8636e5261f344e @@ -143,6 +143,19 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.29.8": + version: 7.29.8 + resolution: "@babel/generator@npm:7.29.8" + dependencies: + "@babel/parser": "npm:^7.29.8" + "@babel/types": "npm:^7.29.8" + "@jridgewell/gen-mapping": "npm:^0.3.12" + "@jridgewell/trace-mapping": "npm:^0.3.28" + jsesc: "npm:^3.0.2" + checksum: 10c0/7b896696314a659652393b76d78276e236acd0f7fae40a9a1af7f01c76aeafc630dd0966aad6f9386d35d7c674a8e6e2d8e217c44d25fb11460e68afa9ba8441 + languageName: node + linkType: hard + "@babel/helper-annotate-as-pure@npm:^7.27.1, @babel/helper-annotate-as-pure@npm:^7.27.3": version: 7.27.3 resolution: "@babel/helper-annotate-as-pure@npm:7.27.3" @@ -174,7 +187,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.29.7": +"@babel/helper-compilation-targets@npm:^7.28.6, @babel/helper-compilation-targets@npm:^7.29.7": version: 7.29.7 resolution: "@babel/helper-compilation-targets@npm:7.29.7" dependencies: @@ -249,6 +262,21 @@ __metadata: languageName: node linkType: hard +"@babel/helper-define-polyfill-provider@npm:^0.6.8": + version: 0.6.8 + resolution: "@babel/helper-define-polyfill-provider@npm:0.6.8" + dependencies: + "@babel/helper-compilation-targets": "npm:^7.28.6" + "@babel/helper-plugin-utils": "npm:^7.28.6" + debug: "npm:^4.4.3" + lodash.debounce: "npm:^4.0.8" + resolve: "npm:^1.22.11" + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 10c0/306a169f2cb285f368578219ef18ea9702860d3d02d64334f8d45ea38648be0b9e1edad8c8f732fa34bb4206ccbb9883c395570fd57ab7bbcf293bc5964c5b3a + languageName: node + linkType: hard + "@babel/helper-globals@npm:^7.28.0": version: 7.28.0 resolution: "@babel/helper-globals@npm:7.28.0" @@ -354,7 +382,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.29.7": +"@babel/helper-plugin-utils@npm:^7.28.6, @babel/helper-plugin-utils@npm:^7.29.7": version: 7.29.7 resolution: "@babel/helper-plugin-utils@npm:7.29.7" checksum: 10c0/380477a06133274a2759f9355929cb60a95e8b8fee624a1ae1fa349e1d1645b89daca456f72833f6d1062bffa12ee4271c5bf0cc5a61c0166cdc24c7591e2408 @@ -515,6 +543,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.29.8": + version: 7.29.8 + resolution: "@babel/parser@npm:7.29.8" + dependencies: + "@babel/types": "npm:^7.29.8" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/acc890c5e6a6dd40863a47b50bac111d7185ee6fbbe163ebe11d5214854ca2adb901462ad4d718a65090ef84bd2230e9e8ab45a2e0caccc685f1f57ab0bb1e28 + languageName: node + linkType: hard + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.28.5": version: 7.28.5 resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.28.5" @@ -1207,17 +1246,17 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.28.5": - version: 7.28.5 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.28.5" +"@babel/plugin-transform-modules-systemjs@npm:7.29.8": + version: 7.29.8 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.29.8" dependencies: - "@babel/helper-module-transforms": "npm:^7.28.3" - "@babel/helper-plugin-utils": "npm:^7.27.1" - "@babel/helper-validator-identifier": "npm:^7.28.5" - "@babel/traverse": "npm:^7.28.5" + "@babel/helper-module-transforms": "npm:^7.29.7" + "@babel/helper-plugin-utils": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.8" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/7e8c0bcff79689702b974f6a0fedb5d0c6eeb5a5e3384deb7028e7cfe92a5242cc80e981e9c1817aad29f2ecc01841753365dd38d877aa0b91737ceec2acfd07 + checksum: 10c0/18167443bae93f485a43a1acdba9e53ac143043b93553d2ca1178030b68d4c1a7d5f5e717198aa8d73f39f33c023090af2270da774484105307e0fada5654687 languageName: node linkType: hard @@ -1861,6 +1900,21 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.29.8": + version: 7.29.8 + resolution: "@babel/traverse@npm:7.29.8" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.8" + "@babel/helper-globals": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.8" + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.8" + debug: "npm:^4.3.1" + checksum: 10c0/87a28989c434add26d787776ac6d30f749b89cb030f2a605c89f671a516a6fa165ac0476f07e2b69feed70128b2734cae1cbbf41dfe95ccf22081bd8f8b91923 + languageName: node + linkType: hard + "@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": version: 7.28.5 resolution: "@babel/types@npm:7.28.5" @@ -1881,6 +1935,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.29.8": + version: 7.29.8 + resolution: "@babel/types@npm:7.29.8" + dependencies: + "@babel/helper-string-parser": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + checksum: 10c0/be7c279f0abf2a086c633e21b49c7ca80275d05283cc5a268b67a708c9914bd0c944f1422b3eb3cb37682a2af5d560abf520ccf9b01b53ecbfe6b71fbc3fdde6 + languageName: node + linkType: hard + "@bcoe/v8-coverage@npm:^0.2.3": version: 0.2.3 resolution: "@bcoe/v8-coverage@npm:0.2.3" @@ -2098,6 +2162,25 @@ __metadata: languageName: node linkType: hard +"@conventional-changelog/git-client@npm:^2.7.0": + version: 2.7.0 + resolution: "@conventional-changelog/git-client@npm:2.7.0" + dependencies: + "@simple-libs/child-process-utils": "npm:^1.0.0" + "@simple-libs/stream-utils": "npm:^1.2.0" + semver: "npm:^7.5.2" + peerDependencies: + conventional-commits-filter: ^5.0.0 + conventional-commits-parser: ^6.4.0 + peerDependenciesMeta: + conventional-commits-filter: + optional: true + conventional-commits-parser: + optional: true + checksum: 10c0/798097baa08a13311989e803451b9a8e602f404fcde1ec9fef609512625674c2d2a2f4368fbe00754a36f255e5b93dd852e7d3f2a9814215f9887ffa55c93993 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1": version: 4.9.1 resolution: "@eslint-community/eslint-utils@npm:4.9.1" @@ -2130,14 +2213,14 @@ __metadata: languageName: node linkType: hard -"@eslint/config-array@npm:^0.21.1": - version: 0.21.1 - resolution: "@eslint/config-array@npm:0.21.1" +"@eslint/config-array@npm:^0.21.2": + version: 0.21.2 + resolution: "@eslint/config-array@npm:0.21.2" dependencies: "@eslint/object-schema": "npm:^2.1.7" debug: "npm:^4.3.1" - minimatch: "npm:^3.1.2" - checksum: 10c0/2f657d4edd6ddcb920579b72e7a5b127865d4c3fb4dda24f11d5c4f445a93ca481aebdbd6bf3291c536f5d034458dbcbb298ee3b698bc6c9dd02900fe87eec3c + minimatch: "npm:^3.1.5" + checksum: 10c0/89dfe815d18456177c0a1f238daf4593107fd20298b3598e0103054360d3b8d09d967defd8318f031185d68df1f95cfa68becf1390a9c5c6887665f1475142e3 languageName: node linkType: hard @@ -2176,7 +2259,31 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.39.2, @eslint/js@npm:^9.35.0": +"@eslint/eslintrc@npm:^3.3.6": + version: 3.3.6 + resolution: "@eslint/eslintrc@npm:3.3.6" + dependencies: + ajv: "npm:^6.14.0" + debug: "npm:^4.3.2" + espree: "npm:^10.0.1" + globals: "npm:^14.0.0" + ignore: "npm:^5.2.0" + import-fresh: "npm:^3.2.1" + js-yaml: "npm:^4.3.0" + minimatch: "npm:^3.1.5" + strip-json-comments: "npm:^3.1.1" + checksum: 10c0/349697171ee116f501a2f483bf47cd38937a61880aeb1c23481a69afc95e95859430a0947acbe1f5507f223180bf57530ecf2989e73bcbea763a6dcffdf1d010 + languageName: node + linkType: hard + +"@eslint/js@npm:9.39.5": + version: 9.39.5 + resolution: "@eslint/js@npm:9.39.5" + checksum: 10c0/49894e98ba313a7d3bfb1b20d55c0f9826be45a7db876fd84e533ac7f101b1d95b51cd22c6a6db3a45066b9c0d068c31b4a22fa0db8a6cc8744a25540efdb824 + languageName: node + linkType: hard + +"@eslint/js@npm:^9.35.0": version: 9.39.2 resolution: "@eslint/js@npm:9.39.2" checksum: 10c0/00f51c52b04ac79faebfaa65a9652b2093b9c924e945479f1f3945473f78aee83cbc76c8d70bbffbf06f7024626575b16d97b66eab16182e1d0d39daff2f26f5 @@ -2566,22 +2673,6 @@ __metadata: languageName: node linkType: hard -"@isaacs/balanced-match@npm:^4.0.1": - version: 4.0.1 - resolution: "@isaacs/balanced-match@npm:4.0.1" - checksum: 10c0/7da011805b259ec5c955f01cee903da72ad97c5e6f01ca96197267d3f33103d5b2f8a1af192140f3aa64526c593c8d098ae366c2b11f7f17645d12387c2fd420 - languageName: node - linkType: hard - -"@isaacs/brace-expansion@npm:^5.0.0": - version: 5.0.0 - resolution: "@isaacs/brace-expansion@npm:5.0.0" - dependencies: - "@isaacs/balanced-match": "npm:^4.0.1" - checksum: 10c0/b4d4812f4be53afc2c5b6c545001ff7a4659af68d4484804e9d514e183d20269bb81def8682c01a22b17c4d6aed14292c8494f7d2ac664e547101c1a905aa977 - languageName: node - linkType: hard - "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -2970,28 +3061,6 @@ __metadata: languageName: node linkType: hard -"@npmcli/agent@npm:^4.0.0": - version: 4.0.0 - resolution: "@npmcli/agent@npm:4.0.0" - dependencies: - agent-base: "npm:^7.1.0" - http-proxy-agent: "npm:^7.0.0" - https-proxy-agent: "npm:^7.0.1" - lru-cache: "npm:^11.2.1" - socks-proxy-agent: "npm:^8.0.3" - checksum: 10c0/f7b5ce0f3dd42c3f8c6546e8433573d8049f67ef11ec22aa4704bc41483122f68bf97752e06302c455ead667af5cb753e6a09bff06632bc465c1cfd4c4b75a53 - languageName: node - linkType: hard - -"@npmcli/fs@npm:^5.0.0": - version: 5.0.0 - resolution: "@npmcli/fs@npm:5.0.0" - dependencies: - semver: "npm:^7.3.5" - checksum: 10c0/26e376d780f60ff16e874a0ac9bc3399186846baae0b6e1352286385ac134d900cc5dafaded77f38d77f86898fc923ae1cee9d7399f0275b1aa24878915d722b - languageName: node - linkType: hard - "@octokit/auth-token@npm:^6.0.0": version: 6.0.0 resolution: "@octokit/auth-token@npm:6.0.0" @@ -3130,10 +3199,10 @@ __metadata: languageName: node linkType: hard -"@pkgr/core@npm:^0.2.9": - version: 0.2.9 - resolution: "@pkgr/core@npm:0.2.9" - checksum: 10c0/ac8e4e8138b1a7a4ac6282873aef7389c352f1f8b577b4850778f5182e4a39a5241facbe48361fec817f56d02b51691b383010843fb08b34a8e8ea3614688fd5 +"@pkgr/core@npm:^0.3.6": + version: 0.3.6 + resolution: "@pkgr/core@npm:0.3.6" + checksum: 10c0/153f0f4563f505faeba13c733efa0e05e467ce1c6b941055a5fd3b4560da60fbf1dff4b11da0075f034ddda11f2842b90395f60895dde5825875b616edccc11c languageName: node linkType: hard @@ -3717,6 +3786,13 @@ __metadata: languageName: node linkType: hard +"@simple-libs/hosted-git-info@npm:^1.0.2": + version: 1.0.2 + resolution: "@simple-libs/hosted-git-info@npm:1.0.2" + checksum: 10c0/6f22b598b9d37f3e37e409eb588126d14a68f62d52f6e2bb1d99745eb86de8843a02a6fbf7e7f4d026a07fcb1f98d0036d9dcb47363275fa86e67c237fadac75 + languageName: node + linkType: hard + "@simple-libs/stream-utils@npm:^1.1.0": version: 1.1.0 resolution: "@simple-libs/stream-utils@npm:1.1.0" @@ -3726,6 +3802,13 @@ __metadata: languageName: node linkType: hard +"@simple-libs/stream-utils@npm:^1.2.0": + version: 1.2.0 + resolution: "@simple-libs/stream-utils@npm:1.2.0" + checksum: 10c0/2788ac7b167d1b6c81b8c6fae2f5d9688b1f02ab31e9e15b33c9dc2ae920cf7de87869de10679be8957f9adb645c91c8919e271f3e34b6b4ec56daf725522dc7 + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.27.8": version: 0.27.8 resolution: "@sinclair/typebox@npm:0.27.8" @@ -3786,6 +3869,48 @@ __metadata: languageName: node linkType: hard +"@turbo/darwin-64@npm:2.10.8": + version: 2.10.8 + resolution: "@turbo/darwin-64@npm:2.10.8" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@turbo/darwin-arm64@npm:2.10.8": + version: 2.10.8 + resolution: "@turbo/darwin-arm64@npm:2.10.8" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@turbo/linux-64@npm:2.10.8": + version: 2.10.8 + resolution: "@turbo/linux-64@npm:2.10.8" + conditions: (os=android | os=linux) & cpu=x64 + languageName: node + linkType: hard + +"@turbo/linux-arm64@npm:2.10.8": + version: 2.10.8 + resolution: "@turbo/linux-arm64@npm:2.10.8" + conditions: (os=android | os=linux) & cpu=arm64 + languageName: node + linkType: hard + +"@turbo/windows-64@npm:2.10.8": + version: 2.10.8 + resolution: "@turbo/windows-64@npm:2.10.8" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@turbo/windows-arm64@npm:2.10.8": + version: 2.10.8 + resolution: "@turbo/windows-arm64@npm:2.10.8" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@types/babel__core@npm:^7.1.14": version: 7.20.5 resolution: "@types/babel__core@npm:7.20.5" @@ -4135,10 +4260,10 @@ __metadata: languageName: node linkType: hard -"abbrev@npm:^4.0.0": - version: 4.0.0 - resolution: "abbrev@npm:4.0.0" - checksum: 10c0/b4cc16935235e80702fc90192e349e32f8ef0ed151ef506aa78c81a7c455ec18375c4125414b99f84b2e055199d66383e787675f0bcd87da7a4dbd59f9eac1d5 +"abbrev@npm:^5.0.0": + version: 5.0.0 + resolution: "abbrev@npm:5.0.0" + checksum: 10c0/8e88f5c798ea4562d28c5a3e9ad69e3879890bc5d695d8f2dffb8609be4c890aacc8f80ef4553fdd2c6a62d70c2ce8bc57b38074e383beb7487bdafa9ed42ea5 languageName: node linkType: hard @@ -4181,11 +4306,11 @@ __metadata: linkType: hard "acorn@npm:^8.15.0": - version: 8.15.0 - resolution: "acorn@npm:8.15.0" + version: 8.18.0 + resolution: "acorn@npm:8.18.0" bin: acorn: bin/acorn - checksum: 10c0/dec73ff59b7d6628a01eebaece7f2bdb8bb62b9b5926dcad0f8931f2b8b79c2be21f6c68ac095592adb5adb15831a3635d9343e6a91d028bbe85d564875ec3ec + checksum: 10c0/be771be2135cc07910cf76f444ad514d7dcfd6d4a8026e597e93155275abc8ef61eee12211d52146e9d962874269b634f397464942087be013c89d0c54c5f8e5 languageName: node linkType: hard @@ -4206,27 +4331,27 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.12.4": - version: 6.12.6 - resolution: "ajv@npm:6.12.6" +"ajv@npm:^6.12.4, ajv@npm:^6.14.0": + version: 6.15.0 + resolution: "ajv@npm:6.15.0" dependencies: fast-deep-equal: "npm:^3.1.1" fast-json-stable-stringify: "npm:^2.0.0" json-schema-traverse: "npm:^0.4.1" uri-js: "npm:^4.2.2" - checksum: 10c0/41e23642cbe545889245b9d2a45854ebba51cda6c778ebced9649420d9205f2efb39cb43dbc41e358409223b1ea43303ae4839db682c848b891e4811da1a5a71 + checksum: 10c0/67966499dd272ecde1c2e467084411132891523d057487587879d39ac04207f4351b7b2324c83198013967fbfa632c1612adc960114a30770fbe07a0773b32c2 languageName: node linkType: hard "ajv@npm:^8.11.0": - version: 8.17.1 - resolution: "ajv@npm:8.17.1" + version: 8.20.0 + resolution: "ajv@npm:8.20.0" dependencies: fast-deep-equal: "npm:^3.1.3" fast-uri: "npm:^3.0.1" json-schema-traverse: "npm:^1.0.0" require-from-string: "npm:^2.0.2" - checksum: 10c0/ec3ba10a573c6b60f94639ffc53526275917a2df6810e4ab5a6b959d87459f9ef3f00d5e7865b82677cb7d21590355b34da14d1d0b9c32d75f95a187e76fff35 + checksum: 10c0/5df9a1c8f83863cde1bd3a9ddb426f599718f88e3dc9153616c79fb28e0be455335830d7f21d745576519f057b371352daa31047b6a33d7036fe08777d60cf2a languageName: node linkType: hard @@ -4271,7 +4396,7 @@ __metadata: languageName: node linkType: hard -"ansi-regex@npm:^6.0.1": +"ansi-regex@npm:^6.2.2": version: 6.2.2 resolution: "ansi-regex@npm:6.2.2" checksum: 10c0/05d4acb1d2f59ab2cf4b794339c7b168890d44dda4bf0ce01152a8da0213aca207802f930442ce8cd22d7a92f44907664aac6508904e75e038fa944d2601b30f @@ -4321,9 +4446,9 @@ __metadata: linkType: hard "appdirsjs@npm:^1.2.4": - version: 1.2.7 - resolution: "appdirsjs@npm:1.2.7" - checksum: 10c0/79dd8d7a764cdde2b47efc4383e054814be917ba0cd661ee324bdf3fd11542834548316faea31344f96a7ebc898b5f89c11d1418f825a1d40c396bf1ecb0902b + version: 1.2.8 + resolution: "appdirsjs@npm:1.2.8" + checksum: 10c0/dc16a9aa5e9fd2fc8c7a0daaa7184b74c6e4e52afe9242ae73b683af16468d55eb9325b03926721c77307de668696a2e2e935253d107babe8daed96f822d849b languageName: node linkType: hard @@ -4343,23 +4468,23 @@ __metadata: languageName: node linkType: hard -"arkregex@npm:0.0.5": - version: 0.0.5 - resolution: "arkregex@npm:0.0.5" +"arkregex@npm:0.0.8": + version: 0.0.8 + resolution: "arkregex@npm:0.0.8" dependencies: - "@ark/util": "npm:0.56.0" - checksum: 10c0/1a39510e04d69b9287b9b53d3965afcc4ef27bdd9ff9c21a78092fcb841f35c11227d8476be66d2f76347deccfd10c202f395bd871383c328057ad004ffe7ebd + "@ark/util": "npm:0.56.2" + checksum: 10c0/ea1385599fded2e4153ce5cfb75f86e18f221b9c73ddd4071ffe3f63cfffcacd7a16748f1588de48658f7def2069e2a6b7235c3da7f1e11f12d1b05d2c892f9a languageName: node linkType: hard "arktype@npm:^2.1.15": - version: 2.1.29 - resolution: "arktype@npm:2.1.29" + version: 2.2.3 + resolution: "arktype@npm:2.2.3" dependencies: - "@ark/schema": "npm:0.56.0" - "@ark/util": "npm:0.56.0" - arkregex: "npm:0.0.5" - checksum: 10c0/c89cd5cc9eee7a5e14df0f32b228c06760b99440dabf2ed2d9272877a1607d7f27641d4105d1466b70fc620007fe864ddd826acee11c07102388c8dd301a20fe + "@ark/schema": "npm:0.56.2" + "@ark/util": "npm:0.56.2" + arkregex: "npm:0.0.8" + checksum: 10c0/01d6cd9f1c84807772623ad8df5825df9646de1aca4b17869604d523942c92c297b654c930f405338065e18ab52691e00da3e7e2b581039e4aded62d1a9c9e0f languageName: node linkType: hard @@ -4574,15 +4699,15 @@ __metadata: linkType: hard "babel-plugin-polyfill-corejs2@npm:^0.4.14": - version: 0.4.14 - resolution: "babel-plugin-polyfill-corejs2@npm:0.4.14" + version: 0.4.17 + resolution: "babel-plugin-polyfill-corejs2@npm:0.4.17" dependencies: - "@babel/compat-data": "npm:^7.27.7" - "@babel/helper-define-polyfill-provider": "npm:^0.6.5" + "@babel/compat-data": "npm:^7.28.6" + "@babel/helper-define-polyfill-provider": "npm:^0.6.8" semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: 10c0/d74cba0600a6508e86d220bde7164eb528755d91be58020e5ea92ea7fbb12c9d8d2c29246525485adfe7f68ae02618ec428f9a589cac6cbedf53cc3972ad7fbe + checksum: 10c0/1284960ea403c63b0dd598f338666c4b17d489aefee30b4da6a7313eff1d91edffb0ccf26341a6e5d94231684b74e016eade66b3921ea112f8b0e4980fa08a5c languageName: node linkType: hard @@ -4599,13 +4724,13 @@ __metadata: linkType: hard "babel-plugin-polyfill-regenerator@npm:^0.6.5": - version: 0.6.5 - resolution: "babel-plugin-polyfill-regenerator@npm:0.6.5" + version: 0.6.8 + resolution: "babel-plugin-polyfill-regenerator@npm:0.6.8" dependencies: - "@babel/helper-define-polyfill-provider": "npm:^0.6.5" + "@babel/helper-define-polyfill-provider": "npm:^0.6.8" peerDependencies: "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: 10c0/63aa8ed716df6a9277c6ab42b887858fa9f57a70cc1d0ae2b91bdf081e45d4502848cba306fb60b02f59f99b32fd02ff4753b373cac48ccdac9b7d19dd56f06d + checksum: 10c0/7c8b2497c29fa880e0acdc8e7b93e29b81b154179b83beb0476eb2c4e7a78b6b42fc35c2554ca250c9bd6d39941eaf75416254b8592ce50979f9a12e1d51c049 languageName: node linkType: hard @@ -4694,19 +4819,19 @@ __metadata: languageName: node linkType: hard -"baseline-browser-mapping@npm:^2.9.0": - version: 2.9.11 - resolution: "baseline-browser-mapping@npm:2.9.11" +"baseline-browser-mapping@npm:^2.10.44": + version: 2.11.10 + resolution: "baseline-browser-mapping@npm:2.11.10" bin: - baseline-browser-mapping: dist/cli.js - checksum: 10c0/eba49fcc1b33ab994aeeb73a4848f2670e06a0886dd5b903689ae6f60d47e7f1bea9262dbb2548c48179e858f7eda2b82ddf941ae783b862f4dcc51085a246f2 + baseline-browser-mapping: dist/cli.cjs + checksum: 10c0/6e46bf9e1e46d96d909b4f754968603a291dea22fbb051cf86aa73b53f3a4c8ba922bdc1d6b617f5147403b289746e8549e344affcee7a1be87a09020cd73ce1 languageName: node linkType: hard "basic-ftp@npm:^5.0.2": - version: 5.1.0 - resolution: "basic-ftp@npm:5.1.0" - checksum: 10c0/397d5ed490f4d3b8b2dcd7afdf8e9fcf714a7d1c394a6c66b31704baf49c9fc250f1742f6189136a76b983675edf3d986b7ed93d253dc6477e65a567cf1e04c0 + version: 5.3.1 + resolution: "basic-ftp@npm:5.3.1" + checksum: 10c0/03511b488cd292abfa82a8c0ea3b9573b40d12d2f1518d6f41a9461b012b3376d3e6d50679b38d9b2b4f48fd6e8e0418ac196312ee7e2da13cb801169940d1c3 languageName: node linkType: hard @@ -4736,8 +4861,8 @@ __metadata: linkType: hard "body-parser@npm:^1.20.3": - version: 1.20.4 - resolution: "body-parser@npm:1.20.4" + version: 1.20.6 + resolution: "body-parser@npm:1.20.6" dependencies: bytes: "npm:~3.1.2" content-type: "npm:~1.0.5" @@ -4747,11 +4872,11 @@ __metadata: http-errors: "npm:~2.0.1" iconv-lite: "npm:~0.4.24" on-finished: "npm:~2.4.1" - qs: "npm:~6.14.0" + qs: "npm:~6.15.1" raw-body: "npm:~2.5.3" type-is: "npm:~1.6.18" unpipe: "npm:~1.0.0" - checksum: 10c0/569c1e896297d1fcd8f34026c8d0ab70b90d45343c15c5d8dff5de2bad08125fc1e2f8c2f3f4c1ac6c0caaad115218202594d37dcb8d89d9b5dcae1c2b736aa9 + checksum: 10c0/ad477209f1e714c41fa892a7d2fe22e10b23262103cc25cc6492dd3e6f7869cd2d8b00928b543a1a14d3c3663432452a192efd00422fad6f3687b0e38f7e3b07 languageName: node linkType: hard @@ -4781,30 +4906,30 @@ __metadata: linkType: hard "brace-expansion@npm:^1.1.7": - version: 1.1.12 - resolution: "brace-expansion@npm:1.1.12" + version: 1.1.18 + resolution: "brace-expansion@npm:1.1.18" dependencies: balanced-match: "npm:^1.0.0" concat-map: "npm:0.0.1" - checksum: 10c0/975fecac2bb7758c062c20d0b3b6288c7cc895219ee25f0a64a9de662dbac981ff0b6e89909c3897c1f84fa353113a721923afdec5f8b2350255b097f12b1f73 + checksum: 10c0/3432c18a9e2ebf94162d4effb62198bd0adea06a9f332b2c0188df5d5e30b1e51ea3c848b6608e47d0b857ebe1ea5b3888ed3326dd3c4f6f9645c94153cf9c14 languageName: node linkType: hard -"brace-expansion@npm:^2.0.1": - version: 2.0.2 - resolution: "brace-expansion@npm:2.0.2" +"brace-expansion@npm:^2.0.2": + version: 2.1.4 + resolution: "brace-expansion@npm:2.1.4" dependencies: balanced-match: "npm:^1.0.0" - checksum: 10c0/6d117a4c793488af86b83172deb6af143e94c17bc53b0b3cec259733923b4ca84679d506ac261f4ba3c7ed37c46018e2ff442f9ce453af8643ecd64f4a54e6cf + checksum: 10c0/6c0a0e2573eac1dc565b52b1e1bfbeba39bf1830d106ebbc61ff1eaefcf610e9111cd3baa091addd18e33292135c99e019d3184d966389d85dc958fdcdc1449f languageName: node linkType: hard -"brace-expansion@npm:^5.0.5": - version: 5.0.7 - resolution: "brace-expansion@npm:5.0.7" +"brace-expansion@npm:^5.0.8": + version: 5.0.9 + resolution: "brace-expansion@npm:5.0.9" dependencies: balanced-match: "npm:^4.0.2" - checksum: 10c0/4769109c3c082de178e449a371bcad50d51ab468f644bce2dd9188efe0cf0a080ed102105d7fc8577382cedc45bad7e6443a91bf3d8102264ee8cf927dbaf205 + checksum: 10c0/3dea38884a1c3c8b1c9c44a7402a0c76fca460f70cffb3127242b0b4cbf4472019e022ade021eec44838ff19f1dac2625dfd11dd459d7e1e055b0698a8d52fec languageName: node linkType: hard @@ -4817,18 +4942,18 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.20.4, browserslist@npm:^4.24.0, browserslist@npm:^4.28.0": - version: 4.28.1 - resolution: "browserslist@npm:4.28.1" +"browserslist@npm:^4.20.4, browserslist@npm:^4.24.0, browserslist@npm:^4.28.1": + version: 4.28.7 + resolution: "browserslist@npm:4.28.7" dependencies: - baseline-browser-mapping: "npm:^2.9.0" - caniuse-lite: "npm:^1.0.30001759" - electron-to-chromium: "npm:^1.5.263" - node-releases: "npm:^2.0.27" - update-browserslist-db: "npm:^1.2.0" + baseline-browser-mapping: "npm:^2.10.44" + caniuse-lite: "npm:^1.0.30001806" + electron-to-chromium: "npm:^1.5.393" + node-releases: "npm:^2.0.51" + update-browserslist-db: "npm:^1.2.3" bin: browserslist: cli.js - checksum: 10c0/545a5fa9d7234e3777a7177ec1e9134bb2ba60a69e6b95683f6982b1473aad347c77c1264ccf2ac5dea609a9731fbfbda6b85782bdca70f80f86e28a402504bd + checksum: 10c0/dc41922a9ff0d81e5492498bdab2d932e3a3222f4277814ba38ee64f4e51f26e5244a7cce0777b4cac3ceff6eb196f5cadbb2a832620973a7f9c39611f6bc78e languageName: node linkType: hard @@ -4899,26 +5024,7 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^20.0.1": - version: 20.0.3 - resolution: "cacache@npm:20.0.3" - dependencies: - "@npmcli/fs": "npm:^5.0.0" - fs-minipass: "npm:^3.0.0" - glob: "npm:^13.0.0" - lru-cache: "npm:^11.1.0" - minipass: "npm:^7.0.3" - minipass-collect: "npm:^2.0.1" - minipass-flush: "npm:^1.0.5" - minipass-pipeline: "npm:^1.2.4" - p-map: "npm:^7.0.2" - ssri: "npm:^13.0.0" - unique-filename: "npm:^5.0.0" - checksum: 10c0/c7da1ca694d20e8f8aedabd21dc11518f809a7d2b59aa76a1fc655db5a9e62379e465c157ddd2afe34b19230808882288effa6911b2de26a088a6d5645123462 - languageName: node - linkType: hard - -"call-bind-apply-helpers@npm:^1.0.0, call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": +"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": version: 1.0.2 resolution: "call-bind-apply-helpers@npm:1.0.2" dependencies: @@ -4928,15 +5034,15 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.7, call-bind@npm:^1.0.8": - version: 1.0.8 - resolution: "call-bind@npm:1.0.8" +"call-bind@npm:^1.0.7, call-bind@npm:^1.0.8, call-bind@npm:^1.0.9": + version: 1.0.9 + resolution: "call-bind@npm:1.0.9" dependencies: - call-bind-apply-helpers: "npm:^1.0.0" - es-define-property: "npm:^1.0.0" - get-intrinsic: "npm:^1.2.4" + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" + get-intrinsic: "npm:^1.3.0" set-function-length: "npm:^1.2.2" - checksum: 10c0/a13819be0681d915144467741b69875ae5f4eba8961eb0bf322aab63ec87f8250eb6d6b0dcbb2e1349876412a56129ca338592b3829ef4343527f5f18a0752d4 + checksum: 10c0/a6621f6da1444481919ce3b4983dff725691e0754d3507ae483ce56e54985f2da7d6f1df512c56dbf28660745cf1ca52553f1fc9aef5557f3ce353ef14fab714 languageName: node linkType: hard @@ -4971,10 +5077,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001759": - version: 1.0.30001762 - resolution: "caniuse-lite@npm:1.0.30001762" - checksum: 10c0/93707eac5b0240af3f2ce6e2d7ab504a6fefcf9c2f9cd8fb9d488e496a333c61e557dab0472c1b00c17bc386a5dbb792aa4c778cda2d768e17f986617d7aec53 +"caniuse-lite@npm:^1.0.30001806": + version: 1.0.30001806 + resolution: "caniuse-lite@npm:1.0.30001806" + checksum: 10c0/9442c8afff0968e9b2ce0104a7340c1ce4a5c09f469ccb9eef10d25712ea0afe542486e5318c697c70dd502f988cfc04eaa1c9438c92ac3bcf4d708a2a17bee1 languageName: node linkType: hard @@ -5003,9 +5109,9 @@ __metadata: linkType: hard "chardet@npm:^2.1.1": - version: 2.1.1 - resolution: "chardet@npm:2.1.1" - checksum: 10c0/d8391dd412338442b3de0d3a488aa9327f8bcf74b62b8723d6bd0b85c4084d50b731320e0a7c710edb1d44de75969995d2784b80e4c13b004a6c7a0db4c6e793 + version: 2.2.0 + resolution: "chardet@npm:2.2.0" + checksum: 10c0/8d43a1dd3ce535aa070d04139fae62f879b4388394eb1d857364ce416675a1f0f63974fba8208e9cd11b49e1a6da3e65ea6393d0fc654a8c4217c0d74c16b1b4 languageName: node linkType: hard @@ -5067,9 +5173,9 @@ __metadata: linkType: hard "ci-info@npm:^4.3.1": - version: 4.3.1 - resolution: "ci-info@npm:4.3.1" - checksum: 10c0/7dd82000f514d76ddfe7775e4cb0d66e5c638f5fa0e2a3be29557e898da0d32ac04f231217d414d07fb968b1fbc6d980ee17ddde0d2c516f23da9cfff608f6c1 + version: 4.4.0 + resolution: "ci-info@npm:4.4.0" + checksum: 10c0/44156201545b8dde01aa8a09ee2fe9fc7a73b1bef9adbd4606c9f61c8caeeb73fb7a575c88b0443f7b4edb5ee45debaa59ed54ba5f99698339393ca01349eb3a languageName: node linkType: hard @@ -5082,6 +5188,13 @@ __metadata: languageName: node linkType: hard +"citty@npm:^0.2.2": + version: 0.2.2 + resolution: "citty@npm:0.2.2" + checksum: 10c0/c896c9dcd187d2a16685706d11428dcdec9eb59aa13fe50aff7b12e1d3521f1bf434d6a5316fe75a341f8ba5ce1d2beceb84f7f261d018985a73d3f6f2a793e3 + languageName: node + linkType: hard + "cjs-module-lexer@npm:^1.0.0": version: 1.4.3 resolution: "cjs-module-lexer@npm:1.4.3" @@ -5122,9 +5235,9 @@ __metadata: linkType: hard "cli-spinners@npm:^3.2.0": - version: 3.3.0 - resolution: "cli-spinners@npm:3.3.0" - checksum: 10c0/ce7278be322655e564df4383a2d79ad2c357b43e5771428f33ca69334fde6881d050652ee19854e9ab177867850c9365c2090fdb9fae145b23ceee21bdc77b7e + version: 3.4.0 + resolution: "cli-spinners@npm:3.4.0" + checksum: 10c0/91296c32e147d5b973c9d439d1512306499215437b92f0c0d8be44ec850b555acb8795c19c606b2f6747f31d50c4e41fdde7dcef653f18f0ae7cdd58e99a4764 languageName: node linkType: hard @@ -5348,10 +5461,10 @@ __metadata: languageName: node linkType: hard -"confbox@npm:^0.2.2": - version: 0.2.2 - resolution: "confbox@npm:0.2.2" - checksum: 10c0/7c246588d533d31e8cdf66cb4701dff6de60f9be77ab54c0d0338e7988750ac56863cc0aca1b3f2046f45ff223a765d3e5d4977a7674485afcd37b6edf3fd129 +"confbox@npm:^0.2.2, confbox@npm:^0.2.4": + version: 0.2.4 + resolution: "confbox@npm:0.2.4" + checksum: 10c0/4c36af33d9df7034300c452f7b289179264493bd0671fa81b995a0d70dc897b1d37f1af10d3ffb187f178d17ba1ed2ba167ed0f599ba3a139c271205dd553f73 languageName: node linkType: hard @@ -5367,7 +5480,7 @@ __metadata: languageName: node linkType: hard -"consola@npm:^3.2.3, consola@npm:^3.4.0, consola@npm:^3.4.2": +"consola@npm:^3.2.3, consola@npm:^3.4.0": version: 3.4.2 resolution: "consola@npm:3.4.2" checksum: 10c0/7cebe57ecf646ba74b300bcce23bff43034ed6fbec9f7e39c27cee1dc00df8a21cd336b466ad32e304ea70fba04ec9e890c200270de9a526ce021ba8a7e4c11a @@ -5391,11 +5504,11 @@ __metadata: linkType: hard "conventional-changelog-angular@npm:^8.1.0": - version: 8.1.0 - resolution: "conventional-changelog-angular@npm:8.1.0" + version: 8.3.1 + resolution: "conventional-changelog-angular@npm:8.3.1" dependencies: compare-func: "npm:^2.0.0" - checksum: 10c0/b82aab869117fd9bd6ccfa960521e7638d3c2a3599c95fd5ba30d3b3fe972b5f819af4d57229f2973a7129ea18546cdf5822004565cab1ee35355cc90ac4588f + checksum: 10c0/a3776ff7066004040d7d25d2b72fe8f9fee4f1fc5dc6c929ab281899b8c7a6dd6408130064344515b37a4f91d2a9fb90b69cce0bab55415c72a8ba3ee801c2b6 languageName: node linkType: hard @@ -5409,11 +5522,11 @@ __metadata: linkType: hard "conventional-changelog-conventionalcommits@npm:^9.1.0": - version: 9.1.0 - resolution: "conventional-changelog-conventionalcommits@npm:9.1.0" + version: 9.3.1 + resolution: "conventional-changelog-conventionalcommits@npm:9.3.1" dependencies: compare-func: "npm:^2.0.0" - checksum: 10c0/b1dfbb8ce5983bb80837c35f089fb0f9603a1b067f34be680f88fde20871792e461e29d119d468bc293f38a1ca916c1c40a841f8c049a0a1efaa40582f4fecc9 + checksum: 10c0/e3d0dfe5680899da3660a7fbc859de1a92dd9358dc497624c3582596173713a80dcc8236d844116a3ba8403350e244c007c0c7fa5796631aea19e464cc6c2f44 languageName: node linkType: hard @@ -5424,35 +5537,37 @@ __metadata: languageName: node linkType: hard -"conventional-changelog-writer@npm:^8.2.0": - version: 8.2.0 - resolution: "conventional-changelog-writer@npm:8.2.0" +"conventional-changelog-writer@npm:^8.4.0": + version: 8.4.0 + resolution: "conventional-changelog-writer@npm:8.4.0" dependencies: + "@simple-libs/stream-utils": "npm:^1.2.0" conventional-commits-filter: "npm:^5.0.0" handlebars: "npm:^4.7.7" meow: "npm:^13.0.0" semver: "npm:^7.5.2" bin: conventional-changelog-writer: dist/cli/index.js - checksum: 10c0/e25052bb366ecee6389326fd5b7d3ecbd6f6a65439f45b5a2b1d4096baeb1bbfa93cd6bea686f419423265db5bbb02870a014cb92f43f972c00191c60711e9b6 + checksum: 10c0/d657bf74c470e5d515d3a07814d266e6e2aea018e6867bfefa4bc486bb3f948b47b01936d65e46b3090111823364c21c201f9fbe875b1fc805cdf884bb032bc1 languageName: node linkType: hard "conventional-changelog@npm:^7.1.1": - version: 7.1.1 - resolution: "conventional-changelog@npm:7.1.1" + version: 7.2.1 + resolution: "conventional-changelog@npm:7.2.1" dependencies: - "@conventional-changelog/git-client": "npm:^2.5.1" + "@conventional-changelog/git-client": "npm:^2.7.0" + "@simple-libs/hosted-git-info": "npm:^1.0.2" "@types/normalize-package-data": "npm:^2.4.4" conventional-changelog-preset-loader: "npm:^5.0.0" - conventional-changelog-writer: "npm:^8.2.0" - conventional-commits-parser: "npm:^6.2.0" + conventional-changelog-writer: "npm:^8.4.0" + conventional-commits-parser: "npm:^6.4.0" fd-package-json: "npm:^2.0.0" meow: "npm:^13.0.0" normalize-package-data: "npm:^7.0.0" bin: conventional-changelog: dist/cli/index.js - checksum: 10c0/8f2c36b7a463bdedc9f919a50458a061b8017d003452b5bc648a6c43a0affe2e76ed9cb5933aa55ad4d6c99061c0b9a567ec5c528333593ec0f210151e1e11b7 + checksum: 10c0/10e0c7b1904c8f557099485b21fdf2cd3359b67a9e917be4cae0636b9f111520c76208949823082747486b4ab4db6602f724f57878c28257bbd0012590fb9fe9 languageName: node linkType: hard @@ -5477,14 +5592,15 @@ __metadata: languageName: node linkType: hard -"conventional-commits-parser@npm:^6.1.0, conventional-commits-parser@npm:^6.2.0": - version: 6.2.1 - resolution: "conventional-commits-parser@npm:6.2.1" +"conventional-commits-parser@npm:^6.1.0, conventional-commits-parser@npm:^6.4.0": + version: 6.4.0 + resolution: "conventional-commits-parser@npm:6.4.0" dependencies: + "@simple-libs/stream-utils": "npm:^1.2.0" meow: "npm:^13.0.0" bin: conventional-commits-parser: dist/cli/index.js - checksum: 10c0/217b3fff627802f7fd7cb09bdfe897aa76986865543dfaa99b7957e4717d039e1e12c4a9b72706f098a5716bbbbdae540ef0b2429f7219d5fc5be0f190f1bc1e + checksum: 10c0/3595b85b420a3f431dbad65f7c367e803ee8bca500ec24482e8669d4ed5ff6febbb5e9a17c52f07ebe882abdee3418e759245bcb06e4d1e2cce09d638f31d5ce languageName: node linkType: hard @@ -5511,30 +5627,30 @@ __metadata: linkType: hard "core-js-compat@npm:^3.43.0": - version: 3.47.0 - resolution: "core-js-compat@npm:3.47.0" + version: 3.49.0 + resolution: "core-js-compat@npm:3.49.0" dependencies: - browserslist: "npm:^4.28.0" - checksum: 10c0/71da415899633120db7638dd7b250eee56031f63c4560dcba8eeeafd1168fae171d59b223e3fd2e0aa543a490d64bac7d946764721e2c05897056fdfb22cce33 + browserslist: "npm:^4.28.1" + checksum: 10c0/546e64b7ce45f724825bc13c1347f35c0459a6e71c0dcccff3ec21fbff463f5b0b97fc1220e6d90302153863489301793276fe2bf96f46001ff555ead4140308 languageName: node linkType: hard "cosmiconfig-typescript-loader@npm:^6.1.0": - version: 6.2.0 - resolution: "cosmiconfig-typescript-loader@npm:6.2.0" + version: 6.3.0 + resolution: "cosmiconfig-typescript-loader@npm:6.3.0" dependencies: - jiti: "npm:^2.6.1" + jiti: "npm:2.6.1" peerDependencies: "@types/node": "*" cosmiconfig: ">=9" typescript: ">=5" - checksum: 10c0/0fd8fd9b9b6a04eec75617b965ce0a1f63310fe29a361c1f95cb971e05dbbb935291899c2b15abfd69e09db58dbe97077f24a7c61414bbc6c3e78349b4314ad7 + checksum: 10c0/dd53519bf59b31c32a831f1140eaa44f4f0bf49229b7e3ba27bb6f26097c3ecff955a490e70b31349049463066b5047bd129ab82ed078be9b1f1f22ccb776c6a languageName: node linkType: hard "cosmiconfig@npm:^9.0.0": - version: 9.0.0 - resolution: "cosmiconfig@npm:9.0.0" + version: 9.0.2 + resolution: "cosmiconfig@npm:9.0.2" dependencies: env-paths: "npm:^2.2.1" import-fresh: "npm:^3.3.0" @@ -5545,7 +5661,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/1c1703be4f02a250b1d6ca3267e408ce16abfe8364193891afc94c2d5c060b69611fdc8d97af74b7e6d5d1aac0ab2fb94d6b079573146bc2d756c2484ce5f0ee + checksum: 10c0/132d32863c0b3d9ace7010a10011e09089532b36e3b11e02004d671dcbd8b8f2f16293b82d510d9c15890f30358baf8722127c7b1c011449c90b6a178f9c6594 languageName: node linkType: hard @@ -5662,9 +5778,9 @@ __metadata: linkType: hard "dayjs@npm:^1.8.15": - version: 1.11.19 - resolution: "dayjs@npm:1.11.19" - checksum: 10c0/7d8a6074a343f821f81ea284d700bd34ea6c7abbe8d93bce7aba818948957c1b7f56131702e5e890a5622cdfc05dcebe8aed0b8313bdc6838a594d7846b0b000 + version: 1.11.21 + resolution: "dayjs@npm:1.11.21" + checksum: 10c0/bd97dfdc4bfea3c66268635690313828b386faa040fbc1f829ff42a2bd748b72c9d9b3c8f9616ce9e61fcb78923f1461a462c969c54b1084458ae1b715898fb0 languageName: node linkType: hard @@ -5711,14 +5827,14 @@ __metadata: linkType: hard "dedent@npm:^1.0.0": - version: 1.7.1 - resolution: "dedent@npm:1.7.1" + version: 1.7.2 + resolution: "dedent@npm:1.7.2" peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: babel-plugin-macros: optional: true - checksum: 10c0/ae29ec1c5bd5216c698c9f23acaa5b720260fd4cef3c8b5af887eb5f8c9e6fdd5fed8668767437b4efea35e2991bd798987717633411a1734807c28255769b78 + checksum: 10c0/acaff07cac355b93f17b1b17ebbb84d3cc55af6ab4b7814c3f505e061903e168bc6bf9ddce331552d64dee1525f0b4c549c9ade46aebfac6f69caaed74e90751 languageName: node linkType: hard @@ -5744,12 +5860,12 @@ __metadata: linkType: hard "default-browser@npm:^5.2.1": - version: 5.4.0 - resolution: "default-browser@npm:5.4.0" + version: 5.5.0 + resolution: "default-browser@npm:5.5.0" dependencies: bundle-name: "npm:^4.1.0" default-browser-id: "npm:^5.0.0" - checksum: 10c0/a49ddd0c7b1a319163f64a5fc68ebb45a98548ea23a3155e04518f026173d85cfa2f451b646366c36c8f70b01e4cb773e23d1d22d2c61d8b84e5fbf151b4b609 + checksum: 10c0/576593b617b17a7223014b4571bfe1c06a2581a4eb8b130985d90d253afa3f40999caec70eb0e5776e80d4af6a41cce91018cd3f86e57ad578bf59e46fb19abe languageName: node linkType: hard @@ -5792,9 +5908,9 @@ __metadata: linkType: hard "defu@npm:^6.1.4": - version: 6.1.4 - resolution: "defu@npm:6.1.4" - checksum: 10c0/2d6cc366262dc0cb8096e429368e44052fdf43ed48e53ad84cc7c9407f890301aa5fcb80d0995abaaf842b3949f154d060be4160f7a46cb2bc2f7726c81526f5 + version: 6.1.7 + resolution: "defu@npm:6.1.7" + checksum: 10c0/e6635388103c8be3c574ac31302f6930e5e6eeedba32cb1b30cf993c7d9fb571aec2485446dfa23bfa63e55e66156fe109027a9695db82a50f931e91e8d4bedb languageName: node linkType: hard @@ -5954,9 +6070,9 @@ __metadata: linkType: hard "dotenv@npm:^17.2.3": - version: 17.2.3 - resolution: "dotenv@npm:17.2.3" - checksum: 10c0/c884403209f713214a1b64d4d1defa4934c2aa5b0002f5a670ae298a51e3c3ad3ba79dfee2f8df49f01ae74290fcd9acdb1ab1d09c7bfb42b539036108bb2ba0 + version: 17.4.2 + resolution: "dotenv@npm:17.4.2" + checksum: 10c0/164f8e77a646c8446867d5b588d26ea6005c8ea7c5eb41cf926f6113d23f2191355f6e0cfd95ea9bab98394a5b0a3f1e51a8399711b666fe55cc7b0bd745f942 languageName: node linkType: hard @@ -5985,10 +6101,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.5.263": - version: 1.5.267 - resolution: "electron-to-chromium@npm:1.5.267" - checksum: 10c0/0732bdb891b657f2e43266a3db8cf86fff6cecdcc8d693a92beff214e136cb5c2ee7dc5945ed75fa1db16e16bad0c38695527a020d15f39e79084e0b2e447621 +"electron-to-chromium@npm:^1.5.393": + version: 1.5.399 + resolution: "electron-to-chromium@npm:1.5.399" + checksum: 10c0/5b2ab966e0748d00a2bc2dfd7eee203fe872ce2ca4478eea94f1ceda629172770378128cd6d20be4af8c21874ef250163cd47735e01f38f1182f4312d8c7e78b languageName: node linkType: hard @@ -6034,15 +6150,6 @@ __metadata: languageName: node linkType: hard -"encoding@npm:^0.1.13": - version: 0.1.13 - resolution: "encoding@npm:0.1.13" - dependencies: - iconv-lite: "npm:^0.6.2" - checksum: 10c0/36d938712ff00fe1f4bac88b43bcffb5930c1efa57bbcdca9d67e1d9d6c57cfb1200fb01efe0f3109b2ce99b231f90779532814a81370a1bd3274a0f58585039 - languageName: node - linkType: hard - "end-of-stream@npm:^1.1.0": version: 1.4.5 resolution: "end-of-stream@npm:1.4.5" @@ -6075,13 +6182,6 @@ __metadata: languageName: node linkType: hard -"err-code@npm:^2.0.2": - version: 2.0.3 - resolution: "err-code@npm:2.0.3" - checksum: 10c0/b642f7b4dd4a376e954947550a3065a9ece6733ab8e51ad80db727aaae0817c2e99b02a97a3d6cecc648a97848305e728289cf312d09af395403a90c9d4d8a66 - languageName: node - linkType: hard - "error-ex@npm:^1.3.1": version: 1.3.4 resolution: "error-ex@npm:1.3.4" @@ -6110,9 +6210,21 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.17.5, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3, es-abstract@npm:^1.23.5, es-abstract@npm:^1.23.6, es-abstract@npm:^1.23.9, es-abstract@npm:^1.24.0, es-abstract@npm:^1.24.1": - version: 1.24.1 - resolution: "es-abstract@npm:1.24.1" +"es-abstract-get@npm:^1.0.0": + version: 1.0.0 + resolution: "es-abstract-get@npm:1.0.0" + dependencies: + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.2" + is-callable: "npm:^1.2.7" + object-inspect: "npm:^1.13.4" + checksum: 10c0/f9b4838ae719752207383a6d95a74590f891122bf26b92f5e72eeedbe53771029e4561f1cf75ea19330b71bcf3d4f536fb0c8f7e2b601fe24d284f46e488c7e3 + languageName: node + linkType: hard + +"es-abstract@npm:^1.17.5, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3, es-abstract@npm:^1.23.5, es-abstract@npm:^1.23.6, es-abstract@npm:^1.23.9, es-abstract@npm:^1.24.0, es-abstract@npm:^1.24.2": + version: 1.24.2 + resolution: "es-abstract@npm:1.24.2" dependencies: array-buffer-byte-length: "npm:^1.0.2" arraybuffer.prototype.slice: "npm:^1.0.4" @@ -6168,7 +6280,7 @@ __metadata: typed-array-length: "npm:^1.0.7" unbox-primitive: "npm:^1.1.0" which-typed-array: "npm:^1.1.19" - checksum: 10c0/fca062ef8b5daacf743732167d319a212d45cb655b0bb540821d38d715416ae15b04b84fc86da9e2c89135aa7b337337b6c867f84dcde698d75d55688d5d765c + checksum: 10c0/67a5bf21ef5c7d775e6f6131a836323900b4d87194cf544394ac68fe31c57fa53828b978af4a4f551ef307f83a2f910a16b6b982760ad3ddc3dc471f98d5fd1b languageName: node linkType: hard @@ -6187,13 +6299,13 @@ __metadata: linkType: hard "es-iterator-helpers@npm:^1.2.1": - version: 1.2.2 - resolution: "es-iterator-helpers@npm:1.2.2" + version: 1.4.0 + resolution: "es-iterator-helpers@npm:1.4.0" dependencies: - call-bind: "npm:^1.0.8" + call-bind: "npm:^1.0.9" call-bound: "npm:^1.0.4" define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.24.1" + es-abstract: "npm:^1.24.2" es-errors: "npm:^1.3.0" es-set-tostringtag: "npm:^2.1.0" function-bind: "npm:^1.1.2" @@ -6205,17 +6317,17 @@ __metadata: has-symbols: "npm:^1.1.0" internal-slot: "npm:^1.1.0" iterator.prototype: "npm:^1.1.5" - safe-array-concat: "npm:^1.1.3" - checksum: 10c0/1ced8abf845a45e660dd77b5f3a64358421df70e4a0bd1897d5ddfefffed8409a6a2ca21241b9367e639df9eca74abc1c678b3020bffe6bee1f1826393658ddb + math-intrinsics: "npm:^1.1.0" + checksum: 10c0/839a5e881446e1b4ab270a9ad5d01a23b83a38fadb9e526674df171357e90ad3111dc8c0b15e7d30db1958f2c3332dd963fab7d55f406a660d098b2b7eefb218 languageName: node linkType: hard -"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": - version: 1.1.1 - resolution: "es-object-atoms@npm:1.1.1" +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1, es-object-atoms@npm:^1.1.2": + version: 1.1.2 + resolution: "es-object-atoms@npm:1.1.2" dependencies: es-errors: "npm:^1.3.0" - checksum: 10c0/65364812ca4daf48eb76e2a3b7a89b3f6a2e62a1c420766ce9f692665a29d94fe41fe88b65f24106f449859549711e4b40d9fb8002d862dfd7eb1c512d10be0c + checksum: 10c0/1772861f094f739d6f41b579cfb9a18579daffeb434552a370a5fbef50a32d22227e27b63fdbb757b7ddd429d1b42fe52ccae7966d9302a2ec221b6f1b41bbc4 languageName: node linkType: hard @@ -6241,13 +6353,16 @@ __metadata: linkType: hard "es-to-primitive@npm:^1.3.0": - version: 1.3.0 - resolution: "es-to-primitive@npm:1.3.0" + version: 1.3.4 + resolution: "es-to-primitive@npm:1.3.4" dependencies: + es-abstract-get: "npm:^1.0.0" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" is-callable: "npm:^1.2.7" - is-date-object: "npm:^1.0.5" - is-symbol: "npm:^1.0.4" - checksum: 10c0/c7e87467abb0b438639baa8139f701a06537d2b9bc758f23e8622c3b42fd0fdb5bde0f535686119e446dd9d5e4c0f238af4e14960f4771877cf818d023f6730b + is-date-object: "npm:^1.1.0" + is-symbol: "npm:^1.1.1" + checksum: 10c0/b10029f8b0b13841bade224ff39005a13d76d9cb803cd1efb18cc96a24414e83e2e7ed15d7ad9d0d0bda66884afd211b7b579b8fa31940e05b060934aa7077d8 languageName: node linkType: hard @@ -6359,15 +6474,15 @@ __metadata: linkType: hard "eslint-plugin-jest@npm:^29.0.1": - version: 29.15.4 - resolution: "eslint-plugin-jest@npm:29.15.4" + version: 29.16.0 + resolution: "eslint-plugin-jest@npm:29.16.0" dependencies: "@typescript-eslint/utils": "npm:^8.0.0" peerDependencies: "@typescript-eslint/eslint-plugin": ^8.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 jest: "*" - typescript: ">=4.8.4 <7.0.0" + typescript: ">=4.8.4 <8.0.0" peerDependenciesMeta: "@typescript-eslint/eslint-plugin": optional: true @@ -6375,16 +6490,16 @@ __metadata: optional: true typescript: optional: true - checksum: 10c0/4a32cd29cb67b54e5fb5f4acbc749e79c024f50099825e91c93d1d9674caf5b4bfe4810c7931e01d6b30e77cbf72d7619db785a12f459f2b23f5885d39cb1033 + checksum: 10c0/ac3b0b0502c48285063a968219852979b0526f814bee90c65ef417e495b270a5aee437961ab51941caa1d0ae4ae5387a55374946165c17cf5e4f001972e0de44 languageName: node linkType: hard "eslint-plugin-prettier@npm:^5.5.4": - version: 5.5.4 - resolution: "eslint-plugin-prettier@npm:5.5.4" + version: 5.5.6 + resolution: "eslint-plugin-prettier@npm:5.5.6" dependencies: - prettier-linter-helpers: "npm:^1.0.0" - synckit: "npm:^0.11.7" + prettier-linter-helpers: "npm:^1.0.1" + synckit: "npm:^0.11.13" peerDependencies: "@types/eslint": ">=8.0.0" eslint: ">=8.0.0" @@ -6395,7 +6510,7 @@ __metadata: optional: true eslint-config-prettier: optional: true - checksum: 10c0/5cc780e0ab002f838ad8057409e86de4ff8281aa2704a50fa8511abff87028060c2e45741bc9cbcbd498712e8d189de8026e70aed9e20e50fe5ba534ee5a8442 + checksum: 10c0/af37126c947ff3e87ff1ea76408db8cb1c7da966ebdaba68c6dd5924da7eb1b43b1abc4796d753a97b1bc26ea94c5497093e6ab9f8588fffe558d5a554e19bbf languageName: node linkType: hard @@ -6509,22 +6624,22 @@ __metadata: linkType: hard "eslint@npm:^9.35.0": - version: 9.39.2 - resolution: "eslint@npm:9.39.2" + version: 9.39.5 + resolution: "eslint@npm:9.39.5" dependencies: "@eslint-community/eslint-utils": "npm:^4.8.0" "@eslint-community/regexpp": "npm:^4.12.1" - "@eslint/config-array": "npm:^0.21.1" + "@eslint/config-array": "npm:^0.21.2" "@eslint/config-helpers": "npm:^0.4.2" "@eslint/core": "npm:^0.17.0" - "@eslint/eslintrc": "npm:^3.3.1" - "@eslint/js": "npm:9.39.2" + "@eslint/eslintrc": "npm:^3.3.6" + "@eslint/js": "npm:9.39.5" "@eslint/plugin-kit": "npm:^0.4.1" "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" "@humanwhocodes/retry": "npm:^0.4.2" "@types/estree": "npm:^1.0.6" - ajv: "npm:^6.12.4" + ajv: "npm:^6.14.0" chalk: "npm:^4.0.0" cross-spawn: "npm:^7.0.6" debug: "npm:^4.3.2" @@ -6543,7 +6658,7 @@ __metadata: is-glob: "npm:^4.0.0" json-stable-stringify-without-jsonify: "npm:^1.0.1" lodash.merge: "npm:^4.6.2" - minimatch: "npm:^3.1.2" + minimatch: "npm:^3.1.5" natural-compare: "npm:^1.4.0" optionator: "npm:^0.9.3" peerDependencies: @@ -6553,7 +6668,7 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10c0/bb88ca8fd16bb7e1ac3e13804c54d41c583214460c0faa7b3e7c574e69c5600c7122295500fb4b0c06067831111db740931e98da1340329527658e1cf80073d3 + checksum: 10c0/46335bfcf180ffea9955b38122b578ac9e075b6220e5695be1c988354ea429b04f5db05edc132aa9019ce9847736e9ca0c9ea6d6cedda3c06209c9b52dbd0fd5 languageName: node linkType: hard @@ -6716,10 +6831,10 @@ __metadata: languageName: node linkType: hard -"exsolve@npm:^1.0.7, exsolve@npm:^1.0.8": - version: 1.0.8 - resolution: "exsolve@npm:1.0.8" - checksum: 10c0/65e44ae05bd4a4a5d87cfdbbd6b8f24389282cf9f85fa5feb17ca87ad3f354877e6af4cd99e02fc29044174891f82d1d68c77f69234410eb8f163530e6278c67 +"exsolve@npm:^1.0.8": + version: 1.1.1 + resolution: "exsolve@npm:1.1.1" + checksum: 10c0/0b157a7600408bd356ff78cff5ad9c8c0dd7ca5e7f246216c3c9b66196e76277d3130021122b9c5078a3462751fd7aa988fe3dc1e3b80c79cb873c1623f628e1 languageName: node linkType: hard @@ -6772,20 +6887,20 @@ __metadata: linkType: hard "fast-uri@npm:^3.0.1": - version: 3.1.0 - resolution: "fast-uri@npm:3.1.0" - checksum: 10c0/44364adca566f70f40d1e9b772c923138d47efeac2ae9732a872baafd77061f26b097ba2f68f0892885ad177becd065520412b8ffeec34b16c99433c5b9e2de7 + version: 3.1.5 + resolution: "fast-uri@npm:3.1.5" + checksum: 10c0/2bf60eb800dd610c65e17be436425dcb21c92aff3a87d442a8bccab0b7b071e88cf1a5d7d1ea946370b937e6fc0375c405c0296c10587e57de4f78be4646d1d0 languageName: node linkType: hard "fast-xml-parser@npm:^4.4.1": - version: 4.5.3 - resolution: "fast-xml-parser@npm:4.5.3" + version: 4.5.7 + resolution: "fast-xml-parser@npm:4.5.7" dependencies: - strnum: "npm:^1.1.1" + strnum: "npm:^1.0.5" bin: fxparser: src/cli/cli.js - checksum: 10c0/bf9ccadacfadc95f6e3f0e7882a380a7f219cf0a6f96575149f02cb62bf44c3b7f0daee75b8ff3847bcfd7fbcb201e402c71045936c265cf6d94b141ec4e9327 + checksum: 10c0/5fccf3f53d6b2b83143d73089f04ef5db5343422891193cf10d92d3eb856007b7337818494109e46f6fa47b31b9716be15c4b275ff87a0aeb6f7315aa2edc181 languageName: node linkType: hard @@ -6919,9 +7034,9 @@ __metadata: linkType: hard "flatted@npm:^3.2.9": - version: 3.3.3 - resolution: "flatted@npm:3.3.3" - checksum: 10c0/e957a1c6b0254aa15b8cce8533e24165abd98fadc98575db082b786b5da1b7d72062b81bfdcd1da2f4d46b6ed93bec2434e62333e9b4261d79ef2e75a10dd538 + version: 3.4.4 + resolution: "flatted@npm:3.4.4" + checksum: 10c0/a3a52a88ea5a4c333e5a1f097dcd87a037fa31c236a77cf46222c2aa4036ef895ab9bc76138a66d9dc22bdb3652128f9598cd26e7439561bf19f67276e2bc37e languageName: node linkType: hard @@ -6980,15 +7095,6 @@ __metadata: languageName: node linkType: hard -"fs-minipass@npm:^3.0.0": - version: 3.0.3 - resolution: "fs-minipass@npm:3.0.3" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10c0/63e80da2ff9b621e2cb1596abcb9207f1cf82b968b116ccd7b959e3323144cce7fb141462200971c38bbf2ecca51695069db45265705bed09a7cd93ae5b89f94 - languageName: node - linkType: hard - "fs.realpath@npm:^1.0.0": version: 1.0.0 resolution: "fs.realpath@npm:1.0.0" @@ -7023,16 +7129,19 @@ __metadata: linkType: hard "function.prototype.name@npm:^1.1.6, function.prototype.name@npm:^1.1.8": - version: 1.1.8 - resolution: "function.prototype.name@npm:1.1.8" + version: 1.2.0 + resolution: "function.prototype.name@npm:1.2.0" dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.3" - define-properties: "npm:^1.2.1" + call-bind: "npm:^1.0.9" + call-bound: "npm:^1.0.4" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" functions-have-names: "npm:^1.2.3" - hasown: "npm:^2.0.2" + has-property-descriptors: "npm:^1.0.2" + hasown: "npm:^2.0.4" is-callable: "npm:^1.2.7" - checksum: 10c0/e920a2ab52663005f3cbe7ee3373e3c71c1fb5558b0b0548648cdf3e51961085032458e26c71ff1a8c8c20e7ee7caeb03d43a5d1fa8610c459333323a2e71253 + is-document.all: "npm:^1.0.0" + checksum: 10c0/b20e6370ef4f7d56d0bedf5719f6684a517a8dd3334209b4d9f51e8834859302a584187156bf024cda9f50ba2479e4d6764ac34af9532ea47d2f4d9fa6bcf90d languageName: node linkType: hard @@ -7064,20 +7173,13 @@ __metadata: languageName: node linkType: hard -"get-east-asian-width@npm:^1.0.0": +"get-east-asian-width@npm:^1.0.0, get-east-asian-width@npm:^1.5.0": version: 1.6.0 resolution: "get-east-asian-width@npm:1.6.0" checksum: 10c0/7e72e9550fd49ca5b246f9af6bb2afc129c96412845ff6556b3274fd44817a381702ca17028efe9866b261a3d44254cbf21e6c90cf05b4b61675630af776d431 languageName: node linkType: hard -"get-east-asian-width@npm:^1.3.0": - version: 1.4.0 - resolution: "get-east-asian-width@npm:1.4.0" - checksum: 10c0/4e481d418e5a32061c36fbb90d1b225a254cc5b2df5f0b25da215dcd335a3c111f0c2023ffda43140727a9cafb62dac41d022da82c08f31083ee89f714ee3b83 - languageName: node - linkType: hard - "get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": version: 1.3.1 resolution: "get-intrinsic@npm:1.3.1" @@ -7251,13 +7353,13 @@ __metadata: linkType: hard "glob@npm:^13.0.0": - version: 13.0.0 - resolution: "glob@npm:13.0.0" + version: 13.0.6 + resolution: "glob@npm:13.0.6" dependencies: - minimatch: "npm:^10.1.1" - minipass: "npm:^7.1.2" - path-scurry: "npm:^2.0.0" - checksum: 10c0/8e2f5821f3f7c312dd102e23a15b80c79e0837a9872784293ba2e15ec73b3f3749a49a42a31bfcb4e52c84820a474e92331c2eebf18819d20308f5c33876630a + minimatch: "npm:^10.2.2" + minipass: "npm:^7.1.3" + path-scurry: "npm:^2.0.2" + checksum: 10c0/269c236f11a9b50357fe7a8c6aadac667e01deb5242b19c84975628f05f4438d8ee1354bb62c5d6c10f37fd59911b54d7799730633a2786660d8c69f1d18120a languageName: node linkType: hard @@ -7344,8 +7446,8 @@ __metadata: linkType: hard "handlebars@npm:^4.7.7": - version: 4.7.8 - resolution: "handlebars@npm:4.7.8" + version: 4.7.9 + resolution: "handlebars@npm:4.7.9" dependencies: minimist: "npm:^1.2.5" neo-async: "npm:^2.6.2" @@ -7357,7 +7459,7 @@ __metadata: optional: true bin: handlebars: bin/handlebars - checksum: 10c0/7aff423ea38a14bb379316f3857fe0df3c5d66119270944247f155ba1f08e07a92b340c58edaa00cfe985c21508870ee5183e0634dcb53dd405f35c93ef7f10d + checksum: 10c0/22f8105a7e68e81aff2662bb434edf05f757d21d850731d71cec886d69c10cd33d3c43e34b2892968ec62de8241611851d3d0674c8ef324ea3e01dc66262faa9 languageName: node linkType: hard @@ -7409,12 +7511,12 @@ __metadata: languageName: node linkType: hard -"hasown@npm:^2.0.2": - version: 2.0.2 - resolution: "hasown@npm:2.0.2" +"hasown@npm:^2.0.2, hasown@npm:^2.0.3, hasown@npm:^2.0.4": + version: 2.0.4 + resolution: "hasown@npm:2.0.4" dependencies: function-bind: "npm:^1.1.2" - checksum: 10c0/3769d434703b8ac66b209a4cca0737519925bbdb61dd887f93a16372b14694c63ff4e797686d87c90f08168e81082248b9b028bad60d4da9e0d1148766f56eb9 + checksum: 10c0/2d8de939e270b70618f8cebb69746620db10617dbb495bc66ddad326955ea24d3ca4af133aff3eb7c1853e0218f867bc2b050ec26fe02e3aea58f880ffc5e506 languageName: node linkType: hard @@ -7505,13 +7607,6 @@ __metadata: languageName: node linkType: hard -"http-cache-semantics@npm:^4.1.1": - version: 4.2.0 - resolution: "http-cache-semantics@npm:4.2.0" - checksum: 10c0/45b66a945cf13ec2d1f29432277201313babf4a01d9e52f44b31ca923434083afeca03f18417f599c9ab3d0e7b618ceb21257542338b57c54b710463b4a53e37 - languageName: node - linkType: hard - "http-errors@npm:~2.0.1": version: 2.0.1 resolution: "http-errors@npm:2.0.1" @@ -7535,7 +7630,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.5, https-proxy-agent@npm:^7.0.6": +"https-proxy-agent@npm:^7.0.5, https-proxy-agent@npm:^7.0.6": version: 7.0.6 resolution: "https-proxy-agent@npm:7.0.6" dependencies: @@ -7566,21 +7661,12 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:^0.6.2": - version: 0.6.3 - resolution: "iconv-lite@npm:0.6.3" - dependencies: - safer-buffer: "npm:>= 2.1.2 < 3.0.0" - checksum: 10c0/98102bc66b33fcf5ac044099d1257ba0b7ad5e3ccd3221f34dd508ab4070edff183276221684e1e0555b145fce0850c9f7d2b60a9fcac50fbb4ea0d6e845a3b1 - languageName: node - linkType: hard - "iconv-lite@npm:^0.7.0": - version: 0.7.1 - resolution: "iconv-lite@npm:0.7.1" + version: 0.7.3 + resolution: "iconv-lite@npm:0.7.3" dependencies: safer-buffer: "npm:>= 2.1.2 < 3.0.0" - checksum: 10c0/f5c9e2bddd7101a71b07a381ace44ebdc65ca76a10be0e9e64d372b511132acc4ee41b830962f438840d492cd6f9e08c237289f760d6a7fed754e61cffcb6757 + checksum: 10c0/be2fd2414f7e94be3a63063fa0ad5919c16bc7b6e00c77eea0765ced6db405739f38dd51016583058fba25cc1d0106ba30b83fbb7a7e32f824d4db76f23d8d33 languageName: node linkType: hard @@ -7607,14 +7693,7 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^7.0.3": - version: 7.0.5 - resolution: "ignore@npm:7.0.5" - checksum: 10c0/ae00db89fe873064a093b8999fe4cc284b13ef2a178636211842cceb650b9c3e390d3339191acb145d81ed5379d2074840cf0c33a20bdbd6f32821f79eb4ad5d - languageName: node - linkType: hard - -"ignore@npm:^7.0.5": +"ignore@npm:^7.0.3, ignore@npm:^7.0.5": version: 7.0.6 resolution: "ignore@npm:7.0.6" checksum: 10c0/fc01ef1d14efbe003439b60538726351e81483d1b6f55bdbb3a4465c6346d9481afad5b350dfbd604ddd7049618ef9093ff26dc147984aabc303c56ba53ea3b5 @@ -7739,10 +7818,10 @@ __metadata: languageName: node linkType: hard -"ip-address@npm:^10.0.1": - version: 10.1.0 - resolution: "ip-address@npm:10.1.0" - checksum: 10c0/0103516cfa93f6433b3bd7333fa876eb21263912329bfa47010af5e16934eeeff86f3d2ae700a3744a137839ddfad62b900c7a445607884a49b5d1e32a3d7566 +"ip-address@npm:^10.1.1": + version: 10.4.0 + resolution: "ip-address@npm:10.4.0" + checksum: 10c0/d7b0bd2624fd861afbae6e49036a9b56f9506eaff7ff38592b7b4492dd5c272b53f5356dc8cc019e318acf29a96c90a3d1af1df761960267d23efa96858270fa languageName: node linkType: hard @@ -7820,12 +7899,12 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.13.0, is-core-module@npm:^2.16.1": - version: 2.16.1 - resolution: "is-core-module@npm:2.16.1" +"is-core-module@npm:^2.16.1, is-core-module@npm:^2.16.2": + version: 2.16.2 + resolution: "is-core-module@npm:2.16.2" dependencies: - hasown: "npm:^2.0.2" - checksum: 10c0/898443c14780a577e807618aaae2b6f745c8538eca5c7bc11388a3f2dc6de82b9902bcc7eb74f07be672b11bbe82dd6a6edded44a00cb3d8f933d0459905eedd + hasown: "npm:^2.0.3" + checksum: 10c0/14b4258390283709c15476d023ec173e27458d5d014ccdb8ed39d576e551c3fa45498b7c9fe178f1529c4cb2648ddd58852a6a62107a019f6e349529f277518a languageName: node linkType: hard @@ -7840,7 +7919,7 @@ __metadata: languageName: node linkType: hard -"is-date-object@npm:^1.0.5, is-date-object@npm:^1.1.0": +"is-date-object@npm:^1.1.0": version: 1.1.0 resolution: "is-date-object@npm:1.1.0" dependencies: @@ -7868,6 +7947,15 @@ __metadata: languageName: node linkType: hard +"is-document.all@npm:^1.0.0": + version: 1.0.0 + resolution: "is-document.all@npm:1.0.0" + dependencies: + call-bound: "npm:^1.0.4" + checksum: 10c0/955c20ed5bf01d49da8243b4c714947a6ff64b6d9ba0e12bdbfa654a3e7c47f72cc01c6cd2905e85512d02bc3a1290edd73857bca8842566ff9dcfb7c3f92dae + languageName: node + linkType: hard + "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -8108,7 +8196,7 @@ __metadata: languageName: node linkType: hard -"is-symbol@npm:^1.0.4, is-symbol@npm:^1.1.1": +"is-symbol@npm:^1.1.1": version: 1.1.1 resolution: "is-symbol@npm:1.1.1" dependencies: @@ -8210,11 +8298,11 @@ __metadata: linkType: hard "is-wsl@npm:^3.1.0": - version: 3.1.0 - resolution: "is-wsl@npm:3.1.0" + version: 3.1.1 + resolution: "is-wsl@npm:3.1.1" dependencies: is-inside-container: "npm:^1.0.0" - checksum: 10c0/d3317c11995690a32c362100225e22ba793678fe8732660c6de511ae71a0ff05b06980cf21f98a6bf40d7be0e9e9506f859abe00a1118287d63e53d0a3d06947 + checksum: 10c0/7e5023522bfb8f27de4de960b0d82c4a8146c0bddb186529a3616d78b5bbbfc19ef0c5fc60d0b3a3cc0bf95a415fbdedc18454310ea3049587c879b07ace5107 languageName: node linkType: hard @@ -8232,10 +8320,10 @@ __metadata: languageName: node linkType: hard -"isexe@npm:^3.1.1": - version: 3.1.1 - resolution: "isexe@npm:3.1.1" - checksum: 10c0/9ec257654093443eb0a528a9c8cbba9c0ca7616ccb40abd6dde7202734d96bb86e4ac0d764f0f8cd965856aacbff2f4ce23e730dc19dfb41e3b0d865ca6fdcc7 +"isexe@npm:^4.0.0": + version: 4.0.0 + resolution: "isexe@npm:4.0.0" + checksum: 10c0/5884815115bceac452877659a9c7726382531592f43dc29e5d48b7c4100661aed54018cb90bd36cb2eaeba521092570769167acbb95c18d39afdccbcca06c5ce languageName: node linkType: hard @@ -8783,7 +8871,7 @@ __metadata: languageName: node linkType: hard -"jiti@npm:^2.6.1": +"jiti@npm:2.6.1": version: 2.6.1 resolution: "jiti@npm:2.6.1" bin: @@ -8792,16 +8880,25 @@ __metadata: languageName: node linkType: hard +"jiti@npm:^2.6.1": + version: 2.7.0 + resolution: "jiti@npm:2.7.0" + bin: + jiti: lib/jiti-cli.mjs + checksum: 10c0/1b1e2310a490dce1aeea3da5f5dfe18273516c20ce48be2e98eb8ea452d5f3dcc8fd0cfd6d28b4052a24c5dbab6e3089b2d7e79f0bce7915b10d750929563c42 + languageName: node + linkType: hard + "joi@npm:^17.2.1": - version: 17.13.3 - resolution: "joi@npm:17.13.3" + version: 17.13.4 + resolution: "joi@npm:17.13.4" dependencies: "@hapi/hoek": "npm:^9.3.0" "@hapi/topo": "npm:^5.1.0" "@sideway/address": "npm:^4.1.5" "@sideway/formula": "npm:^3.0.1" "@sideway/pinpoint": "npm:^2.0.0" - checksum: 10c0/9262aef1da3f1bec5b03caf50c46368899fe03b8ff26cbe3d53af4584dd1049079fc97230bbf1500b6149db7cc765b9ee45f0deb24bb6fc3fa06229d7148c17f + checksum: 10c0/5addfef638e90eb6a45a72e3884128d8d9a80c487cb8bfcb949be179b875ad1095af347fa1bd05c4ebec2b2ccbc3f3db5f34501c8672ecd16100de2b77087122 languageName: node linkType: hard @@ -8813,25 +8910,25 @@ __metadata: linkType: hard "js-yaml@npm:^3.13.1": - version: 3.14.2 - resolution: "js-yaml@npm:3.14.2" + version: 3.15.1 + resolution: "js-yaml@npm:3.15.1" dependencies: argparse: "npm:^1.0.7" esprima: "npm:^4.0.0" bin: js-yaml: bin/js-yaml.js - checksum: 10c0/3261f25912f5dd76605e5993d0a126c2b6c346311885d3c483706cd722efe34f697ea0331f654ce27c00a42b426e524518ec89d65ed02ea47df8ad26dcc8ce69 + checksum: 10c0/6c693b8c59ffe12021df3b36994b090df19d920826dd810baab81652b40861e1974509dd11fe92225ab4c00cc14de053300c94db015c2c0e211edea39b118737 languageName: node linkType: hard -"js-yaml@npm:^4.1.0, js-yaml@npm:^4.1.1": - version: 4.1.1 - resolution: "js-yaml@npm:4.1.1" +"js-yaml@npm:^4.1.0, js-yaml@npm:^4.1.1, js-yaml@npm:^4.3.0": + version: 4.3.1 + resolution: "js-yaml@npm:4.3.1" dependencies: argparse: "npm:^2.0.1" bin: js-yaml: bin/js-yaml.js - checksum: 10c0/561c7d7088c40a9bb53cc75becbfb1df6ae49b34b5e6e5a81744b14ae8667ec564ad2527709d1a6e7d5e5fa6d483aa0f373a50ad98d42fde368ec4a190d4fae7 + checksum: 10c0/13c500ca322e0c3f8c81686e6ecda96d2ea37b45247a420c17c7db36932d6965cc27391abc2d1a104501600e7f0d947a5f8b7be6db619c4fefa87901b3512807 languageName: node linkType: hard @@ -8908,15 +9005,15 @@ __metadata: linkType: hard "jsonfile@npm:^6.0.1": - version: 6.2.0 - resolution: "jsonfile@npm:6.2.0" + version: 6.2.1 + resolution: "jsonfile@npm:6.2.1" dependencies: graceful-fs: "npm:^4.1.6" universalify: "npm:^2.0.0" dependenciesMeta: graceful-fs: optional: true - checksum: 10c0/7f4f43b08d1869ded8a6822213d13ae3b99d651151d77efd1557ced0889c466296a7d9684e397bd126acf5eb2cfcb605808c3e681d0fdccd2fe5a04b47e76c0d + checksum: 10c0/e1abf000ecee9942d4d028a8e02dc752617face227d72afd1cfb2187e2433079e625bf82b807a313689db71b6472c6b2b389a2340d2798737b1199a39631c28a languageName: node linkType: hard @@ -8963,99 +9060,99 @@ __metadata: linkType: hard "launch-editor@npm:^2.9.1": - version: 2.12.0 - resolution: "launch-editor@npm:2.12.0" + version: 2.14.1 + resolution: "launch-editor@npm:2.14.1" dependencies: picocolors: "npm:^1.1.1" - shell-quote: "npm:^1.8.3" - checksum: 10c0/fac5e7ad90bf185594cad4c831a52419eef50e667c4eddb5b0a58eb5f944e16d947636ee767b9896ffd46a51db34925edd3b854c48efb47f6d767ffd7d904e71 + shell-quote: "npm:^1.8.4" + checksum: 10c0/bb0ab182086afaf1c391abd38d6fc9d5391cb43d0c2da1d96176f79e9995635cdf34dcfd8df3f756bb82d7bbbb7d37014d5eb312f337350889c0cff92db53dab languageName: node linkType: hard -"lefthook-darwin-arm64@npm:2.0.13": - version: 2.0.13 - resolution: "lefthook-darwin-arm64@npm:2.0.13" +"lefthook-darwin-arm64@npm:2.1.10": + version: 2.1.10 + resolution: "lefthook-darwin-arm64@npm:2.1.10" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"lefthook-darwin-x64@npm:2.0.13": - version: 2.0.13 - resolution: "lefthook-darwin-x64@npm:2.0.13" +"lefthook-darwin-x64@npm:2.1.10": + version: 2.1.10 + resolution: "lefthook-darwin-x64@npm:2.1.10" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"lefthook-freebsd-arm64@npm:2.0.13": - version: 2.0.13 - resolution: "lefthook-freebsd-arm64@npm:2.0.13" +"lefthook-freebsd-arm64@npm:2.1.10": + version: 2.1.10 + resolution: "lefthook-freebsd-arm64@npm:2.1.10" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"lefthook-freebsd-x64@npm:2.0.13": - version: 2.0.13 - resolution: "lefthook-freebsd-x64@npm:2.0.13" +"lefthook-freebsd-x64@npm:2.1.10": + version: 2.1.10 + resolution: "lefthook-freebsd-x64@npm:2.1.10" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"lefthook-linux-arm64@npm:2.0.13": - version: 2.0.13 - resolution: "lefthook-linux-arm64@npm:2.0.13" +"lefthook-linux-arm64@npm:2.1.10": + version: 2.1.10 + resolution: "lefthook-linux-arm64@npm:2.1.10" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"lefthook-linux-x64@npm:2.0.13": - version: 2.0.13 - resolution: "lefthook-linux-x64@npm:2.0.13" +"lefthook-linux-x64@npm:2.1.10": + version: 2.1.10 + resolution: "lefthook-linux-x64@npm:2.1.10" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"lefthook-openbsd-arm64@npm:2.0.13": - version: 2.0.13 - resolution: "lefthook-openbsd-arm64@npm:2.0.13" +"lefthook-openbsd-arm64@npm:2.1.10": + version: 2.1.10 + resolution: "lefthook-openbsd-arm64@npm:2.1.10" conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard -"lefthook-openbsd-x64@npm:2.0.13": - version: 2.0.13 - resolution: "lefthook-openbsd-x64@npm:2.0.13" +"lefthook-openbsd-x64@npm:2.1.10": + version: 2.1.10 + resolution: "lefthook-openbsd-x64@npm:2.1.10" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"lefthook-windows-arm64@npm:2.0.13": - version: 2.0.13 - resolution: "lefthook-windows-arm64@npm:2.0.13" +"lefthook-windows-arm64@npm:2.1.10": + version: 2.1.10 + resolution: "lefthook-windows-arm64@npm:2.1.10" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"lefthook-windows-x64@npm:2.0.13": - version: 2.0.13 - resolution: "lefthook-windows-x64@npm:2.0.13" +"lefthook-windows-x64@npm:2.1.10": + version: 2.1.10 + resolution: "lefthook-windows-x64@npm:2.1.10" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "lefthook@npm:^2.0.3": - version: 2.0.13 - resolution: "lefthook@npm:2.0.13" - dependencies: - lefthook-darwin-arm64: "npm:2.0.13" - lefthook-darwin-x64: "npm:2.0.13" - lefthook-freebsd-arm64: "npm:2.0.13" - lefthook-freebsd-x64: "npm:2.0.13" - lefthook-linux-arm64: "npm:2.0.13" - lefthook-linux-x64: "npm:2.0.13" - lefthook-openbsd-arm64: "npm:2.0.13" - lefthook-openbsd-x64: "npm:2.0.13" - lefthook-windows-arm64: "npm:2.0.13" - lefthook-windows-x64: "npm:2.0.13" + version: 2.1.10 + resolution: "lefthook@npm:2.1.10" + dependencies: + lefthook-darwin-arm64: "npm:2.1.10" + lefthook-darwin-x64: "npm:2.1.10" + lefthook-freebsd-arm64: "npm:2.1.10" + lefthook-freebsd-x64: "npm:2.1.10" + lefthook-linux-arm64: "npm:2.1.10" + lefthook-linux-x64: "npm:2.1.10" + lefthook-openbsd-arm64: "npm:2.1.10" + lefthook-openbsd-x64: "npm:2.1.10" + lefthook-windows-arm64: "npm:2.1.10" + lefthook-windows-x64: "npm:2.1.10" dependenciesMeta: lefthook-darwin-arm64: optional: true @@ -9079,7 +9176,7 @@ __metadata: optional: true bin: lefthook: bin/index.js - checksum: 10c0/11dc5a952c24325b19803c261331f1e12ea8eaf31c0ab131815955a6e7210323828995c2b9cc73b79c23de960beda27ab71de88afdf00e40f367c6b13cee1ce3 + checksum: 10c0/be812e1517a967977750a41f15a2070cea63e1774137737426f636e1bd379f4dfc73482fa7f07df6b9a1413ee50c6e2d790936d7986bdcafd46255d871c17b76 languageName: node linkType: hard @@ -9250,9 +9347,9 @@ __metadata: linkType: hard "lodash@npm:^4.15.0, lodash@npm:^4.17.21": - version: 4.17.21 - resolution: "lodash@npm:4.17.21" - checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c + version: 4.18.1 + resolution: "lodash@npm:4.18.1" + checksum: 10c0/757228fc68805c59789e82185135cf85f05d0b2d3d54631d680ca79ec21944ec8314d4533639a14b8bcfbd97a517e78960933041a5af17ecb693ec6eecb99a27 languageName: node linkType: hard @@ -9307,10 +9404,10 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1": - version: 11.2.4 - resolution: "lru-cache@npm:11.2.4" - checksum: 10c0/4a24f9b17537619f9144d7b8e42cd5a225efdfd7076ebe7b5e7dc02b860a818455201e67fbf000765233fe7e339d3c8229fc815e9b58ee6ede511e07608c19b2 +"lru-cache@npm:^11.0.0": + version: 11.5.2 + resolution: "lru-cache@npm:11.5.2" + checksum: 10c0/ece1ad731f5b655e85d67047d04bfc13823dc77aa61c5454924a9869ba600a0104e39cc33d726021feef812bc347ca34a5608a5eb1972a5dd0870b7ecd42c3f2 languageName: node linkType: hard @@ -9342,9 +9439,9 @@ __metadata: linkType: hard "macos-release@npm:^3.3.0": - version: 3.4.0 - resolution: "macos-release@npm:3.4.0" - checksum: 10c0/cb6ea203cc2a2b2cc2214db4658d0da0e52f8298c5c43c94cf9cb9e871daac59e4e56a2559859727a4b43b0afec1123f998ef62c58d1ac6c6c8a5c8a808330cb + version: 3.5.1 + resolution: "macos-release@npm:3.5.1" + checksum: 10c0/a940167179ba1eeb0b514c447f0c2cc40e7301289ba0941e3c98b80262e34d4790bba73e4e99b215f44909e38f841fac72f93c89a94851d58431d275981979f8 languageName: node linkType: hard @@ -9357,25 +9454,6 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^15.0.0": - version: 15.0.3 - resolution: "make-fetch-happen@npm:15.0.3" - dependencies: - "@npmcli/agent": "npm:^4.0.0" - cacache: "npm:^20.0.1" - http-cache-semantics: "npm:^4.1.1" - minipass: "npm:^7.0.2" - minipass-fetch: "npm:^5.0.0" - minipass-flush: "npm:^1.0.5" - minipass-pipeline: "npm:^1.2.4" - negotiator: "npm:^1.0.0" - proc-log: "npm:^6.0.0" - promise-retry: "npm:^2.0.1" - ssri: "npm:^13.0.0" - checksum: 10c0/525f74915660be60b616bcbd267c4a5b59481b073ba125e45c9c3a041bb1a47a2bd0ae79d028eb6f5f95bf9851a4158423f5068539c3093621abb64027e8e461 - languageName: node - linkType: hard - "makeerror@npm:1.0.12": version: 1.0.12 resolution: "makeerror@npm:1.0.12" @@ -9754,38 +9832,29 @@ __metadata: linkType: hard "minimatch@npm:^10.0.1, minimatch@npm:^10.2.2": - version: 10.2.5 - resolution: "minimatch@npm:10.2.5" - dependencies: - brace-expansion: "npm:^5.0.5" - checksum: 10c0/6bb058bd6324104b9ec2f763476a35386d05079c1f5fe4fbf1f324a25237cd4534d6813ecd71f48208f4e635c1221899bef94c3c89f7df55698fe373aaae20fd - languageName: node - linkType: hard - -"minimatch@npm:^10.1.1": - version: 10.1.1 - resolution: "minimatch@npm:10.1.1" + version: 10.2.6 + resolution: "minimatch@npm:10.2.6" dependencies: - "@isaacs/brace-expansion": "npm:^5.0.0" - checksum: 10c0/c85d44821c71973d636091fddbfbffe62370f5ee3caf0241c5b60c18cd289e916200acb2361b7e987558cd06896d153e25d505db9fc1e43e6b4b6752e2702902 + brace-expansion: "npm:^5.0.8" + checksum: 10c0/4559a836243b98bd4d17ea9f7edae698717c76399eea7be374f3737f33164e4907f19e9726891ddeb122f750a5a7fa80d2ac43e851d6e5984dc4ff42ec127d3a languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": - version: 3.1.2 - resolution: "minimatch@npm:3.1.2" +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2, minimatch@npm:^3.1.5": + version: 3.1.5 + resolution: "minimatch@npm:3.1.5" dependencies: brace-expansion: "npm:^1.1.7" - checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311 + checksum: 10c0/2ecbdc0d33f07bddb0315a8b5afbcb761307a8778b48f0b312418ccbced99f104a2d17d8aca7573433c70e8ccd1c56823a441897a45e384ea76ef401a26ace70 languageName: node linkType: hard "minimatch@npm:^9.0.4": - version: 9.0.5 - resolution: "minimatch@npm:9.0.5" + version: 9.0.9 + resolution: "minimatch@npm:9.0.9" dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed + brace-expansion: "npm:^2.0.2" + checksum: 10c0/0b6a58530dbb00361745aa6c8cffaba4c90f551afe7c734830bd95fd88ebf469dd7355a027824ea1d09e37181cfeb0a797fb17df60c15ac174303ac110eb7e86 languageName: node linkType: hard @@ -9796,74 +9865,14 @@ __metadata: languageName: node linkType: hard -"minipass-collect@npm:^2.0.1": - version: 2.0.1 - resolution: "minipass-collect@npm:2.0.1" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10c0/5167e73f62bb74cc5019594709c77e6a742051a647fe9499abf03c71dca75515b7959d67a764bdc4f8b361cf897fbf25e2d9869ee039203ed45240f48b9aa06e - languageName: node - linkType: hard - -"minipass-fetch@npm:^5.0.0": - version: 5.0.0 - resolution: "minipass-fetch@npm:5.0.0" - dependencies: - encoding: "npm:^0.1.13" - minipass: "npm:^7.0.3" - minipass-sized: "npm:^1.0.3" - minizlib: "npm:^3.0.1" - dependenciesMeta: - encoding: - optional: true - checksum: 10c0/9443aab5feab190972f84b64116e54e58dd87a58e62399cae0a4a7461b80568281039b7c3a38ba96453431ebc799d1e26999e548540156216729a4967cd5ef06 - languageName: node - linkType: hard - -"minipass-flush@npm:^1.0.5": - version: 1.0.5 - resolution: "minipass-flush@npm:1.0.5" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10c0/2a51b63feb799d2bb34669205eee7c0eaf9dce01883261a5b77410c9408aa447e478efd191b4de6fc1101e796ff5892f8443ef20d9544385819093dbb32d36bd - languageName: node - linkType: hard - -"minipass-pipeline@npm:^1.2.4": - version: 1.2.4 - resolution: "minipass-pipeline@npm:1.2.4" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10c0/cbda57cea20b140b797505dc2cac71581a70b3247b84480c1fed5ca5ba46c25ecc25f68bfc9e6dcb1a6e9017dab5c7ada5eab73ad4f0a49d84e35093e0c643f2 - languageName: node - linkType: hard - -"minipass-sized@npm:^1.0.3": - version: 1.0.3 - resolution: "minipass-sized@npm:1.0.3" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10c0/298f124753efdc745cfe0f2bdfdd81ba25b9f4e753ca4a2066eb17c821f25d48acea607dfc997633ee5bf7b6dfffb4eee4f2051eb168663f0b99fad2fa4829cb - languageName: node - linkType: hard - -"minipass@npm:^3.0.0": - version: 3.3.6 - resolution: "minipass@npm:3.3.6" - dependencies: - yallist: "npm:^4.0.0" - checksum: 10c0/a114746943afa1dbbca8249e706d1d38b85ed1298b530f5808ce51f8e9e941962e2a5ad2e00eae7dd21d8a4aae6586a66d4216d1a259385e9d0358f0c1eba16c - languageName: node - linkType: hard - -"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2": - version: 7.1.2 - resolution: "minipass@npm:7.1.2" - checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.4, minipass@npm:^7.1.2, minipass@npm:^7.1.3": + version: 7.1.3 + resolution: "minipass@npm:7.1.3" + checksum: 10c0/539da88daca16533211ea5a9ee98dc62ff5742f531f54640dd34429e621955e91cc280a91a776026264b7f9f6735947629f920944e9c1558369e8bf22eb33fbb languageName: node linkType: hard -"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": +"minizlib@npm:^3.1.0": version: 3.1.0 resolution: "minizlib@npm:3.1.0" dependencies: @@ -9903,11 +9912,11 @@ __metadata: linkType: hard "nanoid@npm:^3.3.11": - version: 3.3.11 - resolution: "nanoid@npm:3.3.11" + version: 3.3.16 + resolution: "nanoid@npm:3.3.16" bin: nanoid: bin/nanoid.cjs - checksum: 10c0/40e7f70b3d15f725ca072dfc4f74e81fcf1fbb02e491cf58ac0c79093adc9b0a73b152bcde57df4b79cd097e13023d7504acb38404a4da7bc1cd8e887b82fe0b + checksum: 10c0/bbf2dcffe22d2b62d16de2711752070b539c0f644c7916f823ad6521986b2078cbe524f2d6240f58c46e9141ea0c7b87a029e100c0f7f175228cacaf30e41bba languageName: node linkType: hard @@ -9947,9 +9956,9 @@ __metadata: linkType: hard "netmask@npm:^2.0.2": - version: 2.0.2 - resolution: "netmask@npm:2.0.2" - checksum: 10c0/cafd28388e698e1138ace947929f842944d0f1c0b87d3fa2601a61b38dc89397d33c0ce2c8e7b99e968584b91d15f6810b91bef3f3826adf71b1833b61d4bf4f + version: 2.1.1 + resolution: "netmask@npm:2.1.1" + checksum: 10c0/c78e31869b0578fb0a9874a0c0fdf0e1f8b3492392d1043355fb11d9ea42ef94e0216c6aee7d8e15db39d1a8caf331f9b144ae3ee43fd951b73a66837711fb09 languageName: node linkType: hard @@ -9984,6 +9993,18 @@ __metadata: languageName: node linkType: hard +"node-exports-info@npm:^1.6.0": + version: 1.6.2 + resolution: "node-exports-info@npm:1.6.2" + dependencies: + array.prototype.flatmap: "npm:^1.3.3" + es-errors: "npm:^1.3.0" + object.entries: "npm:^1.1.9" + semver: "npm:^6.3.1" + checksum: 10c0/5278fab18e8c97f45275c51819c3078ca9488361e875bc01b680776a339d2fee1ca9c6fcfb972df14945a1b184cc9924a1d9ba87e90f6cb3422c7f5b6900f4bc + languageName: node + linkType: hard + "node-fetch-native@npm:^1.6.6": version: 1.6.7 resolution: "node-fetch-native@npm:1.6.7" @@ -9992,22 +10013,22 @@ __metadata: linkType: hard "node-gyp@npm:latest": - version: 12.1.0 - resolution: "node-gyp@npm:12.1.0" + version: 13.0.1 + resolution: "node-gyp@npm:13.0.1" dependencies: env-paths: "npm:^2.2.0" exponential-backoff: "npm:^3.1.1" graceful-fs: "npm:^4.2.6" - make-fetch-happen: "npm:^15.0.0" - nopt: "npm:^9.0.0" - proc-log: "npm:^6.0.0" + nopt: "npm:^10.0.0" + proc-log: "npm:^7.0.0" semver: "npm:^7.3.5" - tar: "npm:^7.5.2" + tar: "npm:^7.5.4" tinyglobby: "npm:^0.2.12" - which: "npm:^6.0.0" + undici: "npm:^8.4.1" + which: "npm:^7.0.0" bin: node-gyp: bin/node-gyp.js - checksum: 10c0/f43efea8aaf0beb6b2f6184e533edad779b2ae38062953e21951f46221dd104006cc574154f2ad4a135467a5aae92c49e84ef289311a82e08481c5df0e8dc495 + checksum: 10c0/424077bc9e9bbe953a8e86db473ba818cbc6a121714008c977fd589e21e5f0c811fbf22faac730dc7182450b5e52df301811d01ae3373898658d999b7710f4e6 languageName: node linkType: hard @@ -10018,28 +10039,28 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.27": - version: 2.0.27 - resolution: "node-releases@npm:2.0.27" - checksum: 10c0/f1e6583b7833ea81880627748d28a3a7ff5703d5409328c216ae57befbced10ce2c991bea86434e8ec39003bd017f70481e2e5f8c1f7e0a7663241f81d6e00e2 +"node-releases@npm:^2.0.51": + version: 2.0.51 + resolution: "node-releases@npm:2.0.51" + checksum: 10c0/6cf3fe1f9eabe02ce6de67e9db77b73edc9f5625003791e1f8f239f8e2a851450a5aeb1eff2cc43cfb26312e19b26bfe07626bf428479e6a76f56553fc6148da languageName: node linkType: hard "node-stream-zip@npm:^1.9.1": - version: 1.15.0 - resolution: "node-stream-zip@npm:1.15.0" - checksum: 10c0/429fce95d7e90e846adbe096c61d2ea8d18defc155c0345d25d0f98dd6fc72aeb95039318484a4e0a01dc3814b6d0d1ae0fe91847a29669dff8676ec064078c9 + version: 1.16.0 + resolution: "node-stream-zip@npm:1.16.0" + checksum: 10c0/3e201e39b42b46c2e543bd11c71821aa675a98d6cb4901840bd952281696599c9c29c630cd00e8da9bee4b02879fee2d0194a225b359d48a3bf966bdd63d8648 languageName: node linkType: hard -"nopt@npm:^9.0.0": - version: 9.0.0 - resolution: "nopt@npm:9.0.0" +"nopt@npm:^10.0.0": + version: 10.0.1 + resolution: "nopt@npm:10.0.1" dependencies: - abbrev: "npm:^4.0.0" + abbrev: "npm:^5.0.0" bin: nopt: bin/nopt.js - checksum: 10c0/1822eb6f9b020ef6f7a7516d7b64a8036e09666ea55ac40416c36e4b2b343122c3cff0e2f085675f53de1d2db99a2a89a60ccea1d120bcd6a5347bf6ceb4a7fd + checksum: 10c0/980d89257f9587f3e1f77877ddbf905d6aa3b738ec33e49a4fa1a059a0dd82eb28063982b150654a7ae9de386f2ead60e56172db7d37cf56de545f7392a2a26a languageName: node linkType: hard @@ -10096,17 +10117,15 @@ __metadata: linkType: hard "nypm@npm:^0.6.0": - version: 0.6.2 - resolution: "nypm@npm:0.6.2" + version: 0.6.9 + resolution: "nypm@npm:0.6.9" dependencies: - citty: "npm:^0.1.6" - consola: "npm:^3.4.2" + citty: "npm:^0.2.2" pathe: "npm:^2.0.3" - pkg-types: "npm:^2.3.0" - tinyexec: "npm:^1.0.1" + tinyexec: "npm:^1.2.4" bin: - nypm: dist/cli.mjs - checksum: 10c0/b1aca658e29ed616ad6e487f9c3fd76773485ad75c1f99efe130ccb304de60b639a3dda43c3ce6c060113a3eebaee7ccbea554f5fbd1f244474181dc9bf3f17c + nypm: ./dist/cli.mjs + checksum: 10c0/8014c1842e554531a5092bae9f483d22413fe5f68fd2e15e74a2b7dc5b94e36cac836b9ebe9a70fd7ccba65a663c517c98663368f5b5e48d6bc65d74f0752d72 languageName: node linkType: hard @@ -10348,13 +10367,14 @@ __metadata: linkType: hard "own-keys@npm:^1.0.1": - version: 1.0.1 - resolution: "own-keys@npm:1.0.1" + version: 1.0.2 + resolution: "own-keys@npm:1.0.2" dependencies: - get-intrinsic: "npm:^1.2.6" + call-bound: "npm:^1.0.4" + get-intrinsic: "npm:^1.3.0" object-keys: "npm:^1.1.1" safe-push-apply: "npm:^1.0.0" - checksum: 10c0/6dfeb3455bff92ec3f16a982d4e3e65676345f6902d9f5ded1d8265a6318d0200ce461956d6d1c70053c7fe9f9fe65e552faac03f8140d37ef0fdd108e67013a + checksum: 10c0/84b0d9959475231166c3d4b9abd1b8af8042911e2e5625761eed980d1a1e4381cf52b34d907cae21ee8766c79de943f3cd85eba636149dee5e64cceff3ccdb78 languageName: node linkType: hard @@ -10422,9 +10442,9 @@ __metadata: linkType: hard "p-map@npm:^7.0.2": - version: 7.0.4 - resolution: "p-map@npm:7.0.4" - checksum: 10c0/a5030935d3cb2919d7e89454d1ce82141e6f9955413658b8c9403cfe379283770ed3048146b44cde168aa9e8c716505f196d5689db0ae3ce9a71521a2fef3abd + version: 7.0.6 + resolution: "p-map@npm:7.0.6" + checksum: 10c0/46e3b3c79b6179613161aa1a28c72cfe7155f27f1928afee1b2406858728f9dd4c29637bba25cef9b50517caacdcb71549f5ff228292524b0069b2310922b22e languageName: node linkType: hard @@ -10574,13 +10594,13 @@ __metadata: languageName: node linkType: hard -"path-scurry@npm:^2.0.0": - version: 2.0.1 - resolution: "path-scurry@npm:2.0.1" +"path-scurry@npm:^2.0.2": + version: 2.0.2 + resolution: "path-scurry@npm:2.0.2" dependencies: lru-cache: "npm:^11.0.0" minipass: "npm:^7.1.2" - checksum: 10c0/2a16ed0e81fbc43513e245aa5763354e25e787dab0d539581a6c3f0f967461a159ed6236b2559de23aa5b88e7dc32b469b6c47568833dd142a4b24b4f5cd2620 + checksum: 10c0/b35ad37cf6557a87fd057121ce2be7695380c9138d93e87ae928609da259ea0a170fac6f3ef1eb3ece8a068e8b7f2f3adf5bb2374cf4d4a57fe484954fcc9482 languageName: node linkType: hard @@ -10606,9 +10626,9 @@ __metadata: linkType: hard "perfect-debounce@npm:^2.0.0": - version: 2.0.0 - resolution: "perfect-debounce@npm:2.0.0" - checksum: 10c0/c08d7bf9c43f262206eceb9b3dfd332e2470b5488bb93f87721c498045e61e4f3f1d21abfe3236a8eab547c7cc031d3efb64cd3b38a85a820d5cafb4c897a8d4 + version: 2.1.0 + resolution: "perfect-debounce@npm:2.1.0" + checksum: 10c0/c4f833816f249129cea996d60b1351b640cf06954ab4eecaca440234ef70f4aefe6483637049750f5b00e15a5036307ea77f831c9779d6ad878457680af49b6d languageName: node linkType: hard @@ -10620,20 +10640,13 @@ __metadata: linkType: hard "picomatch@npm:^2.0.4, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": - version: 2.3.1 - resolution: "picomatch@npm:2.3.1" - checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be + version: 2.3.2 + resolution: "picomatch@npm:2.3.2" + checksum: 10c0/a554d1709e59be97d1acb9eaedbbc700a5c03dbd4579807baed95100b00420bc729335440ef15004ae2378984e2487a7c1cebd743cfdb72b6fa9ab69223c0d61 languageName: node linkType: hard -"picomatch@npm:^4.0.3": - version: 4.0.3 - resolution: "picomatch@npm:4.0.3" - checksum: 10c0/9582c951e95eebee5434f59e426cddd228a7b97a0161a375aed4be244bd3fe8e3a31b846808ea14ef2c8a2527a6eeab7b3946a67d5979e81694654f939473ae2 - languageName: node - linkType: hard - -"picomatch@npm:^4.0.4": +"picomatch@npm:^4.0.3, picomatch@npm:^4.0.4": version: 4.0.5 resolution: "picomatch@npm:4.0.5" checksum: 10c0/947bc6b6e1ff1e6c5aaf95b107a0839d12802f4f7b867663f67d47accba939ca1cb582cf99dfc30438efa1c4648ac5990967e783e8929c36b03e8440704ef1bd @@ -10657,13 +10670,13 @@ __metadata: linkType: hard "pkg-types@npm:^2.3.0": - version: 2.3.0 - resolution: "pkg-types@npm:2.3.0" + version: 2.3.1 + resolution: "pkg-types@npm:2.3.1" dependencies: - confbox: "npm:^0.2.2" - exsolve: "npm:^1.0.7" + confbox: "npm:^0.2.4" + exsolve: "npm:^1.0.8" pathe: "npm:^2.0.3" - checksum: 10c0/d2bbddc5b81bd4741e1529c08ef4c5f1542bbdcf63498b73b8e1d84cff71806d1b8b1577800549bb569cb7aa20056257677b979bff48c97967cba7e64f72ae12 + checksum: 10c0/dd9b20682426abaddcac9dc786aa22542e12df15a03dea2afa7bf54da0933358c6c7f545c0c3c1c7b24a2904ab4a451bf4e34a50c6837e458359eea30c257ed4 languageName: node linkType: hard @@ -10678,7 +10691,7 @@ __metadata: languageName: node linkType: hard -"possible-typed-array-names@npm:^1.0.0": +"possible-typed-array-names@npm:^1.0.0, possible-typed-array-names@npm:^1.1.0": version: 1.1.0 resolution: "possible-typed-array-names@npm:1.1.0" checksum: 10c0/c810983414142071da1d644662ce4caebce890203eb2bc7bf119f37f3fe5796226e117e6cca146b521921fa6531072674174a3325066ac66fce089a53e1e5196 @@ -10699,7 +10712,7 @@ __metadata: languageName: node linkType: hard -"prettier-linter-helpers@npm:^1.0.0": +"prettier-linter-helpers@npm:^1.0.1": version: 1.0.1 resolution: "prettier-linter-helpers@npm:1.0.1" dependencies: @@ -10728,20 +10741,10 @@ __metadata: languageName: node linkType: hard -"proc-log@npm:^6.0.0": - version: 6.1.0 - resolution: "proc-log@npm:6.1.0" - checksum: 10c0/4f178d4062733ead9d71a9b1ab24ebcecdfe2250916a5b1555f04fe2eda972a0ec76fbaa8df1ad9c02707add6749219d118a4fc46dc56bdfe4dde4b47d80bb82 - languageName: node - linkType: hard - -"promise-retry@npm:^2.0.1": - version: 2.0.1 - resolution: "promise-retry@npm:2.0.1" - dependencies: - err-code: "npm:^2.0.2" - retry: "npm:^0.12.0" - checksum: 10c0/9c7045a1a2928094b5b9b15336dcd2a7b1c052f674550df63cc3f36cd44028e5080448175b6f6ca32b642de81150f5e7b1a98b728f15cb069f2dd60ac2616b96 +"proc-log@npm:^7.0.0": + version: 7.0.0 + resolution: "proc-log@npm:7.0.0" + checksum: 10c0/b89c2d862604f35fec795477b0c7e376feab3ba0d4f4d291c4e959567442697cf451ac557d0623c1cc38af45a78128b983410f397a10c5d3a67f76c33de4754b languageName: node linkType: hard @@ -10806,12 +10809,12 @@ __metadata: linkType: hard "pump@npm:^3.0.0": - version: 3.0.3 - resolution: "pump@npm:3.0.3" + version: 3.0.4 + resolution: "pump@npm:3.0.4" dependencies: end-of-stream: "npm:^1.1.0" once: "npm:^1.3.1" - checksum: 10c0/ada5cdf1d813065bbc99aa2c393b8f6beee73b5de2890a8754c9f488d7323ffd2ca5f5a0943b48934e3fcbd97637d0337369c3c631aeb9614915db629f1c75c9 + checksum: 10c0/2780e66b5471c19e3e3e1063b84f3f6a3a08367f24c5ed552f98cd5901e6ada27c7ad6495d4244f553fd03b01884a4561933064f053f47c8994d84fd352768ea languageName: node linkType: hard @@ -10829,12 +10832,13 @@ __metadata: languageName: node linkType: hard -"qs@npm:~6.14.0": - version: 6.14.1 - resolution: "qs@npm:6.14.1" +"qs@npm:~6.15.1": + version: 6.15.3 + resolution: "qs@npm:6.15.3" dependencies: - side-channel: "npm:^1.1.0" - checksum: 10c0/0e3b22dc451f48ce5940cbbc7c7d9068d895074f8c969c0801ac15c1313d1859c4d738e46dc4da2f498f41a9ffd8c201bd9fb12df67799b827db94cc373d2613 + es-define-property: "npm:^1.0.1" + side-channel: "npm:^1.1.1" + checksum: 10c0/8f3f6e45ece255347d57696628401cde29e9ec649fff698b53bd3150dea7cefdf33036e1bc1826b9f110bfa7cb0ec4ab9f5297eca628ce216c55af82c304e08e languageName: node linkType: hard @@ -10929,15 +10933,15 @@ __metadata: linkType: hard "react-is@npm:^19.1.0": - version: 19.2.3 - resolution: "react-is@npm:19.2.3" - checksum: 10c0/2b54c422c21b8dbd68a435a1cce21ecd5b6f06f48659531f7d53dd7368365da5a67e946f352fb2010d11ca40658aa67bec90995f0f1ec5556c0f71dbffe54994 + version: 19.2.8 + resolution: "react-is@npm:19.2.8" + checksum: 10c0/ed5322c84efe035c8fc814b1614ff5ca7fb8c2872a7c045197daf99f9b1bd68f32a2c79ffcbcfcff2fc5d93924ff9f9447400898f5b1534e6302bb0736a257f0 languageName: node linkType: hard "react-native-builder-bob@npm:^0.40.13": - version: 0.40.17 - resolution: "react-native-builder-bob@npm:0.40.17" + version: 0.40.18 + resolution: "react-native-builder-bob@npm:0.40.18" dependencies: "@babel/core": "npm:^7.25.2" "@babel/plugin-transform-flow-strip-types": "npm:^7.26.5" @@ -10958,25 +10962,25 @@ __metadata: json5: "npm:^2.2.1" kleur: "npm:^4.1.4" prompts: "npm:^2.4.2" - react-native-monorepo-config: "npm:^0.3.1" + react-native-monorepo-config: "npm:^0.3.3" which: "npm:^2.0.2" yargs: "npm:^17.5.1" bin: bob: bin/bob - checksum: 10c0/b3bb6f907a181ea0473cbf68cbc9f594eb5f2adc885011f4d29c961258d5f123494c28565881c0190f9f1a297b11aad7a1238742dd88345b0390e4343d9c2c11 + checksum: 10c0/5f1c969d339ece33208faebbf7022c1db3ec127ab91a0f21a29e0d7cbd18dfa702586fa1169f9dd642fbceeb2733af4651143d50010effb8bb150a9bfd4cc4ff languageName: node linkType: hard "react-native-gesture-handler@npm:^3.0.2": - version: 3.0.2 - resolution: "react-native-gesture-handler@npm:3.0.2" + version: 3.1.0 + resolution: "react-native-gesture-handler@npm:3.1.0" dependencies: "@types/react-test-renderer": "npm:^19.1.0" invariant: "npm:^2.2.4" peerDependencies: react: "*" react-native: "*" - checksum: 10c0/06d2b1a621f73c148c4000692e55d5f2a44d44229cabc79829de0b0b3dbf3bcf4bc00e8635f82bd1062d7eac87f39ca5213eb1041e23a5543947e64773eaddfb + checksum: 10c0/3d5e93b7f490abb6eecfa7b292c15f14fcb89103f6adfb9a9f7a2216924ee3e860d9e68ee9973df5b42a53229174043a50b679443ff13a7bbd2bf1077c44258d languageName: node linkType: hard @@ -11010,13 +11014,13 @@ __metadata: languageName: node linkType: hard -"react-native-monorepo-config@npm:^0.3.1": - version: 0.3.2 - resolution: "react-native-monorepo-config@npm:0.3.2" +"react-native-monorepo-config@npm:^0.3.3": + version: 0.3.5 + resolution: "react-native-monorepo-config@npm:0.3.5" dependencies: escape-string-regexp: "npm:^5.0.0" fast-glob: "npm:^3.3.3" - checksum: 10c0/c711f6a898ae263b68aef3bf8bd9c3806cd489676da8c5480dd155859b47bcd49a7700f3c056896c19b3ce2c2c3ea42a5482def93b0ff03febbe023ceb128c96 + checksum: 10c0/e0343807046c16d5fb5999b966e0a70fd8a757d658f58f10bee830485fa600ffb8de9100dea1cdead36a639c3e18a0f697b35e22a5564cb74e437d8aeca30d87 languageName: node linkType: hard @@ -11031,7 +11035,7 @@ __metadata: languageName: node linkType: hard -"react-native-nitro-modules@npm:0.36.1, react-native-nitro-modules@npm:^0.36.1": +"react-native-nitro-modules@npm:0.36.1": version: 0.36.1 resolution: "react-native-nitro-modules@npm:0.36.1" peerDependencies: @@ -11041,54 +11045,64 @@ __metadata: languageName: node linkType: hard +"react-native-nitro-modules@npm:^0.36.1": + version: 0.36.5 + resolution: "react-native-nitro-modules@npm:0.36.5" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/c7b2133f924967b68c0f0f678b5a5dce1a784a7e22f57ebf203ef5803a5c9c745b6d4dda8c9bcedd13f7c5ca53a8707784e98572a2ca8a864fe0b905163a34b5 + languageName: node + linkType: hard + "react-native-permissions@npm:^5.4.4": - version: 5.4.4 - resolution: "react-native-permissions@npm:5.4.4" + version: 5.6.1 + resolution: "react-native-permissions@npm:5.6.1" peerDependencies: - react: ">=18.1.0" - react-native: ">=0.70.0" - react-native-windows: ">=0.70.0" + react: "*" + react-native: "*" + react-native-windows: "*" peerDependenciesMeta: react-native-windows: optional: true - checksum: 10c0/9a4fa223053db242a66c18303a525e7552e849876e927427485b0e799f46131ccbbd1ad85e6dfac146d12af942a77a95caa8649b9f4ebcfbb7f2ff6dd8fece04 + checksum: 10c0/8c64576c12df4abcdbd156ad26b9e1c272873b16de350618854225df504052b556ef7a2610e15952894072e6ae447a513773dffcca87c42395e1ad0f26d0c6c6 languageName: node linkType: hard "react-native-reanimated@npm:^4.5.1": - version: 4.5.1 - resolution: "react-native-reanimated@npm:4.5.1" + version: 4.5.3 + resolution: "react-native-reanimated@npm:4.5.3" dependencies: react-native-is-edge-to-edge: "npm:^1.3.1" semver: "npm:^7.7.3" peerDependencies: react: "*" react-native: 0.83 - 0.86 - react-native-worklets: 0.10.x - checksum: 10c0/86c64529fc5d59fb1a9083e0934c957c95c7319149efe02f92252237aa1fb208958da1c42705447c12b1d6bff3c23a354e10a05787995082eda1c284703ec0bc + react-native-worklets: 0.10.x - 0.11.x + checksum: 10c0/8297fab7ac5b4f26448f6d83f5a795045ad1dde798e7b73256728e2363b957a27f2db3c2a3c0a4c3ff96cff28da9392263a1ced64e3187d7080c647641b935d1 languageName: node linkType: hard "react-native-safe-area-context@npm:^5.6.2": - version: 5.6.2 - resolution: "react-native-safe-area-context@npm:5.6.2" + version: 5.8.0 + resolution: "react-native-safe-area-context@npm:5.8.0" peerDependencies: react: "*" react-native: "*" - checksum: 10c0/3c8df21a1dbac83116b9c9bd5d20b7c1bb7649ecef44a111af6fb6b237241f5f4d692189eec30a69f5701b857249257da3621b9e17165460a2bb71faac7b92ae + checksum: 10c0/86d1375ce17d549b541d157148d9d093f9e918de47d3b0a5cbc4cb6afe619aa52e99f07dc62ee0878eaa70e8da3411193b24cfee1b7b271ffd295d0fce83d128 languageName: node linkType: hard "react-native-screens@npm:^4.26.0": - version: 4.26.0 - resolution: "react-native-screens@npm:4.26.0" + version: 4.26.2 + resolution: "react-native-screens@npm:4.26.2" dependencies: react-freeze: "npm:^1.0.0" warn-once: "npm:^0.1.0" peerDependencies: react: "*" - react-native: ">=0.84.0" - checksum: 10c0/6abb462d71c06e91ee0237ae5281bb2e1a36411c69b1931a0d3aaac8adbf6662089eb3b5013878d69961493aac4b41b388c738e69624a5d6fe0d75f492d43a31 + react-native: "*" + checksum: 10c0/3cdc702d8e21c63331e306a2b971307d5d9e13df52d94f435c5bb8d5dcf09fed00f4ef788e85ba87fae1ba5b155f4cdc2ee177a70f1f512adff73f94eef844d1 languageName: node linkType: hard @@ -11193,27 +11207,27 @@ __metadata: linkType: soft "react-native-vision-camera-worklets@npm:^5.1.0": - version: 5.1.0 - resolution: "react-native-vision-camera-worklets@npm:5.1.0" + version: 5.2.1 + resolution: "react-native-vision-camera-worklets@npm:5.2.1" peerDependencies: react: "*" react-native: "*" react-native-nitro-modules: "*" react-native-vision-camera: "*" react-native-worklets: "*" - checksum: 10c0/fe58b63dc2ed5442c0196286e3343bf17f0698d98dc746315156086e65bc203cb467d07e91eb9ac9ae18ab2b54cf65582a00ec598d43307473b76154556d76f6 + checksum: 10c0/c1c5d70220722d468b8b4bde4e11aa42414810618c8e969b2edf475fbc835bc51c83095f040c51d556916b3b586146272fb4a7d7592ee9e2084e0a5d2870a7aa languageName: node linkType: hard "react-native-vision-camera@npm:^5.1.0": - version: 5.1.0 - resolution: "react-native-vision-camera@npm:5.1.0" + version: 5.2.1 + resolution: "react-native-vision-camera@npm:5.2.1" peerDependencies: react: "*" react-native: "*" react-native-nitro-image: "*" react-native-nitro-modules: "*" - checksum: 10c0/6fbac55b041048fbc5ffd96f3df87bd9b07a4b8dc79a16d0bfb55aa08f790d8cd322b754b84f8714787cd910c9b9e4920d9cbe24ab7674fdb0ed9e1bc42e6285 + checksum: 10c0/58e6d11ef9456f54f4b3efb770aaecec966f15798c12b5ca01863b65190df71f734d89f81b78450dad14767432a5769555c6a8bb2f3a923e1dba296319bf4a9a languageName: node linkType: hard @@ -11325,7 +11339,7 @@ __metadata: languageName: node linkType: hard -"reflect.getprototypeof@npm:^1.0.6, reflect.getprototypeof@npm:^1.0.9": +"reflect.getprototypeof@npm:^1.0.10, reflect.getprototypeof@npm:^1.0.9": version: 1.0.10 resolution: "reflect.getprototypeof@npm:1.0.10" dependencies: @@ -11400,19 +11414,19 @@ __metadata: linkType: hard "regjsparser@npm:^0.13.0": - version: 0.13.0 - resolution: "regjsparser@npm:0.13.0" + version: 0.13.2 + resolution: "regjsparser@npm:0.13.2" dependencies: jsesc: "npm:~3.1.0" bin: regjsparser: bin/parser - checksum: 10c0/4702f85cda09f67747c1b2fb673a0f0e5d1ba39d55f177632265a0be471ba59e3f320623f411649141f752b126b8126eac3ff4c62d317921e430b0472bfc6071 + checksum: 10c0/261667a3da2552619adbf331eb32b139fef6af59a88343213df2fd5635ec02eb4e7d62a5fcb3b6aecec0df84d5746d36eabfff2502fd34b8ef01b1f983779274 languageName: node linkType: hard "release-it@npm:^19.0.4": - version: 19.2.2 - resolution: "release-it@npm:19.2.2" + version: 19.2.4 + resolution: "release-it@npm:19.2.4" dependencies: "@nodeutils/defaults-deep": "npm:1.1.0" "@octokit/rest": "npm:22.0.1" @@ -11433,13 +11447,13 @@ __metadata: proxy-agent: "npm:6.5.0" semver: "npm:7.7.3" tinyglobby: "npm:0.2.15" - undici: "npm:6.22.0" + undici: "npm:6.23.0" url-join: "npm:5.0.0" wildcard-match: "npm:5.1.4" yargs-parser: "npm:21.1.1" bin: release-it: bin/release-it.js - checksum: 10c0/922a84d79631178da8badb7ae35eb831af09220f368a3b2d8dbb97f884e8942603aa082e166da40c342710afdb479cbdee4ff5b813730cace019e871ee7d0df2 + checksum: 10c0/54888f271b665a4efc57b635664438ab256193d24a10928e19997c38b854056ad1bb2d653ede85c7552d45ecdcb1f1aa8861b8c0c27f9aaaf00ba916d3ae1781 languageName: node linkType: hard @@ -11494,55 +11508,63 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.20.0, resolve@npm:^1.22.10": - version: 1.22.11 - resolution: "resolve@npm:1.22.11" +"resolve@npm:^1.20.0, resolve@npm:^1.22.10, resolve@npm:^1.22.11": + version: 1.22.12 + resolution: "resolve@npm:1.22.12" dependencies: + es-errors: "npm:^1.3.0" is-core-module: "npm:^2.16.1" path-parse: "npm:^1.0.7" supports-preserve-symlinks-flag: "npm:^1.0.0" bin: resolve: bin/resolve - checksum: 10c0/f657191507530f2cbecb5815b1ee99b20741ea6ee02a59c57028e9ec4c2c8d7681afcc35febbd554ac0ded459db6f2d8153382c53a2f266cee2575e512674409 + checksum: 10c0/b16dc9b537c02e8c3388f7d3dcff9741d3071625f9a97ac1c885f2b0ca51e78df22328fb6d6ef214dd9101fb7cfc19aa2836fe3410402a94f3f7b8639c7149bf languageName: node linkType: hard "resolve@npm:^2.0.0-next.5": - version: 2.0.0-next.5 - resolution: "resolve@npm:2.0.0-next.5" + version: 2.0.0-next.7 + resolution: "resolve@npm:2.0.0-next.7" dependencies: - is-core-module: "npm:^2.13.0" + es-errors: "npm:^1.3.0" + is-core-module: "npm:^2.16.2" + node-exports-info: "npm:^1.6.0" + object-keys: "npm:^1.1.1" path-parse: "npm:^1.0.7" supports-preserve-symlinks-flag: "npm:^1.0.0" bin: resolve: bin/resolve - checksum: 10c0/a6c33555e3482ea2ec4c6e3d3bf0d78128abf69dca99ae468e64f1e30acaa318fd267fb66c8836b04d558d3e2d6ed875fe388067e7d8e0de647d3c21af21c43a + checksum: 10c0/8c6fa17ccdb826a3a52387ed8c42bf705dbc19d5494da52a86661b124b5b88fad5d8edbbb4434a1b563ecf7768368b7da9fb9489e20f36e02f7551a4ea87d707 languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.10#optional!builtin": - version: 1.22.11 - resolution: "resolve@patch:resolve@npm%3A1.22.11#optional!builtin::version=1.22.11&hash=c3c19d" +"resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.10#optional!builtin, resolve@patch:resolve@npm%3A^1.22.11#optional!builtin": + version: 1.22.12 + resolution: "resolve@patch:resolve@npm%3A1.22.12#optional!builtin::version=1.22.12&hash=c3c19d" dependencies: + es-errors: "npm:^1.3.0" is-core-module: "npm:^2.16.1" path-parse: "npm:^1.0.7" supports-preserve-symlinks-flag: "npm:^1.0.0" bin: resolve: bin/resolve - checksum: 10c0/ee5b182f2e37cb1165465e58c6abc797fec0a80b5ba3231607beb4677db0c9291ac010c47cf092b6daa2b7f518d69a0e21888e7e2b633f68d501a874212a8c63 + checksum: 10c0/fc6519984ae1f894d877c0060ba8b1f5ba3bc0e85a02f74e141929c118c23d74d9735619a9cc2965397387e514884245c65d72a40731dcb6cfc84c7bcdc8321e languageName: node linkType: hard "resolve@patch:resolve@npm%3A^2.0.0-next.5#optional!builtin": - version: 2.0.0-next.5 - resolution: "resolve@patch:resolve@npm%3A2.0.0-next.5#optional!builtin::version=2.0.0-next.5&hash=c3c19d" + version: 2.0.0-next.7 + resolution: "resolve@patch:resolve@npm%3A2.0.0-next.7#optional!builtin::version=2.0.0-next.7&hash=c3c19d" dependencies: - is-core-module: "npm:^2.13.0" + es-errors: "npm:^1.3.0" + is-core-module: "npm:^2.16.2" + node-exports-info: "npm:^1.6.0" + object-keys: "npm:^1.1.1" path-parse: "npm:^1.0.7" supports-preserve-symlinks-flag: "npm:^1.0.0" bin: resolve: bin/resolve - checksum: 10c0/78ad6edb8309a2bfb720c2c1898f7907a37f858866ce11a5974643af1203a6a6e05b2fa9c53d8064a673a447b83d42569260c306d43628bff5bb101969708355 + checksum: 10c0/6bb6f1d8a1789f7a5b4e35d950e76bc0ba632ff7d51fe86b6f34fb5f41e1ce0f7b884ec166828cfc0728ddffeaee49527711d752d12398297f90c51acd22195e languageName: node linkType: hard @@ -11573,13 +11595,6 @@ __metadata: languageName: node linkType: hard -"retry@npm:^0.12.0": - version: 0.12.0 - resolution: "retry@npm:0.12.0" - checksum: 10c0/59933e8501727ba13ad73ef4a04d5280b3717fd650408460c987392efe9d7be2040778ed8ebe933c5cbd63da3dcc37919c141ef8af0a54a6e4fca5a2af177bfe - languageName: node - linkType: hard - "reusify@npm:^1.0.4": version: 1.1.0 resolution: "reusify@npm:1.1.0" @@ -11631,15 +11646,15 @@ __metadata: linkType: hard "safe-array-concat@npm:^1.1.3": - version: 1.1.3 - resolution: "safe-array-concat@npm:1.1.3" + version: 1.1.4 + resolution: "safe-array-concat@npm:1.1.4" dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.2" - get-intrinsic: "npm:^1.2.6" + call-bind: "npm:^1.0.9" + call-bound: "npm:^1.0.4" + get-intrinsic: "npm:^1.3.0" has-symbols: "npm:^1.1.0" isarray: "npm:^2.0.5" - checksum: 10c0/43c86ffdddc461fb17ff8a17c5324f392f4868f3c7dd2c6a5d9f5971713bc5fd755667212c80eab9567595f9a7509cc2f83e590ddaebd1bd19b780f9c79f9a8d + checksum: 10c0/95fb4904ab1d9360a666fe5ba6d88f1c4a3a39682739e4512cff809fc6b5722a94bd95189211015bfb45859a7ffbc3340ea303ae22721c91c59e8946d310975a languageName: node linkType: hard @@ -11692,7 +11707,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.7.3, semver@npm:^7.1.3, semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.7.3": +"semver@npm:7.7.3": version: 7.7.3 resolution: "semver@npm:7.7.3" bin: @@ -11710,7 +11725,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.7.4": +"semver@npm:^7.1.3, semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.7.3, semver@npm:^7.7.4": version: 7.8.5 resolution: "semver@npm:7.8.5" bin: @@ -11833,20 +11848,20 @@ __metadata: languageName: node linkType: hard -"shell-quote@npm:^1.6.1, shell-quote@npm:^1.8.3": - version: 1.8.3 - resolution: "shell-quote@npm:1.8.3" - checksum: 10c0/bee87c34e1e986cfb4c30846b8e6327d18874f10b535699866f368ade11ea4ee45433d97bf5eada22c4320c27df79c3a6a7eb1bf3ecfc47f2c997d9e5e2672fd +"shell-quote@npm:^1.6.1, shell-quote@npm:^1.8.4": + version: 1.10.0 + resolution: "shell-quote@npm:1.10.0" + checksum: 10c0/46ee59bfd972ce6a45500c44ed130dff2d0a7d6fbac9841e59d548518cad8060a06393c9a5dcbc0cede294ad80b2a2cd8c904679e09265f53efc0a0879f30961 languageName: node linkType: hard -"side-channel-list@npm:^1.0.0": - version: 1.0.0 - resolution: "side-channel-list@npm:1.0.0" +"side-channel-list@npm:^1.0.1": + version: 1.0.1 + resolution: "side-channel-list@npm:1.0.1" dependencies: es-errors: "npm:^1.3.0" - object-inspect: "npm:^1.13.3" - checksum: 10c0/644f4ac893456c9490ff388bf78aea9d333d5e5bfc64cfb84be8f04bf31ddc111a8d4b83b85d7e7e8a7b845bc185a9ad02c052d20e086983cf59f0be517d9b3d + object-inspect: "npm:^1.13.4" + checksum: 10c0/d346c787fd2f9f1c2fdea14f00e8250118db0e7596d85a6cb9faa75f105d31a73a8f7a341c93d7df2a2429098c3d37a77bd3be9e88c37094b8c01807bc77c7a2 languageName: node linkType: hard @@ -11875,16 +11890,16 @@ __metadata: languageName: node linkType: hard -"side-channel@npm:^1.1.0": - version: 1.1.0 - resolution: "side-channel@npm:1.1.0" +"side-channel@npm:^1.1.0, side-channel@npm:^1.1.1": + version: 1.1.1 + resolution: "side-channel@npm:1.1.1" dependencies: es-errors: "npm:^1.3.0" - object-inspect: "npm:^1.13.3" - side-channel-list: "npm:^1.0.0" + object-inspect: "npm:^1.13.4" + side-channel-list: "npm:^1.0.1" side-channel-map: "npm:^1.0.1" side-channel-weakmap: "npm:^1.0.2" - checksum: 10c0/cb20dad41eb032e6c24c0982e1e5a24963a28aa6122b4f05b3f3d6bf8ae7fd5474ef382c8f54a6a3ab86e0cac4d41a23bd64ede3970e5bfb50326ba02a7996e6 + checksum: 10c0/dc0ab81d67f61bda9247d053ce93f41c3fd8ad2bdcb9cf9d8d2f8540d488f26d87a5e99ebfc07eea49ec025867b2452b705442d974b1478f0395e69f6bfb3270 languageName: node linkType: hard @@ -11968,7 +11983,7 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^8.0.3, socks-proxy-agent@npm:^8.0.5": +"socks-proxy-agent@npm:^8.0.5": version: 8.0.5 resolution: "socks-proxy-agent@npm:8.0.5" dependencies: @@ -11980,12 +11995,12 @@ __metadata: linkType: hard "socks@npm:^2.8.3": - version: 2.8.7 - resolution: "socks@npm:2.8.7" + version: 2.8.9 + resolution: "socks@npm:2.8.9" dependencies: - ip-address: "npm:^10.0.1" + ip-address: "npm:^10.1.1" smart-buffer: "npm:^4.2.0" - checksum: 10c0/2805a43a1c4bcf9ebf6e018268d87b32b32b06fbbc1f9282573583acc155860dc361500f89c73bfbb157caa1b4ac78059eac0ef15d1811eb0ca75e0bdadbc9d2 + checksum: 10c0/2d4350c31142b0931eb1758825b426bcbf4bfb5eed682ca48bc46dc9e7d1930ec366ea574ad49fc6c1fd9e9e17ce243be0ef13e31fc4b0319d9093f1fb19743c languageName: node linkType: hard @@ -12051,9 +12066,9 @@ __metadata: linkType: hard "spdx-license-ids@npm:^3.0.0": - version: 3.0.22 - resolution: "spdx-license-ids@npm:3.0.22" - checksum: 10c0/4a85e44c2ccfc06eebe63239193f526508ebec1abc7cf7bca8ee43923755636234395447c2c87f40fb672cf580a9c8e684513a676bfb2da3d38a4983684bbb38 + version: 3.0.23 + resolution: "spdx-license-ids@npm:3.0.23" + checksum: 10c0/8495620f6f2a237749cce922ea2d593a66f7885c301b1a0f5542183e7041182f27f616a8f13345cefdea0c9b3e0899328e0aa8cec100cf4f3fac4bb3bd975515 languageName: node linkType: hard @@ -12078,15 +12093,6 @@ __metadata: languageName: node linkType: hard -"ssri@npm:^13.0.0": - version: 13.0.0 - resolution: "ssri@npm:13.0.0" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10c0/405f3a531cd98b013cecb355d63555dca42fd12c7bc6671738aaa9a82882ff41cdf0ef9a2b734ca4f9a760338f114c29d01d9238a65db3ccac27929bd6e6d4b2 - languageName: node - linkType: hard - "stack-utils@npm:^2.0.3": version: 2.0.6 resolution: "stack-utils@npm:2.0.6" @@ -12207,13 +12213,13 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^8.1.0": - version: 8.1.0 - resolution: "string-width@npm:8.1.0" +"string-width@npm:^8.1.0, string-width@npm:^8.2.1": + version: 8.2.2 + resolution: "string-width@npm:8.2.2" dependencies: - get-east-asian-width: "npm:^1.3.0" - strip-ansi: "npm:^7.1.0" - checksum: 10c0/749b5d0dab2532b4b6b801064230f4da850f57b3891287023117ab63a464ad79dd208f42f793458f48f3ad121fe2e1f01dd525ff27ead957ed9f205e27406593 + get-east-asian-width: "npm:^1.5.0" + strip-ansi: "npm:^7.1.2" + checksum: 10c0/895624534e4e2f229e880bbc30aa77bdeb758e1a0edce203e0a17b8b4f08b8d3c1156516efc50a35e3efd0052d938a73797692111250ac9f52ee384710a01714 languageName: node linkType: hard @@ -12249,29 +12255,30 @@ __metadata: linkType: hard "string.prototype.trim@npm:^1.2.10": - version: 1.2.10 - resolution: "string.prototype.trim@npm:1.2.10" + version: 1.2.11 + resolution: "string.prototype.trim@npm:1.2.11" dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.2" + call-bind: "npm:^1.0.9" + call-bound: "npm:^1.0.4" define-data-property: "npm:^1.1.4" define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.5" - es-object-atoms: "npm:^1.0.0" + es-abstract: "npm:^1.24.2" + es-object-atoms: "npm:^1.1.2" has-property-descriptors: "npm:^1.0.2" - checksum: 10c0/8a8854241c4b54a948e992eb7dd6b8b3a97185112deb0037a134f5ba57541d8248dd610c966311887b6c2fd1181a3877bffb14d873ce937a344535dabcc648f8 + safe-regex-test: "npm:^1.1.0" + checksum: 10c0/b153cf8ed06db82ff40e27829e88e5c13f45eff9799f1d5707626e25989b488b059d6f5d57011e07f77745e28451e16735f295bc59c8ae146a4fd73a442366b0 languageName: node linkType: hard "string.prototype.trimend@npm:^1.0.9": - version: 1.0.9 - resolution: "string.prototype.trimend@npm:1.0.9" + version: 1.0.10 + resolution: "string.prototype.trimend@npm:1.0.10" dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.2" + call-bind: "npm:^1.0.9" + call-bound: "npm:^1.0.4" define-properties: "npm:^1.2.1" - es-object-atoms: "npm:^1.0.0" - checksum: 10c0/59e1a70bf9414cb4c536a6e31bef5553c8ceb0cf44d8b4d0ed65c9653358d1c64dd0ec203b100df83d0413bbcde38b8c5d49e14bc4b86737d74adc593a0d35b6 + es-object-atoms: "npm:^1.1.2" + checksum: 10c0/cc09233181769047a5330becfd5740fec5f0c8137886e7b553626788b00f75df9f34db1159bc52dbb7fc389b8ebb6e1dab44c8c9e31eb600039729a542013286 languageName: node linkType: hard @@ -12314,11 +12321,11 @@ __metadata: linkType: hard "strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0, strip-ansi@npm:^7.1.2": - version: 7.1.2 - resolution: "strip-ansi@npm:7.1.2" + version: 7.2.0 + resolution: "strip-ansi@npm:7.2.0" dependencies: - ansi-regex: "npm:^6.0.1" - checksum: 10c0/0d6d7a023de33368fd042aab0bf48f4f4077abdfd60e5393e73c7c411e85e1b3a83507c11af2e656188511475776215df9ca589b4da2295c9455cc399ce1858b + ansi-regex: "npm:^6.2.2" + checksum: 10c0/544d13b7582f8254811ea97db202f519e189e59d35740c46095897e254e4f1aa9fe1524a83ad6bc5ad67d4dd6c0281d2e0219ed62b880a6238a16a17d375f221 languageName: node linkType: hard @@ -12350,7 +12357,7 @@ __metadata: languageName: node linkType: hard -"strnum@npm:^1.1.1": +"strnum@npm:^1.0.5": version: 1.1.2 resolution: "strnum@npm:1.1.2" checksum: 10c0/a0fce2498fa3c64ce64a40dada41beb91cabe3caefa910e467dc0518ef2ebd7e4d10f8c2202a6104f1410254cae245066c0e94e2521fb4061a5cb41831952392 @@ -12382,31 +12389,31 @@ __metadata: languageName: node linkType: hard -"synckit@npm:^0.11.7": - version: 0.11.11 - resolution: "synckit@npm:0.11.11" +"synckit@npm:^0.11.13": + version: 0.11.13 + resolution: "synckit@npm:0.11.13" dependencies: - "@pkgr/core": "npm:^0.2.9" - checksum: 10c0/f0761495953d12d94a86edf6326b3a565496c72f9b94c02549b6961fb4d999f4ca316ce6b3eb8ed2e4bfc5056a8de65cda0bd03a233333a35221cd2fdc0e196b + "@pkgr/core": "npm:^0.3.6" + checksum: 10c0/5a6c19f4f79045aaa7994106401bff6dbe7cca23a6d0a0723ff14eb8b1bebeb4a71729118f6914905598e304ea2fa13509885e11ba07d92e7cb68a06740cb328 languageName: node linkType: hard -"tar@npm:^7.5.2": - version: 7.5.2 - resolution: "tar@npm:7.5.2" +"tar@npm:^7.5.4": + version: 7.5.22 + resolution: "tar@npm:7.5.22" dependencies: "@isaacs/fs-minipass": "npm:^4.0.0" chownr: "npm:^3.0.0" minipass: "npm:^7.1.2" minizlib: "npm:^3.1.0" yallist: "npm:^5.0.0" - checksum: 10c0/a7d8b801139b52f93a7e34830db0de54c5aa45487c7cb551f6f3d44a112c67f1cb8ffdae856b05fd4f17b1749911f1c26f1e3a23bbe0279e17fd96077f13f467 + checksum: 10c0/1311f6be85a8157ac4c9147bae43e13923d2a1aae15e4aa1bd5239e4e03d2cf53cfe103dde7f35832fbb4c938b042856bc8e9a0afd29abd05e2d1608788c4fea languageName: node linkType: hard "terser@npm:^5.15.0": - version: 5.44.1 - resolution: "terser@npm:5.44.1" + version: 5.49.0 + resolution: "terser@npm:5.49.0" dependencies: "@jridgewell/source-map": "npm:^0.3.3" acorn: "npm:^8.15.0" @@ -12414,7 +12421,7 @@ __metadata: source-map-support: "npm:~0.5.20" bin: terser: bin/terser - checksum: 10c0/ee7a76692cb39b1ed22c30ff366c33ff3c977d9bb769575338ff5664676168fcba59192fb5168ef80c7cd901ef5411a1b0351261f5eaa50decf0fc71f63bde75 + checksum: 10c0/c1f628a7a6226f7283db92ce43247306d612b80a60a075d97e1f5539a032beee05c87f889eacff2ffabd2fa9ac6bd1c147e5eb49c6546c9b174ed241d5f056a7 languageName: node linkType: hard @@ -12450,14 +12457,14 @@ __metadata: languageName: node linkType: hard -"tinyexec@npm:^1.0.0, tinyexec@npm:^1.0.1": - version: 1.0.2 - resolution: "tinyexec@npm:1.0.2" - checksum: 10c0/1261a8e34c9b539a9aae3b7f0bb5372045ff28ee1eba035a2a059e532198fe1a182ec61ac60fa0b4a4129f0c4c4b1d2d57355b5cb9aa2d17ac9454ecace502ee +"tinyexec@npm:^1.0.0, tinyexec@npm:^1.2.4": + version: 1.3.0 + resolution: "tinyexec@npm:1.3.0" + checksum: 10c0/e9b89f97489d2aab2cef408da279e6b32547e738d1275032ccb8fd0028a006d93eb70fc51c6cffd9fc2f5aca6c2a273d8b6f73b52d46ee5116da6b94969ef958 languageName: node linkType: hard -"tinyglobby@npm:0.2.15, tinyglobby@npm:^0.2.12": +"tinyglobby@npm:0.2.15": version: 0.2.15 resolution: "tinyglobby@npm:0.2.15" dependencies: @@ -12467,7 +12474,7 @@ __metadata: languageName: node linkType: hard -"tinyglobby@npm:^0.2.14, tinyglobby@npm:^0.2.15": +"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.14, tinyglobby@npm:^0.2.15": version: 0.2.17 resolution: "tinyglobby@npm:0.2.17" dependencies: @@ -12526,74 +12533,32 @@ __metadata: languageName: node linkType: hard -"turbo-darwin-64@npm:2.7.2": - version: 2.7.2 - resolution: "turbo-darwin-64@npm:2.7.2" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"turbo-darwin-arm64@npm:2.7.2": - version: 2.7.2 - resolution: "turbo-darwin-arm64@npm:2.7.2" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"turbo-linux-64@npm:2.7.2": - version: 2.7.2 - resolution: "turbo-linux-64@npm:2.7.2" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - -"turbo-linux-arm64@npm:2.7.2": - version: 2.7.2 - resolution: "turbo-linux-arm64@npm:2.7.2" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - -"turbo-windows-64@npm:2.7.2": - version: 2.7.2 - resolution: "turbo-windows-64@npm:2.7.2" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"turbo-windows-arm64@npm:2.7.2": - version: 2.7.2 - resolution: "turbo-windows-arm64@npm:2.7.2" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - "turbo@npm:^2.5.6": - version: 2.7.2 - resolution: "turbo@npm:2.7.2" - dependencies: - turbo-darwin-64: "npm:2.7.2" - turbo-darwin-arm64: "npm:2.7.2" - turbo-linux-64: "npm:2.7.2" - turbo-linux-arm64: "npm:2.7.2" - turbo-windows-64: "npm:2.7.2" - turbo-windows-arm64: "npm:2.7.2" + version: 2.10.8 + resolution: "turbo@npm:2.10.8" + dependencies: + "@turbo/darwin-64": "npm:2.10.8" + "@turbo/darwin-arm64": "npm:2.10.8" + "@turbo/linux-64": "npm:2.10.8" + "@turbo/linux-arm64": "npm:2.10.8" + "@turbo/windows-64": "npm:2.10.8" + "@turbo/windows-arm64": "npm:2.10.8" dependenciesMeta: - turbo-darwin-64: + "@turbo/darwin-64": optional: true - turbo-darwin-arm64: + "@turbo/darwin-arm64": optional: true - turbo-linux-64: + "@turbo/linux-64": optional: true - turbo-linux-arm64: + "@turbo/linux-arm64": optional: true - turbo-windows-64: + "@turbo/windows-64": optional: true - turbo-windows-arm64: + "@turbo/windows-arm64": optional: true bin: turbo: bin/turbo - checksum: 10c0/390937a32070ac52120af56705006e831de2edf8e22a2b0146006d07740cd413b3a36335339e7617b57cf74b008f4b15931cf56e1c7c0c03808f028ed937c4de + checksum: 10c0/892459ca52bdcc0ecd50d0bd5e092bc97052ff726fd727af7df997e6b8ff20b1aea6040caac14c2ceb661f18b08e7abd2027d188f8a67bc74dcab0c043060026 languageName: node linkType: hard @@ -12684,16 +12649,16 @@ __metadata: linkType: hard "typed-array-length@npm:^1.0.7": - version: 1.0.7 - resolution: "typed-array-length@npm:1.0.7" + version: 1.0.8 + resolution: "typed-array-length@npm:1.0.8" dependencies: - call-bind: "npm:^1.0.7" - for-each: "npm:^0.3.3" - gopd: "npm:^1.0.1" - is-typed-array: "npm:^1.1.13" - possible-typed-array-names: "npm:^1.0.0" - reflect.getprototypeof: "npm:^1.0.6" - checksum: 10c0/e38f2ae3779584c138a2d8adfa8ecf749f494af3cd3cdafe4e688ce51418c7d2c5c88df1bd6be2bbea099c3f7cea58c02ca02ed438119e91f162a9de23f61295 + call-bind: "npm:^1.0.9" + for-each: "npm:^0.3.5" + gopd: "npm:^1.2.0" + is-typed-array: "npm:^1.1.15" + possible-typed-array-names: "npm:^1.1.0" + reflect.getprototypeof: "npm:^1.0.10" + checksum: 10c0/5319f740fc426a3217182c2f7c87656acb0903e046de5a938e30167337d26abf1bb3ad4b32833a72521a4cc58223aec80627b38b357d0a3d5fd64881427e77ab languageName: node linkType: hard @@ -12766,10 +12731,10 @@ __metadata: languageName: node linkType: hard -"undici@npm:6.22.0": - version: 6.22.0 - resolution: "undici@npm:6.22.0" - checksum: 10c0/47903c489d73e26bd47960cf2f04d63282ed050818b672cb05f8dfb6403381b850cf1b1751832654fd3af22aacd9d780e5e61aff563cd97943f5c4f10d5b3e23 +"undici@npm:6.28.0": + version: 6.28.0 + resolution: "undici@npm:6.28.0" + checksum: 10c0/3029a70df06b38b5b2f30732932a1e92544c753cd82c8abdf0d35afad48e0ba91612e79fe3a442dbbb9434d6a9eba2b714d5ea28984c903dda2b5d5444f38354 languageName: node linkType: hard @@ -12818,24 +12783,6 @@ __metadata: languageName: node linkType: hard -"unique-filename@npm:^5.0.0": - version: 5.0.0 - resolution: "unique-filename@npm:5.0.0" - dependencies: - unique-slug: "npm:^6.0.0" - checksum: 10c0/afb897e9cf4c2fb622ea716f7c2bb462001928fc5f437972213afdf1cc32101a230c0f1e9d96fc91ee5185eca0f2feb34127145874975f347be52eb91d6ccc2c - languageName: node - linkType: hard - -"unique-slug@npm:^6.0.0": - version: 6.0.0 - resolution: "unique-slug@npm:6.0.0" - dependencies: - imurmurhash: "npm:^0.1.4" - checksum: 10c0/da7ade4cb04eb33ad0499861f82fe95ce9c7c878b7139dc54d140ecfb6a6541c18a5c8dac16188b8b379fe62c0c1f1b710814baac910cde5f4fec06212126c6a - languageName: node - linkType: hard - "universal-user-agent@npm:^7.0.0, universal-user-agent@npm:^7.0.2": version: 7.0.3 resolution: "universal-user-agent@npm:7.0.3" @@ -12864,7 +12811,7 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.2.0": +"update-browserslist-db@npm:^1.2.3": version: 1.2.3 resolution: "update-browserslist-db@npm:1.2.3" dependencies: @@ -13063,17 +13010,17 @@ __metadata: linkType: hard "which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19": - version: 1.1.19 - resolution: "which-typed-array@npm:1.1.19" + version: 1.1.22 + resolution: "which-typed-array@npm:1.1.22" dependencies: available-typed-arrays: "npm:^1.0.7" - call-bind: "npm:^1.0.8" + call-bind: "npm:^1.0.9" call-bound: "npm:^1.0.4" for-each: "npm:^0.3.5" get-proto: "npm:^1.0.1" gopd: "npm:^1.2.0" has-tostringtag: "npm:^1.0.2" - checksum: 10c0/702b5dc878addafe6c6300c3d0af5983b175c75fcb4f2a72dfc3dd38d93cf9e89581e4b29c854b16ea37e50a7d7fca5ae42ece5c273d8060dcd603b2404bbb3f + checksum: 10c0/e59db184a4e78b461fac3b05fafc1e7badbbedafbf04a967ee1de73717f1f9723a79699e7b5de71d449541cb5da8353efc01a4f8a72a152479850e54fa196c40 languageName: node linkType: hard @@ -13088,14 +13035,14 @@ __metadata: languageName: node linkType: hard -"which@npm:^6.0.0": - version: 6.0.0 - resolution: "which@npm:6.0.0" +"which@npm:^7.0.0": + version: 7.0.0 + resolution: "which@npm:7.0.0" dependencies: - isexe: "npm:^3.1.1" + isexe: "npm:^4.0.0" bin: node-which: bin/which.js - checksum: 10c0/fe9d6463fe44a76232bb6e3b3181922c87510a5b250a98f1e43a69c99c079b3f42ddeca7e03d3e5f2241bf2d334f5a7657cfa868b97c109f3870625842f4cc15 + checksum: 10c0/ca0b54f198f78bbc4b7c02e34bda8d335cb352e0adb4cbca1c37b1a957af3a879a82c4c27ca6525bc942f548d8b64f816ef6528360af9f3de55ffb9b979b620d languageName: node linkType: hard @@ -13191,17 +13138,17 @@ __metadata: linkType: hard "ws@npm:^6.2.3": - version: 6.2.3 - resolution: "ws@npm:6.2.3" + version: 6.2.6 + resolution: "ws@npm:6.2.6" dependencies: async-limiter: "npm:~1.0.0" - checksum: 10c0/56a35b9799993cea7ce2260197e7879f21bbbb194a967f31acbbda6f7f46ecda4365951966fb062044c95197e19fb2f053be6f65c172435455186835f494de41 + checksum: 10c0/24245efc3c09b6175df32ddb4a06f85432c4c88abb0e75f43f2551c1d5de456218b1b6c7685c414b8e03a68a167488e4aadf04a895681c8e01502025ce505742 languageName: node linkType: hard "ws@npm:^7, ws@npm:^7.5.10": - version: 7.5.10 - resolution: "ws@npm:7.5.10" + version: 7.5.13 + resolution: "ws@npm:7.5.13" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ^5.0.2 @@ -13210,7 +13157,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 10c0/bd7d5f4aaf04fae7960c23dcb6c6375d525e00f795dd20b9385902bd008c40a94d3db3ce97d878acc7573df852056ca546328b27b39f47609f80fb22a0a9b61d + checksum: 10c0/e223726bbda5768ed58ec9252d04eda7018ef380ea122bb94e595a4d7a02b5baeb423933936576fddb8fa51b642375f7ae814bf0f6f5d9a3ce290566578a8c5a languageName: node linkType: hard @@ -13278,13 +13225,6 @@ __metadata: languageName: node linkType: hard -"yallist@npm:^4.0.0": - version: 4.0.0 - resolution: "yallist@npm:4.0.0" - checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a - languageName: node - linkType: hard - "yallist@npm:^5.0.0": version: 5.0.0 resolution: "yallist@npm:5.0.0" @@ -13293,11 +13233,11 @@ __metadata: linkType: hard "yaml@npm:^2.2.1, yaml@npm:^2.6.1": - version: 2.8.2 - resolution: "yaml@npm:2.8.2" + version: 2.9.0 + resolution: "yaml@npm:2.9.0" bin: yaml: bin.mjs - checksum: 10c0/703e4dc1e34b324aa66876d63618dcacb9ed49f7e7fe9b70f1e703645be8d640f68ab84f12b86df8ac960bac37acf5513e115de7c970940617ce0343c8c9cd96 + checksum: 10c0/f340718df45e97a9551b9bf9dac61c80050bc464513b710debfb5067c380c8472e3b67809cffacb4ab5ffb5e66ef9310816c88b05f371cec60abfedd8c88e0a2 languageName: node linkType: hard @@ -13345,8 +13285,8 @@ __metadata: linkType: hard "yargs@npm:^17.0.0, yargs@npm:^17.3.1, yargs@npm:^17.5.1, yargs@npm:^17.6.2": - version: 17.7.2 - resolution: "yargs@npm:17.7.2" + version: 17.7.3 + resolution: "yargs@npm:17.7.3" dependencies: cliui: "npm:^8.0.1" escalade: "npm:^3.1.1" @@ -13355,21 +13295,21 @@ __metadata: string-width: "npm:^4.2.3" y18n: "npm:^5.0.5" yargs-parser: "npm:^21.1.1" - checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 + checksum: 10c0/7a28572f7e785a57886e34fdbddb9b28756dec552e1453d5f6e7cdd00ad8721a4e8c4321d33683f5e61cacb36ad43258adbb48396b71ec4ed14abee0fc0d0c1f languageName: node linkType: hard "yargs@npm:^18.0.0": - version: 18.0.0 - resolution: "yargs@npm:18.0.0" + version: 18.1.0 + resolution: "yargs@npm:18.1.0" dependencies: cliui: "npm:^9.0.1" escalade: "npm:^3.1.1" get-caller-file: "npm:^2.0.5" - string-width: "npm:^7.2.0" + string-width: "npm:^8.2.1" y18n: "npm:^5.0.5" yargs-parser: "npm:^22.0.0" - checksum: 10c0/bf290e4723876ea9c638c786a5c42ac28e03c9ca2325e1424bf43b94e5876456292d3ed905b853ebbba6daf43ed29e772ac2a6b3c5fb1b16533245d6211778f3 + checksum: 10c0/86052bbed90a9aaaaf1eabe004f47c7420e1b7f8d314170cfb0ad4721f518ec2fb7ba5a6daa20708b1595aba257f1554b2d5b3f5606356f86aa9dae18de0bec5 languageName: node linkType: hard @@ -13395,9 +13335,9 @@ __metadata: linkType: hard "yoctocolors@npm:^2.1.1": - version: 2.1.2 - resolution: "yoctocolors@npm:2.1.2" - checksum: 10c0/b220f30f53ebc2167330c3adc86a3c7f158bcba0236f6c67e25644c3188e2571a6014ffc1321943bb619460259d3d27eb4c9cc58c2d884c1b195805883ec7066 + version: 2.2.0 + resolution: "yoctocolors@npm:2.2.0" + checksum: 10c0/ca179aa83401aa0711f268fda38145a44c828e612a5f5265439efb5796cd63c46ca6b786bae3b74c75440a48518c36b5b23cba7fbb76ea8af5a9f85f830a5754 languageName: node linkType: hard @@ -13418,8 +13358,8 @@ __metadata: linkType: hard "zustand@npm:^5.0.9": - version: 5.0.9 - resolution: "zustand@npm:5.0.9" + version: 5.0.14 + resolution: "zustand@npm:5.0.14" peerDependencies: "@types/react": ">=18.0.0" immer: ">=9.0.6" @@ -13434,6 +13374,6 @@ __metadata: optional: true use-sync-external-store: optional: true - checksum: 10c0/552849e4546c7760704d6509a5c412d57c62a1fa9e53169c939ba5e3d75f8cb3df50a64c3a22e6c3f1c8cc00de7543e4edd61ab5ae0c9169ba9a98e28303aba6 + checksum: 10c0/89de0c8119f1e0861b2f896a0b4df0d67994a373ecee02628d4e7b97cee1885207f646917d3ede395c3a5083316d48846855cfbc84aced7cd23a6db3238930a8 languageName: node linkType: hard From 450495308b394d7e13dced5208e4ad655f4e98a6 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 2 Aug 2026 17:55:41 -0300 Subject: [PATCH 42/45] fix(android): restore compile-time selective install for text recognition and barcode scanning The v2 hardening PR fixed a real compile failure by making com.google.mlkit:text-recognition and com.google.mlkit:barcode-scanning unconditional dependencies, defeating the library's selective-install purpose: a consumer who only wants barcode scanning still got the full text-recognition SDK bundled, and vice versa. Root cause: Kotlin has no #if-equivalent preprocessor. Six Kotlin files (TextRecognitionServiceFactory/MLKitTextRecognitionService/MLKitTextAdapter and their barcode equivalents) import concrete MLKit types at file scope, so they fail to compile whenever the corresponding artifact is missing from the classpath - regardless of whether the runtime feature flag ever lets that code execute. iOS never had this problem since its Swift equivalents use #if MLKIT_TEXT_RECOGNITION_ANY / #if MLKIT_BARCODE_SCANNING, which genuinely elides the guarded code (and its imports) at compile time. Fixed by giving Kotlin the same guarantee via Gradle conditional source sets instead of reflection (MLKit's real client APIs return async Task objects with listener callbacks, which would need a java.lang.reflect.Proxy to invoke reflectively - fragile under R8 and adds reflection overhead to code that runs every camera frame): - Moved the 6 MLKit-coupled files into textRecognitionEnabled/ barcodeScanningEnabled source sets (content unchanged, path only). - Added tiny stub factories in textRecognitionDisabled/ barcodeScanningDisabled that throw if ever reached (they aren't - HybridVisionCameraMLKit already gates construction before this point), existing purely so the always-compiled Hybrid* classes have something to compile against. - android/build.gradle picks the enabled/disabled source dir per flag, and re-gates the two base MLKit SDKs behind their flags again. - Added com.google.mlkit:vision-common as an explicit, always-on dependency: three files (ProcessedImage, ImagePreprocessor, HybridFrameSpec+toProcessedImage) use its InputImage type unconditionally for the shared image-preprocessing pipeline. This isn't a version bump - it's already resolved transitively today at the same 17.3.0. - Updated the README caveat that documented this exact limitation, since it's no longer true. Verified locally (not just "it compiles"): with barcodeScanning:false, com.google.mlkit:barcode-scanning is fully absent from the resolved debugRuntimeClasspath; with textRecognition and all 4 language variants false, com.google.mlkit:text-recognition is fully absent. Full yarn test suite passes (93/93). --- README.md | 10 ++++---- android/build.gradle | 23 +++++++++++++------ .../BarcodeScanningServiceFactory.kt | 14 +++++++++++ .../mlkit/MLKitBarcodeScanningService.kt | 0 .../mlkit/adapters/MLKitBarcodeAdapter.kt | 0 .../BarcodeScanningServiceFactory.kt | 0 .../TextRecognitionServiceFactory.kt | 14 +++++++++++ .../mlkit/MLKitTextRecognitionService.kt | 0 .../mlkit/adapters/MLKitTextAdapter.kt | 0 .../TextRecognitionServiceFactory.kt | 0 10 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 android/src/barcodeScanningDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt rename android/src/{main => barcodeScanningEnabled}/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt (100%) rename android/src/{main => barcodeScanningEnabled}/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitBarcodeAdapter.kt (100%) rename android/src/{main => barcodeScanningEnabled}/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt (100%) create mode 100644 android/src/textRecognitionDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt rename android/src/{main => textRecognitionEnabled}/java/com/visioncameramlkit/infrastructure/mlkit/MLKitTextRecognitionService.kt (100%) rename android/src/{main => textRecognitionEnabled}/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitTextAdapter.kt (100%) rename android/src/{main => textRecognitionEnabled}/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt (100%) diff --git a/README.md b/README.md index 3dff1a5..4c0aa65 100644 --- a/README.md +++ b/README.md @@ -82,11 +82,11 @@ OCR languages you do not need to reduce binary size. Configuration keys for post-v2 roadmap features remain reserved and disabled; enabling one does not provide a public JS/Nitro API in v2. -On Android, the Latin OCR and barcode base SDKs remain compile-time -dependencies even when their feature flags are disabled because v2's native -implementation references their API types. Those flags control runtime -availability; the four optional OCR language modules are still omitted when -disabled. +Both `textRecognition` and `barcodeScanning` are fully optional at compile +time on both platforms: disabling one removes its native SDK from the build +entirely instead of only gating it at runtime, so consumers who only need +barcode scanning (or only text recognition) do not pay for the other's +dependencies or binary size. ### Android (Gradle) diff --git a/android/build.gradle b/android/build.gradle index 070c506..c156d5b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -166,7 +166,9 @@ android { java.srcDirs += [ "generated/java", "generated/jni", - "../nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit" + "../nitrogen/generated/android/kotlin/com/margelo/nitro/visioncameramlkit", + anyTextRecognition ? "src/textRecognitionEnabled/java" : "src/textRecognitionDisabled/java", + mlkitConfig.barcodeScanning ? "src/barcodeScanningEnabled/java" : "src/barcodeScanningDisabled/java", ] } } @@ -188,14 +190,19 @@ dependencies { implementation "androidx.exifinterface:exifinterface:1.3.7" // ===== GoogleMLKit Vision ===== - // Base SDKs for the two v2 features are compile-time dependencies because - // their public types are referenced by unconditional Kotlin sources. Feature - // flags still control runtime availability; optional OCR language models stay - // selective so consumers can trim those larger model artifacts. + // vision-common is always present: it's a small shared-types artifact (InputImage, + // etc.) used by the image-preprocessing pipeline regardless of which MLKit feature + // is enabled, and carries no OCR/barcode model weights. Every feature-specific SDK + // below stays properly gated behind its own flag and its own conditional Kotlin + // source set (see sourceSets above), so enabling only one feature (e.g. barcode + // scanning) does not pull in or compile against the others. + implementation "com.google.mlkit:vision-common:17.3.0" // Text recognition v2 // https://developers.google.com/ml-kit/vision/text-recognition/v2/android - implementation "com.google.mlkit:text-recognition:16.0.1" + if (anyTextRecognition) { + implementation "com.google.mlkit:text-recognition:16.0.1" + } if (mlkitConfig.textRecognitionChinese) { implementation "com.google.mlkit:text-recognition-chinese:16.0.1" } @@ -250,7 +257,9 @@ dependencies { // Barcode scanning // https://developers.google.com/ml-kit/vision/barcode-scanning/android - implementation "com.google.mlkit:barcode-scanning:17.3.0" + if (mlkitConfig.barcodeScanning) { + implementation "com.google.mlkit:barcode-scanning:17.3.0" + } // Image labeling (Base Model) -> Custom models will be added soon // https://developers.google.com/ml-kit/vision/image-labeling/android diff --git a/android/src/barcodeScanningDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt b/android/src/barcodeScanningDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt new file mode 100644 index 0000000..b53b417 --- /dev/null +++ b/android/src/barcodeScanningDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt @@ -0,0 +1,14 @@ +package com.visioncameramlkit.infrastructure.mlkit.factories + +import com.visioncameramlkit.domain.models.BarcodeScanningOptions +import com.visioncameramlkit.domain.models.BarcodeScanningResult +import com.visioncameramlkit.domain.services.IRecognitionService + +object BarcodeScanningServiceFactory { + fun create(options: BarcodeScanningOptions = BarcodeScanningOptions()): IRecognitionService { + throw IllegalStateException( + "Barcode scanning is not enabled in this build. Enable barcodeScanning in your MLKit " + + "configuration to use this feature.", + ) + } +} diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt b/android/src/barcodeScanningEnabled/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt similarity index 100% rename from android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt rename to android/src/barcodeScanningEnabled/java/com/visioncameramlkit/infrastructure/mlkit/MLKitBarcodeScanningService.kt diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitBarcodeAdapter.kt b/android/src/barcodeScanningEnabled/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitBarcodeAdapter.kt similarity index 100% rename from android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitBarcodeAdapter.kt rename to android/src/barcodeScanningEnabled/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitBarcodeAdapter.kt diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt b/android/src/barcodeScanningEnabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt similarity index 100% rename from android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt rename to android/src/barcodeScanningEnabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt diff --git a/android/src/textRecognitionDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt b/android/src/textRecognitionDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt new file mode 100644 index 0000000..aa102c0 --- /dev/null +++ b/android/src/textRecognitionDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt @@ -0,0 +1,14 @@ +package com.visioncameramlkit.infrastructure.mlkit.factories + +import com.visioncameramlkit.domain.models.TextRecognitionLanguage +import com.visioncameramlkit.domain.models.TextRecognitionResult +import com.visioncameramlkit.domain.services.IRecognitionService + +object TextRecognitionServiceFactory { + fun create(language: TextRecognitionLanguage = TextRecognitionLanguage.LATIN): IRecognitionService { + throw IllegalStateException( + "Text recognition is not enabled in this build. Enable textRecognition (or a language " + + "variant) in your MLKit configuration to use this feature.", + ) + } +} diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitTextRecognitionService.kt b/android/src/textRecognitionEnabled/java/com/visioncameramlkit/infrastructure/mlkit/MLKitTextRecognitionService.kt similarity index 100% rename from android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/MLKitTextRecognitionService.kt rename to android/src/textRecognitionEnabled/java/com/visioncameramlkit/infrastructure/mlkit/MLKitTextRecognitionService.kt diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitTextAdapter.kt b/android/src/textRecognitionEnabled/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitTextAdapter.kt similarity index 100% rename from android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitTextAdapter.kt rename to android/src/textRecognitionEnabled/java/com/visioncameramlkit/infrastructure/mlkit/adapters/MLKitTextAdapter.kt diff --git a/android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt b/android/src/textRecognitionEnabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt similarity index 100% rename from android/src/main/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt rename to android/src/textRecognitionEnabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt From e995456afe02624c0f3035007c63005e28e80abe Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 2 Aug 2026 18:06:22 -0300 Subject: [PATCH 43/45] docs: add selective ML Kit install and Expo plugin guides Audited README.md and docs/*.md against what v2 actually ships. Content was accurate, but two Expo plugin behaviors were undocumented anywhere (Android-only keys silently dropped from the iOS Podfile hash, and no Kotlin DSL/.kts support), and neither selective install nor the Expo plugin had a dedicated doc - both only existed condensed in the README. Add docs/selective-mlkit-install.md: what "selective" guarantees now (compile-time removal on both platforms per the recent Android fix, not just runtime gating), defaults table, Android/iOS/Expo setup, and how to verify a disabled feature's SDK is actually absent from the resolved dependency graph. Add docs/expo-plugin.md: usage examples, what the plugin actually writes and where, validation behavior, and the previously-undocumented platform caveats (Android-only key dropping, Groovy-only support, idempotency via marker comments, createRunOncePlugin behavior). Link both from README's API Docs list and from the inline Expo section. --- README.md | 6 +- docs/expo-plugin.md | 129 ++++++++++++++++++++++++++++ docs/selective-mlkit-install.md | 147 ++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 docs/expo-plugin.md create mode 100644 docs/selective-mlkit-install.md diff --git a/README.md b/README.md index 4c0aa65..69e5d84 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,9 @@ you don't have to hand-edit native config. Add it to your `app.json`/ Any key you omit keeps the library's own default (see the Android/iOS sections above). Passing an unrecognized key throws at prebuild time instead of silently being ignored. Requires `@expo/config-plugins` (already a -dependency of `expo` itself, so Expo projects have it for free). +dependency of `expo` itself, so Expo projects have it for free). See +[docs/expo-plugin.md](docs/expo-plugin.md) for platform-specific caveats +(Android-only keys, Kotlin DSL support, idempotency). Static-image processing accepts local files up to 25 MB encoded size. iOS accepts sources up to 25 megapixels and 10,000 pixels on either dimension, then @@ -162,6 +164,8 @@ to protect application disk, memory, and CPU resources. - [Text Recognition API](docs/text-recognition.md) - [Barcode Scanning API](docs/barcode-scanning.md) +- [Selective ML Kit Installation](docs/selective-mlkit-install.md) +- [Expo Config Plugin](docs/expo-plugin.md) - [Migrating from v1 to v2](docs/migration-v1-to-v2.md) ### Text Recognition (Frame Output) diff --git a/docs/expo-plugin.md b/docs/expo-plugin.md new file mode 100644 index 0000000..63b1c29 --- /dev/null +++ b/docs/expo-plugin.md @@ -0,0 +1,129 @@ +# Expo Config Plugin + +This page documents this library's Expo config plugin in more detail than the README quick-start. + +If you're using an Expo prebuild workflow (`expo prebuild`, EAS Build, or a +custom dev client), this library ships a config plugin that mirrors the same +[selective ML Kit configuration](selective-mlkit-install.md) into the +generated `android/build.gradle` and `ios/Podfile` automatically, so you +don't have to hand-edit native config after every prebuild. + +## Requirements + +The plugin depends on `@expo/config-plugins`, which is already a transitive +dependency of `expo` itself — Expo projects have it for free, nothing extra +to install. + +## Usage + +Add the plugin to your `app.json`/`app.config.js`/`app.config.ts` `plugins` +array, with the same keys documented in +[Selective ML Kit Installation](selective-mlkit-install.md#defaults): + +```json +{ + "expo": { + "plugins": [ + [ + "react-native-vision-camera-mlkit", + { + "textRecognition": true, + "textRecognitionChinese": false, + "textRecognitionDevanagari": false, + "textRecognitionJapanese": false, + "textRecognitionKorean": false, + "barcodeScanning": false + } + ] + ] + } +} +``` + +Or in `app.config.js`: + +```js +module.exports = { + expo: { + plugins: [ + [ + 'react-native-vision-camera-mlkit', + { + textRecognition: true, + barcodeScanning: false, + }, + ], + ], + }, +}; +``` + +Any key you omit keeps the library's own default (see +[Defaults](selective-mlkit-install.md#defaults)). Run `npx expo prebuild` +(or let EAS Build run it for you) to regenerate the native projects with +your configuration applied. + +## What it actually does + +The plugin has two halves, one per platform, both driven by the same +options object: + +- **Android** (`withVisionCameraMLKitAndroid`): prepends an + `ext["react-native-vision-camera-mlkit"] = [ mlkit: [ ... ] ]` Groovy block + to the top of the generated project-level `android/build.gradle` — the + exact same block documented for manual installation. This library's own + `android/build.gradle` reads it via `rootProject.ext` at build time. +- **iOS** (`withVisionCameraMLKitIOS`): inserts a `$VisionCameraMLKit = { ... }` + Ruby hash into the generated `ios/Podfile`, immediately before the first + `target` block — again, the same hash documented for manual installation. + `VisionCameraMLKit.podspec` reads the `$VisionCameraMLKit` global at + `pod install` time. + +Both platform writers are pure string transforms under the hood +(`insertAndroidConfig`/`insertIOSConfig`), so they're easy to reason about +and unit tested directly in this repo (`plugin/__tests__/withAndroid.test.js`, +`plugin/__tests__/withIOS.test.js`). + +## Validation + +Unknown option keys throw at prebuild time instead of silently being +ignored — a typo like `"textRecogniton": true` fails fast with an error +listing every valid key, rather than quietly doing nothing: + +``` +react-native-vision-camera-mlkit config plugin: unknown option(s) "textRecogniton". Valid options: textRecognition, textRecognitionChinese, ... +``` + +## Platform-specific behavior to be aware of + +- **Android-only keys are silently dropped for iOS.** `faceMeshDetection`, + `subjectSegmentation`, and `documentScanner` have no ML Kit iOS + equivalent, so the plugin omits them from the generated `$VisionCameraMLKit` + hash entirely rather than writing a key iOS can't use. This isn't an + error — it's expected, since these are Android-only roadmap features to + begin with. +- **Only Groovy `android/build.gradle` is supported**, not Kotlin DSL + (`.kts`). If your Expo project's generated Android build uses `.kts`, the + plugin throws instead of silently producing a broken file. This mirrors + the library's own native Android config, which is also Groovy-only. +- **Idempotent across repeated prebuilds.** Both writers check for a marker + comment (`// react-native-vision-camera-mlkit selective ML Kit dependencies` + on Android, `# react-native-vision-camera-mlkit selective ML Kit + dependencies` on iOS) and skip re-inserting if it's already present, so + running `expo prebuild` more than once — or across a clean vs. non-clean + prebuild — doesn't duplicate the config block. +- **Runs once per config, even if listed twice.** The plugin is wrapped in + Expo's `createRunOncePlugin`, so listing it more than once in your + `plugins` array (e.g. from a nested config) still only applies it a + single time. +- **Config only, not installation.** The plugin edits `android/build.gradle` + and `ios/Podfile` — it does not run `pod install` or a Gradle sync for + you. Follow your normal Expo prebuild/EAS Build workflow after changing + the plugin's options. + +## When you don't need this plugin + +If you're not using Expo prebuild (bare React Native, or Expo with a fully +custom native project you maintain by hand), skip the plugin and edit +`android/build.gradle`/`ios/Podfile` directly — see +[Selective ML Kit Installation](selective-mlkit-install.md). diff --git a/docs/selective-mlkit-install.md b/docs/selective-mlkit-install.md new file mode 100644 index 0000000..fd916cd --- /dev/null +++ b/docs/selective-mlkit-install.md @@ -0,0 +1,147 @@ +# Selective ML Kit Installation + +This page documents the selective-install configuration in more detail than the README quick-start. + +react-native-vision-camera-mlkit is designed to expose the full set of Google +ML Kit vision APIs, but no single app needs all of them. A consumer who only +wants barcode scanning shouldn't have to ship the text-recognition SDK (or +vice versa) — every additional native SDK adds to APK/IPA size. The +`textRecognition` and `barcodeScanning` flags (and the reserved roadmap keys) +let you opt in to only what your app actually uses. + +## What "selective" means here + +Disabling a feature removes its native SDK from the build **entirely**, +on both platforms — it is not just a runtime gate: + +- **Android**: the corresponding `com.google.mlkit:*` Gradle dependency is + wrapped in `if (mlkitConfig.) { implementation "..." }` in this + library's `android/build.gradle`, and the Kotlin source files that touch + that SDK's types live in a Gradle source set that's only added to the + compile classpath when the flag is on. When disabled, a lightweight stub + takes its place so the rest of the module still compiles; that stub is + never actually reached at runtime. +- **iOS**: the corresponding `GoogleMLKit/*` CocoaPods dependency in + `VisionCameraMLKit.podspec` is wrapped in the same conditional, and every + Swift file that touches that SDK's types is wrapped in a matching + `#if MLKIT_TEXT_RECOGNITION_ANY` / `#if MLKIT_BARCODE_SCANNING` compiler + condition, so the guarded code (and its imports) is fully elided from + compilation when the flag is off. + +Both platforms give you the same guarantee: disable a feature you don't use, +and its model weights and native SDK genuinely never end up in your binary. + +## Feature families + +v2 implements two feature families with a public JS/Nitro API: + +- `textRecognition` (plus the four optional OCR language variants: + `textRecognitionChinese`, `textRecognitionDevanagari`, + `textRecognitionJapanese`, `textRecognitionKorean`) +- `barcodeScanning` + +All other configuration keys are reserved for the +[post-v2 roadmap](../README.md#google-ml-kit-vision-features-roadmap): +`faceDetection`, `faceMeshDetection`, `poseDetection`, +`poseDetectionAccurate`, `selfieSegmentation`, `subjectSegmentation`, +`documentScanner`, `imageLabeling`, `objectDetection`, +`digitalInkRecognition`. Enabling one of these keys today does not provide a +public API — there's nothing to selectively install yet for these, since +they aren't implemented. + +## Defaults + +| Key | Default | Notes | +| ---------------------------- | :-----: | ----------------------------------------- | +| `textRecognition` | `true` | Latin OCR (the base text-recognition SDK) | +| `textRecognitionChinese` | `true` | Adds ~a few MB; disable if unused | +| `textRecognitionDevanagari` | `true` | Adds ~a few MB; disable if unused | +| `textRecognitionJapanese` | `true` | Adds ~a few MB; disable if unused | +| `textRecognitionKorean` | `true` | Adds ~a few MB; disable if unused | +| `barcodeScanning` | `true` | | +| all roadmap keys | `false` | Reserved, no public API | + +If you only need barcode scanning, set `textRecognition` (and all four +language variants) to `false`. If you only need Latin-script OCR, keep +`textRecognition: true` and disable the four language variants — that keeps +the base SDK but drops the larger per-language model packages. + +## Android (Gradle) + +Add this to your app's **root** `android/build.gradle` (not +`android/app/build.gradle`), before the `apply plugin: "com.facebook.react.rootproject"` +line or anywhere at the top level of the file: + +```gradle +ext["react-native-vision-camera-mlkit"] = [ + mlkit: [ + textRecognition: true, + textRecognitionChinese: false, + textRecognitionDevanagari: false, + textRecognitionJapanese: false, + textRecognitionKorean: false, + barcodeScanning: false, + ] +] +``` + +Omit any key to keep its default. This library's own `android/build.gradle` +reads `rootProject.ext["react-native-vision-camera-mlkit"].mlkit`, merges it +over its defaults, and uses the result to both pick the compile-time source +set (see above) and generate `BuildConfig` fields that gate the feature at +runtime as a second layer of protection. + +## iOS (Podfile) + +Add a `$VisionCameraMLKit` hash to your `ios/Podfile`, **before** the first +`target` block: + +```ruby +$VisionCameraMLKit = { + 'textRecognition' => true, + 'textRecognitionChinese' => false, + 'textRecognitionDevanagari' => false, + 'textRecognitionJapanese' => false, + 'textRecognitionKorean' => false, + 'barcodeScanning' => false, +} +``` + +Run `pod install` after changing this hash — CocoaPods only re-resolves +dependencies (including conditional ones) on install/update, not on every +build. + +Omit any key to keep its default. `VisionCameraMLKit.podspec` reads +`$VisionCameraMLKit` the same way `android/build.gradle` reads +`rootProject.ext`. + +## Expo + +If you use an Expo prebuild workflow, use this library's config plugin +instead of hand-editing `android/build.gradle`/`ios/Podfile` — see +[Expo Config Plugin](expo-plugin.md). + +## Verifying a feature was actually excluded + +To confirm a disabled feature's SDK is really gone (not just gated), inspect +the resolved dependency graph after a clean build: + +```sh +# Android — run from the app's android/ directory +./gradlew :app:dependencies --configuration debugRuntimeClasspath | grep mlkit + +# iOS — check the generated Podfile.lock +grep -A2 "GoogleMLKit" ios/Podfile.lock +``` + +A disabled feature's `com.google.mlkit:*` / `GoogleMLKit/*` entries should be +absent entirely, not merely present-but-unused. + +## Calling a disabled feature at runtime + +`useTextRecognition`/`useBarcodeScanning`/`processImageTextRecognition`/ +`processImageBarcodeScanning` throw a setup error if the corresponding flag +was disabled at build time — see +[Error Handling](../README.md#error-handling) and the `getAvailableFeatures`/ +`isFeatureAvailable`/`assertFeatureAvailable` helpers for checking +availability before rendering feature-dependent UI. From d1d7174972d197b83c60736bfc9df6f409552a75 Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 2 Aug 2026 18:15:49 -0300 Subject: [PATCH 44/45] chore: refresh package.json description and keywords for v2 Description now mentions the selective/optional install guarantee restored this release. Keywords gain nitro, expo, and config-plugin to reflect v2's Nitro architecture and the Expo config plugin, both of which had no keyword coverage before. --- package.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 454da80..9be7424 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-vision-camera-mlkit", "version": "2.0.0", - "description": "A React Native Vision Camera (v5, Nitro-based) plugin for Google ML Kit vision features. Ships text recognition (OCR) and barcode scanning today, for real-time frame processing and static images, with more ML Kit vision features (face detection, pose detection, and others) on the roadmap.", + "description": "A React Native Vision Camera (v5, Nitro-based) plugin for Google ML Kit vision features. Ships text recognition (OCR) and barcode scanning today, each independently selectable to keep binary size down, for real-time frame processing and static images, with more ML Kit vision features (face detection, pose detection, and others) on the roadmap.", "main": "./lib/module/index.js", "types": "./lib/typescript/src/index.d.ts", "exports": { @@ -69,7 +69,10 @@ "text-detection", "text-recognition", "barcode-scanner", - "barcode-scanning" + "barcode-scanning", + "nitro", + "expo", + "config-plugin" ], "repository": { "type": "git", From d0f3bbfacc346030c9390637112ba7461ea7296c Mon Sep 17 00:00:00 2001 From: Pedro Bueno Date: Sun, 2 Aug 2026 18:35:31 -0300 Subject: [PATCH 45/45] fix(android): satisfy ktlint function-expression-body in disabled MLKit stubs The textRecognitionDisabled/barcodeScanningDisabled stub factories added for selective install had single-throw block bodies, which ktlint's standard:function-expression-body rule flags in favor of = throw ... expression-body form. Auto-fixed via `ktlint --format`; no behavior change. --- .../mlkit/factories/BarcodeScanningServiceFactory.kt | 3 +-- .../mlkit/factories/TextRecognitionServiceFactory.kt | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/android/src/barcodeScanningDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt b/android/src/barcodeScanningDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt index b53b417..e0b0b1a 100644 --- a/android/src/barcodeScanningDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt +++ b/android/src/barcodeScanningDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/BarcodeScanningServiceFactory.kt @@ -5,10 +5,9 @@ import com.visioncameramlkit.domain.models.BarcodeScanningResult import com.visioncameramlkit.domain.services.IRecognitionService object BarcodeScanningServiceFactory { - fun create(options: BarcodeScanningOptions = BarcodeScanningOptions()): IRecognitionService { + fun create(options: BarcodeScanningOptions = BarcodeScanningOptions()): IRecognitionService = throw IllegalStateException( "Barcode scanning is not enabled in this build. Enable barcodeScanning in your MLKit " + "configuration to use this feature.", ) - } } diff --git a/android/src/textRecognitionDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt b/android/src/textRecognitionDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt index aa102c0..74383f9 100644 --- a/android/src/textRecognitionDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt +++ b/android/src/textRecognitionDisabled/java/com/visioncameramlkit/infrastructure/mlkit/factories/TextRecognitionServiceFactory.kt @@ -5,10 +5,9 @@ import com.visioncameramlkit.domain.models.TextRecognitionResult import com.visioncameramlkit.domain.services.IRecognitionService object TextRecognitionServiceFactory { - fun create(language: TextRecognitionLanguage = TextRecognitionLanguage.LATIN): IRecognitionService { + fun create(language: TextRecognitionLanguage = TextRecognitionLanguage.LATIN): IRecognitionService = throw IllegalStateException( "Text recognition is not enabled in this build. Enable textRecognition (or a language " + "variant) in your MLKit configuration to use this feature.", ) - } }