diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index b820143..97bb898 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -4,19 +4,19 @@ ### React Native Versions -| React Native | Package Version | Architecture Support | Notes | -| ------------ | --------------- | ------------------------------ | ------------------------- | -| 0.70.x | ✅ 1.1.x | Old Architecture | Full support | -| 0.71.x | ✅ 1.1.x | Old Architecture | Full support | -| 0.72.x | ✅ 1.1.x | Old Architecture | Full support | -| 0.73.x | ✅ 1.1.x | Old & New Architecture | Full support | -| 0.74.x | ✅ 1.1.x | Old & New Architecture | Full support | -| 0.75.x | ✅ 1.1.x | Old & New Architecture | Full support | -| 0.76.x | ✅ 1.1.x | New Architecture (default) | Full support | -| 0.77.x | ✅ 1.1.x | New Architecture (default) | Full support | -| 0.78.x | ✅ 1.1.x | New Architecture (default) | Full support | -| **0.79.x** | ✅ **1.1.x** | **New Architecture (default)** | **Latest - Full support** | -| 0.80.x+ | 🔜 1.1.x | New Architecture | Ready when released | +| React Native | Package Version | Architecture Support | Notes | +| ------------ | --------------- | ------------------------------ | --------------------- | +| 0.70.x | ✅ 1.1.x | Old Architecture | Full support | +| 0.71.x | ✅ 1.1.x | Old Architecture | Full support | +| 0.72.x | ✅ 1.1.x | Old Architecture | Full support | +| 0.73.x | ✅ 1.1.x | Old & New Architecture | Full support | +| 0.74.x | ✅ 1.1.x | Old & New Architecture | Full support | +| 0.75.x | ✅ 1.1.x | Old & New Architecture | Full support | +| 0.76.x | ✅ 1.1.x | New Architecture (default) | Full support | +| 0.77.x | ✅ 1.1.x | New Architecture (default) | Full support | +| 0.78.x | ✅ 1.1.x | New Architecture (default) | Full support | +| **0.79.x** | ✅ **1.1.x** | **New Architecture (default)** | **Full support** | +| 0.80.x+ | 🔜 1.1.x | New Architecture | Compatibility pending | ### React Versions @@ -28,12 +28,12 @@ ### Expo SDK Compatibility -| Expo SDK | React Native | Package Version | Status | -| ---------- | ------------ | --------------- | --------------------------------------------- | -| SDK 49 | 0.72.x | ✅ 1.1.x | Supported (requires prebuild) | -| SDK 50 | 0.73.x | ✅ 1.1.x | Supported (requires prebuild) | -| SDK 51 | 0.74.x | ✅ 1.1.x | Supported (requires prebuild) | -| **SDK 52** | **0.79.x** | ✅ **1.1.x** | **Latest - Full support (requires prebuild)** | +| Expo SDK | React Native | Package Version | Status | +| ---------- | ------------ | --------------- | -------------------------------------------- | +| SDK 49 | 0.72.x | ✅ 1.1.x | Supported (requires prebuild) | +| SDK 50 | 0.73.x | ✅ 1.1.x | Supported (requires prebuild) | +| SDK 51 | 0.74.x | ✅ 1.1.x | Supported (requires prebuild) | +| **SDK 52** | **0.76.x** | ✅ **1.1.x** | **Example app baseline (requires prebuild)** | **Note**: This library requires prebuild for Expo projects as it uses native modules. @@ -52,9 +52,9 @@ ### Android Requirements -- **Minimum SDK**: 21 (Android 5.0 Lollipop) -- **Target SDK**: 33+ (Android 13+) -- **Compile SDK**: 33+ +- **Minimum SDK**: 24 (Android 7.0 Nougat) +- **Target SDK**: 34+ (Android 14+) +- **Compile SDK**: 35+ - **Kotlin**: 1.6.0+ - **Gradle**: 7.0+ - **Android Gradle Plugin**: 7.0+ @@ -142,7 +142,7 @@ If you're upgrading from React Native 0.6x: 3. Update iOS deployment target to 13.4 or higher -4. Update Android minSdkVersion to 21 or higher +4. Update Android minSdkVersion to 24 or higher ### From Old to New Architecture diff --git a/README.md b/README.md index 13a47c3..ca5e8a6 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ Scan QR codes and barcodes from local image files in React Native (iOS Vision + ## Requirements -- React Native `>=0.70.0`, React `>=17.0.0`, Node `>=18` -- iOS `13.4+`, Android `minSdkVersion 21+` +- React Native `>=0.70.0` (validated through `0.79.x`; `0.80.x+` compatibility is pending), React `>=17.0.0`, Node `>=18` +- iOS `13.4+`, Android `minSdkVersion 24+` ## Installation @@ -45,7 +45,7 @@ npx expo run:ios # or: npx expo run:android ### React Native CLI ```bash -npm install react-native-image-code-scanner +yarn add react-native-image-code-scanner cd ios && pod install ``` @@ -58,11 +58,18 @@ If your app picks images from camera, add to `Info.plist`: ## Usage -Works with any image picker — pass the local file URI to `scan()`: +Works with image pickers that return a supported local path or URI: + +- Android: local file paths, `file://` URIs, and resolver-backed `content://` URIs. +- iOS: local file paths and `file://` URIs, including percent-encoded paths. + +Schemes such as `ph://`, `assets-library://`, remote URLs, and other provider-specific values are not guaranteed to work. ```ts import * as ImagePicker from 'expo-image-picker'; -import ImageCodeScanner, { BarcodeFormat } from 'react-native-image-code-scanner'; +import ImageCodeScanner, { + BarcodeFormat, +} from 'react-native-image-code-scanner'; async function pickAndScan() { const picked = await ImagePicker.launchImageLibraryAsync({ @@ -82,7 +89,7 @@ async function pickAndScan() { } ``` -Also compatible with `react-native-image-picker` and other pickers that return a local URI. +Also compatible with `react-native-image-picker` and other pickers when they return one of the supported values above. ## API @@ -92,6 +99,9 @@ ImageCodeScanner.scan(options: ScanOptions): Promise interface ScanOptions { path: string; formats?: BarcodeFormat[]; // default: [QR_CODE] + enhanceContrast?: boolean; // default: true + convertToGrayscale?: boolean; // default: true + tryRotations?: boolean; // default: true } interface ScanResult { @@ -104,7 +114,8 @@ interface ScanResult { **Supported formats:** `QR_CODE`, `CODE_128`, `CODE_39`, `CODE_93`, `EAN_13`, `EAN_8`, `UPC_A`, `UPC_E`, `PDF_417`, `DATA_MATRIX`, `AZTEC`, `ITF`, `CODABAR` -Preprocessing runs automatically: original → grayscale → contrast enhancement → rotation retries. +Preprocessing runs automatically by default: original → grayscale → contrast enhancement → rotation retries. +Set `enhanceContrast`, `convertToGrayscale`, or `tryRotations` to `false` to skip those retry passes. ## Tips @@ -116,13 +127,53 @@ Preprocessing runs automatically: original → grayscale → contrast enhancemen - **No result:** check image quality/resolution, limit `formats`, crop closer to the barcode. - **iOS build:** `cd ios && pod install`; deployment target `13.4+`. -- **Android build:** `cd android && ./gradlew clean`; `minSdkVersion >= 21`. +- **Android build:** `cd android && ./gradlew clean`; `minSdkVersion >= 24`. - **Expo:** requires prebuild; not supported in Expo Go. ## Example App See [example app](./example) and [example README](./example/README.md). +## Publishing to npm + +1. Run checks: + + ```bash + yarn typecheck + yarn lint + yarn test + yarn build + ``` + +2. Create a release: + + ```bash + yarn release + ``` + + This bumps the version and creates a `vX.Y.Z` tag. + +3. Push the release commit and tag: + + ```bash + git push && git push --tags + ``` + +GitHub Actions publishes to npm automatically after CI passes for the release tag. + +Verify the published version: + +```bash +npm view react-native-image-code-scanner version +``` + +For a beta release: + +```bash +yarn release:beta +git push && git push --tags +``` + ## Contributing Please read [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/android/src/main/java/com/imagecodescanner/ImageCodeScannerModule.kt b/android/src/main/java/com/imagecodescanner/ImageCodeScannerModule.kt index 95ad1b7..4d693d3 100644 --- a/android/src/main/java/com/imagecodescanner/ImageCodeScannerModule.kt +++ b/android/src/main/java/com/imagecodescanner/ImageCodeScannerModule.kt @@ -7,6 +7,7 @@ import android.graphics.ColorMatrix import android.graphics.ColorMatrixColorFilter import android.graphics.Matrix import android.graphics.Paint +import android.net.Uri import com.imagecodescanner.NativeImageCodeScannerSpec import com.facebook.react.bridge.* import com.facebook.react.module.annotations.ReactModule @@ -15,6 +16,10 @@ import com.google.mlkit.vision.barcode.BarcodeScannerOptions import com.google.mlkit.vision.barcode.common.Barcode import com.google.mlkit.vision.common.InputImage import java.io.File +import java.io.FileNotFoundException +import java.io.FileInputStream +import java.io.InputStream +import java.util.concurrent.atomic.AtomicBoolean @ReactModule(name = ImageCodeScannerModule.NAME) class ImageCodeScannerModule(reactContext: ReactApplicationContext) : @@ -24,19 +29,36 @@ class ImageCodeScannerModule(reactContext: ReactApplicationContext) : return NAME } + private fun debugLog(message: String) { + if (BuildConfig.DEBUG) { + android.util.Log.d(TAG, message) + } + } + + private fun debugWarn(message: String) { + if (BuildConfig.DEBUG) { + android.util.Log.w(TAG, message) + } + } + + private fun debugError(message: String, throwable: Throwable) { + if (BuildConfig.DEBUG) { + android.util.Log.e(TAG, message, throwable) + } + } + private fun scaleBitmapIfNeeded(bitmap: Bitmap): Bitmap { - val maxSize = 1024 // Max width or height val width = bitmap.width val height = bitmap.height - if (width <= maxSize && height <= maxSize) { + if (width <= MAX_IMAGE_DIMENSION && height <= MAX_IMAGE_DIMENSION) { return bitmap } val scale = if (width > height) { - maxSize.toFloat() / width + MAX_IMAGE_DIMENSION.toFloat() / width } else { - maxSize.toFloat() / height + MAX_IMAGE_DIMENSION.toFloat() / height } val newWidth = (width * scale).toInt() @@ -97,57 +119,105 @@ class ImageCodeScannerModule(reactContext: ReactApplicationContext) : matrix.postRotate(degrees) return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) } + + private fun readBooleanOption(options: ReadableMap, key: String, defaultValue: Boolean): Boolean { + if (!options.hasKey(key) || options.isNull(key)) { + return defaultValue + } + + return when (options.getType(key)) { + ReadableType.Boolean -> options.getBoolean(key) + ReadableType.Number -> options.getDouble(key) != 0.0 + else -> defaultValue + } + } + + private fun openImageInputStream(path: String): InputStream { + val uri = Uri.parse(path) + return when (uri.scheme?.lowercase()) { + null, "" -> FileInputStream(File(Uri.decode(path))) + "file" -> { + val filePath = uri.path ?: Uri.decode(path.removePrefix("file://")) + FileInputStream(File(filePath)) + } + "content" -> reactApplicationContext.contentResolver.openInputStream(uri) + ?: throw IllegalArgumentException("Cannot open image URI: $path") + else -> throw IllegalArgumentException("Unsupported image URI scheme: ${uri.scheme}") + } + } + + private fun decodeBitmapFromPath(path: String): Bitmap? { + val boundsOptions = BitmapFactory.Options() + boundsOptions.inJustDecodeBounds = true + openImageInputStream(path).use { input -> + BitmapFactory.decodeStream(input, null, boundsOptions) + } + + if (boundsOptions.outWidth <= 0 || boundsOptions.outHeight <= 0) { + return null + } + + var sampleSize = 1 + while (boundsOptions.outWidth / sampleSize > MAX_IMAGE_DIMENSION || boundsOptions.outHeight / sampleSize > MAX_IMAGE_DIMENSION) { + sampleSize *= 2 + } + + val bitmapOptions = BitmapFactory.Options() + bitmapOptions.inSampleSize = sampleSize + return openImageInputStream(path).use { input -> + BitmapFactory.decodeStream(input, null, bitmapOptions) + } + } override fun scanFromPath(path: String, formats: ReadableArray, options: ReadableMap, promise: Promise) { - android.util.Log.d("ImageCodeScanner", "scanFromPath called with path: $path") - android.util.Log.d("ImageCodeScanner", "Starting scan with all preprocessing options enabled") - - val cleanPath = path.replace("file://", "") - val imgFile = File(cleanPath) - - if (!imgFile.exists()) { - promise.reject("INVALID_PATH", "Image file does not exist: $path", null) - return + debugLog("scanFromPath called with path: $path") + val shouldEnhanceContrast = readBooleanOption(options, "enhanceContrast", true) + val shouldConvertToGrayscale = readBooleanOption(options, "convertToGrayscale", true) + val shouldTryRotations = readBooleanOption(options, "tryRotations", true) + debugLog( + "Starting scan with preprocessing options: enhanceContrast=$shouldEnhanceContrast, convertToGrayscale=$shouldConvertToGrayscale, tryRotations=$shouldTryRotations" + ) + + var baseBitmap: Bitmap? = null + val hasResolved = AtomicBoolean(false) + + fun safeResolve(value: Any?) { + if (hasResolved.compareAndSet(false, true)) { + promise.resolve(value) + } } - val imagesToTry = mutableListOf>() + fun safeReject(code: String, message: String, error: Throwable?) { + if (hasResolved.compareAndSet(false, true)) { + promise.reject(code, message, error) + } + } fun cleanupBitmaps() { - val recycled = mutableSetOf() - imagesToTry.forEach { (_, bitmap) -> - val identity = System.identityHashCode(bitmap) - if (!bitmap.isRecycled && recycled.add(identity)) { + baseBitmap?.let { bitmap -> + if (!bitmap.isRecycled) { bitmap.recycle() } } - imagesToTry.clear() + baseBitmap = null } - try { - // Load and validate bitmap with sampling if too large - val bitmapOptions = BitmapFactory.Options() - bitmapOptions.inJustDecodeBounds = true - BitmapFactory.decodeFile(imgFile.absolutePath, bitmapOptions) - - // Calculate sample size if image is too large - var sampleSize = 1 - val maxDimension = 2048 - while (bitmapOptions.outWidth / sampleSize > maxDimension || bitmapOptions.outHeight / sampleSize > maxDimension) { - sampleSize *= 2 + fun cleanupAttemptBitmap(bitmap: Bitmap) { + if (bitmap !== baseBitmap && !bitmap.isRecycled) { + bitmap.recycle() } - - bitmapOptions.inJustDecodeBounds = false - bitmapOptions.inSampleSize = sampleSize - - val originalBitmap = BitmapFactory.decodeFile(imgFile.absolutePath, bitmapOptions) + } + + try { + val originalBitmap = decodeBitmapFromPath(path) if (originalBitmap == null) { - promise.reject("INVALID_IMAGE", "Cannot decode image file: $path", null) + safeReject("INVALID_IMAGE", "Cannot decode image file: $path", null) return } // Scale if needed for better processing - val bitmap = scaleBitmapIfNeeded(originalBitmap) - if (bitmap !== originalBitmap && !originalBitmap.isRecycled) { + baseBitmap = scaleBitmapIfNeeded(originalBitmap) + if (baseBitmap !== originalBitmap && !originalBitmap.isRecycled) { originalBitmap.recycle() } @@ -172,7 +242,7 @@ class ImageCodeScannerModule(reactContext: ReactApplicationContext) : "CODABAR" -> barcodeFormats.add(Barcode.FORMAT_CODABAR) else -> { // Log unsupported format but continue - android.util.Log.w("ImageCodeScanner", "Unsupported format: $format") + debugWarn("Unsupported format: $format") } } } @@ -187,54 +257,71 @@ class ImageCodeScannerModule(reactContext: ReactApplicationContext) : .setBarcodeFormats(barcodeFormats.first(), *barcodeFormats.drop(1).toIntArray()) .build() - // List of images to try with different preprocessing - always try all options - imagesToTry.add("Original" to bitmap) - - // Always add grayscale version - try { - imagesToTry.add("Grayscale" to convertToGrayscale(bitmap)) - android.util.Log.d("ImageCodeScanner", "Added grayscale version") - } catch (e: Exception) { - android.util.Log.w("ImageCodeScanner", "Failed to create grayscale: ${e.message}") + val attempts = mutableListOf Bitmap>>() + attempts.add("Original" to { baseBitmap ?: throw IllegalStateException("Base bitmap has been recycled") }) + + if (shouldConvertToGrayscale) { + attempts.add("Grayscale" to { + convertToGrayscale(baseBitmap ?: throw IllegalStateException("Base bitmap has been recycled")) + }) } - // Always add enhanced contrast version - try { - imagesToTry.add("Enhanced contrast" to enhanceContrast(bitmap)) - android.util.Log.d("ImageCodeScanner", "Added contrast enhanced version") - } catch (e: Exception) { - android.util.Log.w("ImageCodeScanner", "Failed to enhance contrast: ${e.message}") + if (shouldEnhanceContrast) { + attempts.add("Enhanced contrast" to { + enhanceContrast(baseBitmap ?: throw IllegalStateException("Base bitmap has been recycled")) + }) } - // Always add rotated versions - try { - imagesToTry.add("Rotated 90°" to rotateBitmap(bitmap, 90f)) - imagesToTry.add("Rotated 180°" to rotateBitmap(bitmap, 180f)) - imagesToTry.add("Rotated 270°" to rotateBitmap(bitmap, 270f)) - android.util.Log.d("ImageCodeScanner", "Added rotated versions") - } catch (e: Exception) { - android.util.Log.w("ImageCodeScanner", "Failed to rotate: ${e.message}") + if (shouldTryRotations) { + attempts.add("Rotated 90°" to { + rotateBitmap(baseBitmap ?: throw IllegalStateException("Base bitmap has been recycled"), 90f) + }) + attempts.add("Rotated 180°" to { + rotateBitmap(baseBitmap ?: throw IllegalStateException("Base bitmap has been recycled"), 180f) + }) + attempts.add("Rotated 270°" to { + rotateBitmap(baseBitmap ?: throw IllegalStateException("Base bitmap has been recycled"), 270f) + }) } var currentIndex = 0 fun tryNextImage() { - if (currentIndex >= imagesToTry.size) { + if (currentIndex >= attempts.size) { // No more images to try, return empty result val arr = Arguments.fromList(emptyList()) cleanupBitmaps() - promise.resolve(arr) + safeResolve(arr) return } - val (description, currentBitmap) = imagesToTry[currentIndex] + val (description, createBitmap) = attempts[currentIndex] currentIndex++ + + val currentBitmap = try { + createBitmap() + } catch (e: Exception) { + debugWarn("Failed to create $description image: ${e.message}") + tryNextImage() + return + } val image = InputImage.fromBitmap(currentBitmap, 0) val scanner = BarcodeScanning.getClient(scannerOptions) + + fun tryNextImageOrReject() { + try { + tryNextImage() + } catch (e: Exception) { + debugError("Failed to continue image scan", e) + cleanupBitmaps() + safeReject("IMAGE_LOAD_ERROR", "Error loading image: ${e.message}", e) + } + } scanner.process(image) .addOnSuccessListener { barcodes -> + var shouldTryNext = false try { if (barcodes.isNotEmpty()) { // Found barcodes, process and return @@ -264,27 +351,34 @@ class ImageCodeScannerModule(reactContext: ReactApplicationContext) : resultMap } else null } - .filter { it != null } val arr = Arguments.createArray() codes.forEach { code -> arr.pushMap(code) } + cleanupAttemptBitmap(currentBitmap) cleanupBitmaps() - promise.resolve(arr) + safeResolve(arr) } else { // No barcodes found, try next preprocessing - tryNextImage() + cleanupAttemptBitmap(currentBitmap) + shouldTryNext = true } } catch (e: Exception) { - android.util.Log.e("ImageCodeScanner", "Error processing results for $description", e) - tryNextImage() + debugError("Error processing results for $description", e) + cleanupAttemptBitmap(currentBitmap) + shouldTryNext = true } finally { scanner.close() } + + if (shouldTryNext) { + tryNextImageOrReject() + } } .addOnFailureListener { exception -> - android.util.Log.e("ImageCodeScanner", "Scan failed for $description: ${exception.message}") + debugError("Scan failed for $description: ${exception.message}", exception) + cleanupAttemptBitmap(currentBitmap) scanner.close() - tryNextImage() + tryNextImageOrReject() } } @@ -293,11 +387,17 @@ class ImageCodeScannerModule(reactContext: ReactApplicationContext) : } catch (e: Exception) { cleanupBitmaps() - promise.reject("IMAGE_LOAD_ERROR", "Error loading image: ${e.message}", e) + if (e is FileNotFoundException || e is SecurityException) { + safeReject("INVALID_PATH", "Image file does not exist or cannot be opened: $path", e) + } else { + safeReject("IMAGE_LOAD_ERROR", "Error loading image: ${e.message}", e) + } } } companion object { const val NAME = "ImageCodeScanner" + private const val TAG = "ImageCodeScanner" + private const val MAX_IMAGE_DIMENSION = 2048 } } diff --git a/eslint.config.mjs b/eslint.config.mjs index 9286098..d36027f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -18,7 +18,10 @@ export default [ ignores: [ 'node_modules/**', 'lib/**', - 'example/**', + 'example/android/**', + 'example/ios/**', + 'example/node_modules/**', + 'example/.expo/**', 'coverage/**', 'build/**', '.yarn/**', diff --git a/example/README.md b/example/README.md index 7d63f25..6d9c55c 100644 --- a/example/README.md +++ b/example/README.md @@ -12,83 +12,117 @@ This app demonstrates how to use `react-native-image-code-scanner` in an Expo pr ## Requirements - Node.js `>=18` +- Yarn `3.6.1` via Corepack - iOS: Xcode (for native build) - Android: Android Studio + SDK (for native build) +Use Yarn for this workspace. Do not mix npm installs with the generated native projects. + ## Install +From the repository root: + ```bash -cd example -npm install -# or yarn install ``` -## Run modes +## Metro / Fast Refresh -### 1) Expo Go / quick UI check +Run Metro from the example folder: ```bash -npm start -# or -yarn start +yarn expo start --clear --dev-client --host localhost +``` + +- Press `i` for iOS simulator. +- Press `a` for Android emulator. +- If the app was opened before Metro started, terminate the app and open it again from Metro. +- JS/TSX changes use Fast Refresh. Native, Podfile, Gradle, or architecture changes require rebuild. + +## New Architecture + +`app.json` is the source of truth: + +```json +{ + "expo": { + "newArchEnabled": true + } +} ``` -- Useful for UI iteration. -- Barcode scanning will not work in Expo Go because this package uses native modules. +After changing this value, run `prebuild --clean` and rebuild the native app. + +## iOS Build -### 2) Native run (recommended, full functionality) +Clean regenerate iOS and build: ```bash -# iOS -npm run ios +cd example +yarn expo prebuild --clean --platform ios +yarn expo run:ios --no-build-cache +``` -# Android -npm run android +With Metro running in another terminal: + +```bash +cd example +yarn expo run:ios --no-bundler --no-build-cache ``` -If you need a clean native regeneration: +Verify New Architecture: ```bash -npm run prebuild:clean -npm run ios -# or -npm run android +cat ios/Podfile.properties.json +# expect: "newArchEnabled": "true" ``` -## How to test +## Android Build -1. Open app on iOS/Android native build. -2. Choose `Camera` or `Gallery`. -3. Pick barcode formats. -4. Tap `Scan Image`. -5. Check detected results and scan time. +Clean regenerate Android and build: + +```bash +cd example +yarn expo prebuild --clean --platform android +yarn expo run:android --no-build-cache +``` + +With Metro running in another terminal: + +```bash +cd example +yarn expo run:android --no-bundler --no-build-cache +``` + +Verify New Architecture: + +```bash +grep newArchEnabled android/gradle.properties +# expect: newArchEnabled=true +``` ## Notes -- Preprocessing is automatic (grayscale, contrast enhancement, rotation attempts). +- Preprocessing options are configurable in the UI: contrast, grayscale, rotations. - At least one barcode format must stay selected. - Permissions for camera and media library are requested at runtime. +- Expo Go is not enough for scanning because this package uses native modules. ## Troubleshooting -- `No barcodes found`: - - Use a clearer image or crop closer to barcode. - - Select only relevant formats. -- iOS build errors: - - Run `npm run prebuild:clean` then `npm run ios`. -- Android build errors: - - Run `npm run prebuild:clean` then `npm run android`. -- Metro cache issues: - - Run `expo start -c`. +- White screen: Metro is probably not running or the app opened before Metro. Start Metro, terminate the app, then press `i`/`a`. +- No Fast Refresh: confirm `http://127.0.0.1:8081/status` works and open the app from Metro. +- iOS native issue after config changes: run `yarn expo prebuild --clean --platform ios`. +- Android native issue after config changes: run `yarn expo prebuild --clean --platform android`. +- Metro cache issue: run `yarn expo start --clear --dev-client --host localhost`. ## Scripts -- `npm start`: Start Expo dev server -- `npm run ios`: Build and run on iOS (native) -- `npm run android`: Build and run on Android (native) -- `npm run prebuild`: Generate native projects -- `npm run prebuild:clean`: Regenerate native projects from scratch +- `yarn start`: Start Expo dev server +- `yarn ios`: Build and run on iOS +- `yarn android`: Build and run on Android +- `yarn prebuild`: Generate native projects +- `yarn prebuild:clean`: Regenerate native projects from scratch ## Related docs diff --git a/example/app.json b/example/app.json index a9e368f..2b2d65f 100644 --- a/example/app.json +++ b/example/app.json @@ -3,11 +3,10 @@ "name": "ImageCodeScannerExample", "slug": "image-code-scanner-example", "version": "1.0.0", + "newArchEnabled": true, "orientation": "portrait", "userInterfaceStyle": "light", - "assetBundlePatterns": [ - "**/*" - ], + "assetBundlePatterns": ["**/*"], "ios": { "supportsTablet": true, "bundleIdentifier": "com.imagecodescanner.example", diff --git a/example/expo-env.d.ts b/example/expo-env.d.ts deleted file mode 100644 index 61df48b..0000000 --- a/example/expo-env.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -/// -/// -/// diff --git a/example/metro.config.js b/example/metro.config.js new file mode 100644 index 0000000..0c3db3c --- /dev/null +++ b/example/metro.config.js @@ -0,0 +1,42 @@ +const path = require('path'); +const { getDefaultConfig } = require('expo/metro-config'); +const exclusionList = require('metro-config/src/defaults/exclusionList'); + +const projectRoot = __dirname; +const workspaceRoot = path.resolve(projectRoot, '..'); +const appNodeModules = path.join(projectRoot, 'node_modules'); +const libraryPackage = require('../package.json'); +const peerDependencies = Object.keys(libraryPackage.peerDependencies ?? {}); + +const escapePathForRegex = (filePath) => + filePath.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); + +const config = getDefaultConfig(projectRoot); + +config.watchFolders = [...(config.watchFolders ?? []), workspaceRoot]; + +config.resolver = { + ...config.resolver, + blockList: exclusionList( + peerDependencies.map( + (dependency) => + new RegExp( + `^${escapePathForRegex( + path.join(workspaceRoot, 'node_modules', dependency) + )}\\/.*$` + ) + ) + ), + extraNodeModules: { + ...(config.resolver.extraNodeModules ?? {}), + [libraryPackage.name]: path.join(workspaceRoot, 'src'), + ...Object.fromEntries( + peerDependencies.map((dependency) => [ + dependency, + path.join(appNodeModules, dependency), + ]) + ), + }, +}; + +module.exports = config; diff --git a/example/package.json b/example/package.json index cc0f7a1..b416b9c 100644 --- a/example/package.json +++ b/example/package.json @@ -2,13 +2,14 @@ "name": "react-native-image-code-scanner-example", "version": "1.0.0", "private": true, - "main": "App.tsx", + "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", "android": "expo run:android", "ios": "expo run:ios", "web": "expo start --web", - "lint": "eslint .", + "lint": "eslint \"src/**/*.{ts,tsx}\" App.tsx", + "typecheck": "tsc --noEmit", "setup": "expo install", "prebuild": "expo prebuild", "prebuild:clean": "expo prebuild --clean", diff --git a/example/src/App.tsx b/example/src/App.tsx index 40f3839..282188e 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -10,7 +10,6 @@ import { Image, SafeAreaView, Platform, - Switch, } from 'react-native'; import ImageCodeScanner, { BarcodeFormat, @@ -26,14 +25,28 @@ interface ScanResultWithTime { } const BARCODE_FORMATS = [ - { key: BarcodeFormat.QR_CODE, label: 'QR Code' }, - { key: BarcodeFormat.CODE_128, label: 'Code 128' }, - { key: BarcodeFormat.CODE_39, label: 'Code 39' }, + { key: BarcodeFormat.QR_CODE, label: 'QR' }, + { key: BarcodeFormat.CODE_128, label: '128' }, + { key: BarcodeFormat.CODE_39, label: '39' }, { key: BarcodeFormat.EAN_13, label: 'EAN-13' }, + { key: BarcodeFormat.EAN_8, label: 'EAN-8' }, + { key: BarcodeFormat.UPC_A, label: 'UPC-A' }, + { key: BarcodeFormat.UPC_E, label: 'UPC-E' }, { key: BarcodeFormat.PDF_417, label: 'PDF417' }, - { key: BarcodeFormat.DATA_MATRIX, label: 'Data Matrix' }, + { key: BarcodeFormat.DATA_MATRIX, label: 'DM' }, + { key: BarcodeFormat.AZTEC, label: 'Aztec' }, + { key: BarcodeFormat.ITF, label: 'ITF' }, + { key: BarcodeFormat.CODABAR, label: 'Codabar' }, ]; +const PREPROCESSING_OPTIONS = [ + { key: 'enhanceContrast', label: 'Contrast' }, + { key: 'convertToGrayscale', label: 'Grayscale' }, + { key: 'tryRotations', label: 'Rotate' }, +] as const; + +type PreprocessingOption = (typeof PREPROCESSING_OPTIONS)[number]['key']; + export default function App() { const [selectedImage, setSelectedImage] = useState(null); const [scanResult, setScanResult] = useState(null); @@ -41,6 +54,39 @@ export default function App() { const [selectedFormats, setSelectedFormats] = useState([ BarcodeFormat.QR_CODE, ]); + const [preprocessing, setPreprocessing] = useState< + Record + >({ + enhanceContrast: true, + convertToGrayscale: true, + tryRotations: true, + }); + + const toggleFormat = (format: BarcodeFormat) => { + setSelectedFormats((prev) => { + if (prev.includes(format)) { + if (prev.length === 1) { + Alert.alert( + 'Format Required', + 'At least one format must be selected' + ); + return prev; + } + return prev.filter((item) => item !== format); + } + return [...prev, format]; + }); + }; + + const setPreprocessingOption = ( + option: PreprocessingOption, + value: boolean + ) => { + setPreprocessing((prev) => ({ + ...prev, + [option]: value, + })); + }; const requestPermissions = async () => { const { status: cameraStatus } = @@ -67,14 +113,14 @@ export default function App() { if (type === 'camera') { result = await ImagePicker.launchCameraAsync({ - mediaTypes: ImagePicker.MediaTypeOptions.Images, + mediaTypes: ['images'], allowsEditing: true, aspect: [4, 3], quality: 1, }); } else { result = await ImagePicker.launchImageLibraryAsync({ - mediaTypes: ImagePicker.MediaTypeOptions.Images, + mediaTypes: ['images'], allowsEditing: true, aspect: [4, 3], quality: 1, @@ -106,7 +152,7 @@ export default function App() { const results = await ImageCodeScanner.scan({ path: selectedImage, formats: selectedFormats, - // Preprocessing is always enabled automatically for optimal results + ...preprocessing, }); const endTime = Date.now(); @@ -136,24 +182,22 @@ export default function App() { Image Code Scanner - Test all features with Expo {/* Image Selection */} - Select Image handleImagePicker('camera')} > - 📷 Camera + Camera handleImagePicker('gallery')} > - 🖼️ Gallery + Gallery @@ -167,55 +211,82 @@ export default function App() { )} - {/* Barcode Format Selection */} + {/* Scan Options */} - Barcode Formats - - Select formats to scan for: - - {BARCODE_FORMATS.map((format) => ( - - {format.label} - { - if (value) { - setSelectedFormats((prev) => - prev.includes(format.key) ? prev : [...prev, format.key] - ); - } else { - setSelectedFormats((prev) => { - if (prev.length === 1) { - Alert.alert( - 'Format Required', - 'At least one format must be selected' - ); - return prev; - } - return prev.filter((f) => f !== format.key); - }); - } - }} - /> + + Formats + + + setSelectedFormats( + BARCODE_FORMATS.map((format) => format.key) + ) + } + > + All + + setSelectedFormats([BarcodeFormat.QR_CODE])} + > + QR + - ))} - - - {/* Automatic Preprocessing Info */} - - Automatic Preprocessing - - The following enhancements are automatically applied: - + - - • Grayscale conversion for better barcode detection + + {BARCODE_FORMATS.map((format) => { + const selected = selectedFormats.includes(format.key); + return ( + toggleFormat(format.key)} + > + + {format.label} + + + ); + })} - - • Contrast enhancement for improved readability + + + + + Preprocessing - - • Automatic rotation detection (0°, 90°, 180°, 270°) + + {PREPROCESSING_OPTIONS.map((option) => { + const selected = preprocessing[option.key]; + return ( + + setPreprocessingOption( + option.key, + !preprocessing[option.key] + ) + } + > + + {option.label} + + + ); + })} @@ -264,53 +335,47 @@ const styles = StyleSheet.create({ backgroundColor: '#f5f5f5', }, scrollContent: { - padding: 16, + padding: 14, }, header: { alignItems: 'center', - marginBottom: 24, + marginBottom: 14, }, title: { - fontSize: 28, - fontWeight: 'bold', + fontSize: 22, + fontWeight: '700', color: '#333', }, - subtitle: { - fontSize: 16, - color: '#666', - marginTop: 4, - }, section: { backgroundColor: '#fff', - borderRadius: 12, - padding: 16, - marginBottom: 16, + borderRadius: 8, + padding: 12, + marginBottom: 12, shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.1, - shadowRadius: 4, - elevation: 3, + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.08, + shadowRadius: 3, + elevation: 2, + }, + sectionHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: 10, }, sectionTitle: { - fontSize: 18, + fontSize: 15, fontWeight: '600', - marginBottom: 8, color: '#333', }, - sectionSubtitle: { - fontSize: 14, - color: '#666', - marginBottom: 12, - }, buttonRow: { flexDirection: 'row', - justifyContent: 'space-between', - gap: 12, + gap: 8, }, button: { flex: 1, backgroundColor: '#007AFF', - padding: 12, + paddingVertical: 11, borderRadius: 8, alignItems: 'center', }, @@ -320,31 +385,95 @@ const styles = StyleSheet.create({ fontWeight: '500', }, imageContainer: { - marginTop: 16, + marginTop: 10, alignItems: 'center', }, previewImage: { width: '100%', - height: 200, + height: 160, borderRadius: 8, resizeMode: 'contain', backgroundColor: '#f0f0f0', }, - optionRow: { + presetRow: { flexDirection: 'row', - justifyContent: 'space-between', + gap: 6, + }, + presetButton: { + borderWidth: 1, + borderColor: '#D7DEE8', + borderRadius: 999, + paddingHorizontal: 10, + paddingVertical: 5, + }, + presetText: { + color: '#007AFF', + fontSize: 12, + fontWeight: '600', + }, + formatGrid: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 8, + }, + chip: { + minWidth: 58, alignItems: 'center', + borderWidth: 1, + borderColor: '#D7DEE8', + backgroundColor: '#F8FAFC', + borderRadius: 999, + paddingHorizontal: 10, paddingVertical: 8, }, - infoRow: { - paddingVertical: 4, + selectedChip: { + backgroundColor: '#E8F2FF', + borderColor: '#007AFF', + }, + chipText: { + color: '#334155', + fontSize: 12, + fontWeight: '600', + }, + selectedChipText: { + color: '#0057C2', + }, + divider: { + height: 1, + backgroundColor: '#EEF2F6', + marginVertical: 12, + }, + optionGrid: { + flexDirection: 'row', + gap: 8, + }, + optionChip: { + flex: 1, + alignItems: 'center', + borderWidth: 1, + borderColor: '#D7DEE8', + backgroundColor: '#F8FAFC', + borderRadius: 8, + paddingVertical: 9, + }, + selectedOption: { + backgroundColor: '#ECFDF3', + borderColor: '#2EAD63', + }, + optionText: { + color: '#334155', + fontSize: 12, + fontWeight: '600', + }, + selectedOptionText: { + color: '#168046', }, scanButton: { backgroundColor: '#4CAF50', - padding: 16, + padding: 15, borderRadius: 8, alignItems: 'center', - marginVertical: 16, + marginBottom: 12, }, disabledButton: { backgroundColor: '#ccc', diff --git a/example/tsconfig.json b/example/tsconfig.json index 06aa2a6..f281907 100644 --- a/example/tsconfig.json +++ b/example/tsconfig.json @@ -13,8 +13,12 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", - "baseUrl": "." + "baseUrl": ".", + "paths": { + "react-native-image-code-scanner": ["../src/index"] + } }, "include": ["**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "exclude": ["node_modules"], + "extends": "expo/tsconfig.base" } diff --git a/ios/ImageCodeScanner.swift b/ios/ImageCodeScanner.swift index 9ddf4a7..e69f990 100644 --- a/ios/ImageCodeScanner.swift +++ b/ios/ImageCodeScanner.swift @@ -13,6 +13,7 @@ class ImageCodeScanner: NSObject, RCTBridgeModule { // Shared CIContext for reuse across image processing operations private let sharedCIContext = CIContext(options: nil) + private let maxImageDimension: CGFloat = 2048 // MARK: - Image Preprocessing Methods @@ -89,7 +90,9 @@ class ImageCodeScanner: NSObject, RCTBridgeModule { let rendered = UIGraphicsImageRenderer(size: newSizePx, format: format).image { _ in image.draw(in: CGRect(origin: .zero, size: newSizePx)) } - return UIImage(cgImage: rendered.cgImage!, scale: image.scale, orientation: image.imageOrientation) + guard let cgImage = rendered.cgImage else { return image } + // draw() bakes EXIF orientation into pixels; keep metadata upright to avoid double rotation in Vision. + return UIImage(cgImage: cgImage, scale: image.scale, orientation: .up) } private func cgImagePropertyOrientation(from o: UIImage.Orientation) -> CGImagePropertyOrientation { @@ -111,15 +114,43 @@ class ImageCodeScanner: NSObject, RCTBridgeModule { return payload.allSatisfy(\.isNumber) } + private func boolOption(_ options: NSDictionary, key: String, default defaultValue: Bool) -> Bool { + guard let value = options[key] else { return defaultValue } + if let boolValue = value as? Bool { + return boolValue + } + if let numberValue = value as? NSNumber { + return numberValue.boolValue + } + return defaultValue + } + + private func filePath(from path: String) -> String { + if let url = URL(string: path), url.isFileURL { + return url.path + } + return path.removingPercentEncoding ?? path + } + + private func debugLog(_ message: @autoclosure () -> String) { + #if DEBUG + print(message()) + #endif + } + @objc(scanFromPath:formats:options:resolver:rejecter:) func scanFromPath(_ path: String, formats: [String], options: NSDictionary, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) { - - print("ImageCodeScanner iOS - Starting scan with all preprocessing options enabled") - + + let shouldEnhanceContrast = boolOption(options, key: "enhanceContrast", default: true) + let shouldConvertToGrayscale = boolOption(options, key: "convertToGrayscale", default: true) + let shouldTryRotations = boolOption(options, key: "tryRotations", default: true) + + debugLog("ImageCodeScanner iOS - Starting scan with preprocessing options: enhanceContrast=\(shouldEnhanceContrast), convertToGrayscale=\(shouldConvertToGrayscale), tryRotations=\(shouldTryRotations)") + // Use atomic flag to prevent multiple promise calls var hasResolved = false let resolveQueue = DispatchQueue(label: "ImageCodeScanner.resolve") @@ -140,41 +171,42 @@ class ImageCodeScanner: NSObject, RCTBridgeModule { } } - // Remove file:// prefix if present - let cleanPath = path.replacingOccurrences(of: "file://", with: "") + let cleanPath = filePath(from: path) guard let originalImage = UIImage(contentsOfFile: cleanPath) else { safeReject("INVALID_IMAGE", "Cannot load image from path: \(path)", nil) return } - let baseImage = scaleImageIfNeeded(originalImage, maxDimension: 2048) - // Prepare images to try - always try all preprocessing options + let baseImage = scaleImageIfNeeded(originalImage, maxDimension: maxImageDimension) var imagesToTry: [(String, UIImage)] = [("Original", baseImage)] - // Always add grayscale version - if let grayscaleImage = convertToGrayscale(baseImage) { - imagesToTry.append(("Grayscale", grayscaleImage)) - print("ImageCodeScanner iOS - Added grayscale version") + if shouldConvertToGrayscale { + if let grayscaleImage = convertToGrayscale(baseImage) { + imagesToTry.append(("Grayscale", grayscaleImage)) + debugLog("ImageCodeScanner iOS - Added grayscale version") + } } - // Always add contrast enhanced version - if let contrastImage = enhanceContrast(baseImage) { - imagesToTry.append(("Enhanced contrast", contrastImage)) - print("ImageCodeScanner iOS - Added contrast enhanced version") + if shouldEnhanceContrast { + if let contrastImage = enhanceContrast(baseImage) { + imagesToTry.append(("Enhanced contrast", contrastImage)) + debugLog("ImageCodeScanner iOS - Added contrast enhanced version") + } } - // Always add rotated versions - if let rotated90 = rotateImage(baseImage, degrees: 90) { - imagesToTry.append(("Rotated 90°", rotated90)) - } - if let rotated180 = rotateImage(baseImage, degrees: 180) { - imagesToTry.append(("Rotated 180°", rotated180)) - } - if let rotated270 = rotateImage(baseImage, degrees: 270) { - imagesToTry.append(("Rotated 270°", rotated270)) + if shouldTryRotations { + if let rotated90 = rotateImage(baseImage, degrees: 90) { + imagesToTry.append(("Rotated 90°", rotated90)) + } + if let rotated180 = rotateImage(baseImage, degrees: 180) { + imagesToTry.append(("Rotated 180°", rotated180)) + } + if let rotated270 = rotateImage(baseImage, degrees: 270) { + imagesToTry.append(("Rotated 270°", rotated270)) + } + debugLog("ImageCodeScanner iOS - Added rotated versions") } - print("ImageCodeScanner iOS - Added rotated versions") // Convert formats array to Vision symbologies var symbologies: [VNBarcodeSymbology] = [] @@ -226,13 +258,13 @@ class ImageCodeScanner: NSObject, RCTBridgeModule { func tryScanning(images: [(String, UIImage)], index: Int) { guard index < images.count else { // No more images to try, return empty result - print("ImageCodeScanner iOS - No barcodes found after trying all preprocessing options") + debugLog("ImageCodeScanner iOS - No barcodes found after trying all preprocessing options") safeResolve([]) return } let (description, currentImage) = images[index] - print("ImageCodeScanner iOS - Attempting scan with: \(description)") + debugLog("ImageCodeScanner iOS - Attempting scan with: \(description)") guard let cgImage = currentImage.cgImage else { // Try next image if this one fails @@ -242,9 +274,9 @@ class ImageCodeScanner: NSObject, RCTBridgeModule { // Create Vision request for barcode detection let request = VNDetectBarcodesRequest { request, error in - DispatchQueue.main.async { + DispatchQueue.main.async { [self] in if let error = error { - print("ImageCodeScanner iOS - \(description) failed: \(error.localizedDescription)") + debugLog("ImageCodeScanner iOS - \(description) failed: \(error.localizedDescription)") // Try next image tryScanning(images: images, index: index + 1) return @@ -316,11 +348,11 @@ class ImageCodeScanner: NSObject, RCTBridgeModule { } if barcodeResults.isEmpty { - print("ImageCodeScanner iOS - \(description): No barcodes found") + debugLog("ImageCodeScanner iOS - \(description): No barcodes found") // Try next image tryScanning(images: images, index: index + 1) } else { - print("ImageCodeScanner iOS - Success with \(description)! Found \(barcodeResults.count) codes") + debugLog("ImageCodeScanner iOS - Success with \(description)! Found \(barcodeResults.count) codes") safeResolve(barcodeResults) } } @@ -344,8 +376,8 @@ class ImageCodeScanner: NSObject, RCTBridgeModule { do { try handler.perform([request]) } catch { - DispatchQueue.main.async { - print("ImageCodeScanner iOS - \(description) perform error: \(error.localizedDescription)") + DispatchQueue.main.async { [self] in + debugLog("ImageCodeScanner iOS - \(description) perform error: \(error.localizedDescription)") // Try next image tryScanning(images: images, index: index + 1) } diff --git a/package.json b/package.json index eee9fdc..56558c2 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,9 @@ "scripts": { "test": "jest", "test:coverage": "jest --coverage", - "typecheck": "tsc --noEmit", - "lint": "eslint \"**/*.{js,ts,tsx}\"", - "lint:fix": "eslint \"**/*.{js,ts,tsx}\" --fix", + "typecheck": "tsc --noEmit && yarn workspace react-native-image-code-scanner-example typecheck", + "lint": "eslint \"src/**/*.{ts,tsx}\" \"scripts/**/*.js\" \"example/**/*.{js,ts,tsx}\"", + "lint:fix": "eslint \"src/**/*.{ts,tsx}\" \"scripts/**/*.js\" \"example/**/*.{js,ts,tsx}\" --fix", "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"", "format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,md}\"", "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib node_modules/.cache", @@ -47,7 +47,7 @@ "type": "git", "url": "git+https://github.com/anngth/react-native-image-code-scanner.git" }, - "author": "Heimer Nguyen (https://github.com/anngth)", + "author": "An Nguyen (https://github.com/anngth)", "license": "MIT", "bugs": { "url": "https://github.com/anngth/react-native-image-code-scanner/issues" @@ -57,27 +57,27 @@ "registry": "https://registry.npmjs.org/" }, "devDependencies": { - "@commitlint/config-conventional": "^19.6.0", + "@commitlint/config-conventional": "^19.8.1", "@eslint/compat": "^1.2.7", - "@eslint/eslintrc": "^3.3.0", - "@eslint/js": "^9.22.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "^9.39.4", "@evilmartians/lefthook": "^1.5.0", "@react-native-community/cli": "17.0.1", "@react-native/babel-preset": "0.79.2", - "@react-native/eslint-config": "^0.79.2", + "@react-native/eslint-config": "0.79.2", "@types/jest": "^29.5.5", "@types/react": "^18.3.12", - "commitlint": "^19.6.1", + "commitlint": "^19.8.1", "del-cli": "^5.1.0", - "eslint": "^9.22.0", + "eslint": "^9.39.4", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-prettier": "^5.5.6", "jest": "^29.7.0", - "prettier": "^3.0.3", + "prettier": "^3.9.4", "react": "18.3.1", "react-native": "0.79.2", "react-native-builder-bob": "^0.40.13", - "release-it": "^19.2.4", + "release-it": "^20.2.1", "turbo": "^2.10.4", "typescript": "^5.3.0" }, diff --git a/scripts/validate-package.js b/scripts/validate-package.js index 270838f..4f9dd17 100755 --- a/scripts/validate-package.js +++ b/scripts/validate-package.js @@ -15,7 +15,7 @@ const requiredFiles = [ 'tsconfig.json', 'tsconfig.build.json', '.npmignore', - 'ImageCodeScanner.podspec', + 'RNImageCodeScanner.podspec', // Source files 'src/index.tsx', 'src/NativeImageCodeScanner.ts', diff --git a/src/__tests__/index.test.tsx b/src/__tests__/index.test.tsx index 9c99452..bc588e2 100644 --- a/src/__tests__/index.test.tsx +++ b/src/__tests__/index.test.tsx @@ -75,6 +75,28 @@ describe('ImageCodeScanner', () => { ); }); + it('passes preprocessing options through to native', async () => { + scanFromPathMock.mockResolvedValueOnce([]); + + await ImageCodeScanner.scan({ + path: 'file:///tmp/example.png', + formats: [BarcodeFormat.QR_CODE], + enhanceContrast: false, + convertToGrayscale: false, + tryRotations: false, + }); + + expect(scanFromPathMock).toHaveBeenCalledWith( + 'file:///tmp/example.png', + [BarcodeFormat.QR_CODE], + { + enhanceContrast: false, + convertToGrayscale: false, + tryRotations: false, + } + ); + }); + it('passes UPC_A as the only requested format', async () => { scanFromPathMock.mockResolvedValueOnce([]); diff --git a/src/index.tsx b/src/index.tsx index 413bc25..8f1eee8 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -20,11 +20,20 @@ export enum BarcodeFormat { export interface ScanOptions { path: string; formats?: BarcodeFormat[]; + enhanceContrast?: boolean; + convertToGrayscale?: boolean; + tryRotations?: boolean; } const ImageCodeScannerModule = { scan: (options: ScanOptions): Promise => { - const { path, formats = [BarcodeFormat.QR_CODE] } = options; + const { + path, + formats = [BarcodeFormat.QR_CODE], + enhanceContrast = true, + convertToGrayscale = true, + tryRotations = true, + } = options; if (!path) { return Promise.reject(new Error('Image path is required')); } @@ -33,12 +42,10 @@ const ImageCodeScannerModule = { ? formats : [BarcodeFormat.QR_CODE]; - // Note: Preprocessing is always enabled in native implementation - // The native code automatically tries multiple preprocessing techniques const nativeOptions = { - enhanceContrast: true, - convertToGrayscale: true, - tryRotations: true, + enhanceContrast, + convertToGrayscale, + tryRotations, }; return ImageCodeScanner.scanFromPath( diff --git a/yarn.lock b/yarn.lock index 354b5a8..d265f2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1864,7 +1864,7 @@ __metadata: languageName: node linkType: hard -"@commitlint/config-conventional@npm:^19.6.0": +"@commitlint/config-conventional@npm:^19.8.1": version: 19.8.1 resolution: "@commitlint/config-conventional@npm:19.8.1" dependencies: @@ -2070,14 +2070,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": ^2.1.7 debug: ^4.3.1 - minimatch: ^3.1.2 - checksum: fc5b57803b059f7c1f62950ef83baf045a01887fc00551f9e87ac119246fcc6d71c854a7f678accc79cbf829ed010e8135c755a154b0f54b129c538950cd7e6a + minimatch: ^3.1.5 + checksum: f3d6ba56d6a3dfc5400575011fb4ae5ac189c96b6ca4920adb6da2d084f9eaa28583fa0aa55e123c42baa2bd31f85228ee35a05c8a395b58fb8d976e16482697 languageName: node linkType: hard @@ -2116,20 +2116,20 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^3.3.0, @eslint/eslintrc@npm:^3.3.1": - version: 3.3.3 - resolution: "@eslint/eslintrc@npm:3.3.3" +"@eslint/eslintrc@npm:^3.3.5": + version: 3.3.5 + resolution: "@eslint/eslintrc@npm:3.3.5" dependencies: - ajv: ^6.12.4 + ajv: ^6.14.0 debug: ^4.3.2 espree: ^10.0.1 globals: ^14.0.0 ignore: ^5.2.0 import-fresh: ^3.2.1 js-yaml: ^4.1.1 - minimatch: ^3.1.2 + minimatch: ^3.1.5 strip-json-comments: ^3.1.1 - checksum: d1e16e47f1bb29af32defa597eaf84ac0ff8c06760c0a5f4933c604cd9d931d48c89bed96252222f22abac231898a53bc41385a5e6129257f0060b5ec431bdb2 + checksum: b1c0ac8938891f47a92ef662c790cc599f6562b06562f4035efd075f99c2b62eb4960ee0e2021d424942c8d1084665b581f3799d863c67979b269a8ccda48364 languageName: node linkType: hard @@ -2140,10 +2140,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.39.2, @eslint/js@npm:^9.22.0": - version: 9.39.2 - resolution: "@eslint/js@npm:9.39.2" - checksum: 362aa447266fa5717e762b2b252f177345cb0d7b2954113db9773b3a28898f7cbbc807e07f8078995e6da3f62791f7c5fa2c03517b7170a8e76613cf7fd83c92 +"@eslint/js@npm:9.39.4, @eslint/js@npm:^9.39.4": + version: 9.39.4 + resolution: "@eslint/js@npm:9.39.4" + checksum: 5b1cd1e6c13bc119c92911e6cef7cf886d942c9e047db0c923bbdd539ed6b9820d986b4559be1f2e24836de7fbad95bbfe268b2bf3d1fef76de37bdc8fae19d8 languageName: node linkType: hard @@ -2630,250 +2630,244 @@ __metadata: languageName: node linkType: hard -"@inquirer/ansi@npm:^1.0.2": - version: 1.0.2 - resolution: "@inquirer/ansi@npm:1.0.2" - checksum: d1496e573a63ee6752bcf3fc93375cdabc55b0d60f0588fe7902282c710b223252ad318ff600ee904e48555634663b53fda517f5b29ce9fbda90bfae18592fbc +"@inquirer/ansi@npm:^2.0.7": + version: 2.0.7 + resolution: "@inquirer/ansi@npm:2.0.7" + checksum: ae4ff228412f1f67d78aa9a7410e07e692eeb7c9b75034825f1039898821b02505dfe934be53d6ee4ce5bde9e7ff6b4ce7547bacc1c9aa441396cb9b99717e0f languageName: node linkType: hard -"@inquirer/checkbox@npm:^4.3.2": - version: 4.3.2 - resolution: "@inquirer/checkbox@npm:4.3.2" +"@inquirer/checkbox@npm:^5.1.4": + version: 5.2.1 + resolution: "@inquirer/checkbox@npm:5.2.1" dependencies: - "@inquirer/ansi": ^1.0.2 - "@inquirer/core": ^10.3.2 - "@inquirer/figures": ^1.0.15 - "@inquirer/type": ^3.0.10 - yoctocolors-cjs: ^2.1.3 + "@inquirer/ansi": ^2.0.7 + "@inquirer/core": ^11.2.1 + "@inquirer/figures": ^2.0.7 + "@inquirer/type": ^4.0.7 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: cc632a15a6bab120aecba9dfbdd80b2f6a20875cc6f145918adf5b7a4c77fd778eb6fc620157640992d1c09f70e265a75caf0beb8b4b605adb830d936cbb5287 + checksum: f535895177098d28c9fe32247a213007801dd3cd61022c81b167013dd48e39a077ba2304cae9c3037c171d1fc31dc1c1af10652916f0509522301566b0b1a8f7 languageName: node linkType: hard -"@inquirer/confirm@npm:^5.1.21": - version: 5.1.21 - resolution: "@inquirer/confirm@npm:5.1.21" +"@inquirer/confirm@npm:^6.0.12": + version: 6.1.1 + resolution: "@inquirer/confirm@npm:6.1.1" dependencies: - "@inquirer/core": ^10.3.2 - "@inquirer/type": ^3.0.10 + "@inquirer/core": ^11.2.1 + "@inquirer/type": ^4.0.7 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: a107aa0073965ea510affb9e5b55baf40333503d600970c458c07770cd4e0eee01efc4caba66f0409b0fadc9550d127329622efb543cffcabff3ad0e7f865372 + checksum: 631d35403438949abe073ba6b7823682d4bd995a582226df0c8d18fd2b069a42e415bc40988fd10b138984701d585da32086da6d90814ec50eaa8a2778dc6bfb languageName: node linkType: hard -"@inquirer/core@npm:^10.3.2": - version: 10.3.2 - resolution: "@inquirer/core@npm:10.3.2" +"@inquirer/core@npm:^11.2.1": + version: 11.2.1 + resolution: "@inquirer/core@npm:11.2.1" dependencies: - "@inquirer/ansi": ^1.0.2 - "@inquirer/figures": ^1.0.15 - "@inquirer/type": ^3.0.10 + "@inquirer/ansi": ^2.0.7 + "@inquirer/figures": ^2.0.7 + "@inquirer/type": ^4.0.7 cli-width: ^4.1.0 - mute-stream: ^2.0.0 + fast-wrap-ansi: ^0.2.0 + mute-stream: ^3.0.0 signal-exit: ^4.1.0 - wrap-ansi: ^6.2.0 - yoctocolors-cjs: ^2.1.3 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: ca820e798e02b1d4aff2ad4a8057739abf4140918592ff8ab179f774cdbe51916f24267631e86741a85a48cfa1a08666149785b5e2437ca4b18ef10938486017 + checksum: 1fd6aa9e457756f8390ae085cd4839d66ede87ed1a9f832c07dd79791e8a34bb8a1588e8a9195d2cbbfa1566004c30dc86c48dca51ed173abb304b42e143a34c languageName: node linkType: hard -"@inquirer/editor@npm:^4.2.23": - version: 4.2.23 - resolution: "@inquirer/editor@npm:4.2.23" +"@inquirer/editor@npm:^5.1.1": + version: 5.2.2 + resolution: "@inquirer/editor@npm:5.2.2" dependencies: - "@inquirer/core": ^10.3.2 - "@inquirer/external-editor": ^1.0.3 - "@inquirer/type": ^3.0.10 + "@inquirer/core": ^11.2.1 + "@inquirer/external-editor": ^3.0.3 + "@inquirer/type": ^4.0.7 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 1b533213f89feae3b1ef9fe2b6c2345de812a6b4196462555fcb8f657ee9383341a5ec71c4ea1c61c7ad39738f60622ccea496b29340aa16bd3821860c2b55c0 + checksum: 02cfe29da8741f6e61b767bbd33adcc96784974ae87e567cb742c0dab0f2fd507829a8963ff92c510160c81d6f7765222c4050a011994bfdc09f9f37e798e1bc languageName: node linkType: hard -"@inquirer/expand@npm:^4.0.23": - version: 4.0.23 - resolution: "@inquirer/expand@npm:4.0.23" +"@inquirer/expand@npm:^5.0.13": + version: 5.1.1 + resolution: "@inquirer/expand@npm:5.1.1" dependencies: - "@inquirer/core": ^10.3.2 - "@inquirer/type": ^3.0.10 - yoctocolors-cjs: ^2.1.3 + "@inquirer/core": ^11.2.1 + "@inquirer/type": ^4.0.7 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 73ad1d6376e5efe2a452c33494d6d16ee2670c638ae470a795fdff4acb59a8e032e38e141f87b603b6e96320977519b375dac6471d86d5e3087a9c1db40e3111 + checksum: a93b230bcddb4387a720f12615e8414999427e132110122f6ab9273a684fa0917bea946b35cb4a4e16d9cc7859f78b01e39656e140aed5885bc96212a3668857 languageName: node linkType: hard -"@inquirer/external-editor@npm:^1.0.3": - version: 1.0.3 - resolution: "@inquirer/external-editor@npm:1.0.3" +"@inquirer/external-editor@npm:^3.0.3": + version: 3.0.3 + resolution: "@inquirer/external-editor@npm:3.0.3" dependencies: chardet: ^2.1.1 - iconv-lite: ^0.7.0 + iconv-lite: ^0.7.2 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 9bd7a05247a00408c194648c74046d8a212df1e6b9fe0879b945ebfc35c2524e995e43f7ecd83f14d0bd4e31f985d18819efc31c27810e2c2b838ded7261431f + checksum: d34c457a70215054aadae62442e468049f7c5a5381f0e40b3835a17b0c164130c4a544cee64b4c9ff468982a6cb895250d2b2f0f391f86741f4a59244ab6c1d5 languageName: node linkType: hard -"@inquirer/figures@npm:^1.0.15": - version: 1.0.15 - resolution: "@inquirer/figures@npm:1.0.15" - checksum: bd87a578ab667236cb72bdbb900cb144017dbc306d60e9dc7e665cd7d6b3097e9464cb4d8fe215315083a7820530caf86d7af59e7c41a35a555fb22a881913ad +"@inquirer/figures@npm:^2.0.7": + version: 2.0.7 + resolution: "@inquirer/figures@npm:2.0.7" + checksum: 5b3158e78c141e0188a7680db73d1ef824ab00a8515d9c832682093d5add5d8df4d068d216e679407b03f26cb2a99dbb4ae12ffdba2829bfd7c765e14a4be0d9 languageName: node linkType: hard -"@inquirer/input@npm:^4.3.1": - version: 4.3.1 - resolution: "@inquirer/input@npm:4.3.1" +"@inquirer/input@npm:^5.0.12": + version: 5.1.2 + resolution: "@inquirer/input@npm:5.1.2" dependencies: - "@inquirer/core": ^10.3.2 - "@inquirer/type": ^3.0.10 + "@inquirer/core": ^11.2.1 + "@inquirer/type": ^4.0.7 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 41956840a8b2832db6557d14e80bff2c7baf733bbd6c583b5caf10dbe7f3a11578c1a5478d2fa82f38dd53c81277a0cfaa48e634288730540043d02c80ac4556 + checksum: f0c151a6e1f9a5ecac450a28b18ea06c43f0163cbc5a95c3e0233aaf7d60915c114cfadc886986d23248a7dc7a6b11a52ef4475e196c8a4cdf1ff38755455531 languageName: node linkType: hard -"@inquirer/number@npm:^3.0.23": - version: 3.0.23 - resolution: "@inquirer/number@npm:3.0.23" +"@inquirer/number@npm:^4.0.12": + version: 4.1.1 + resolution: "@inquirer/number@npm:4.1.1" dependencies: - "@inquirer/core": ^10.3.2 - "@inquirer/type": ^3.0.10 + "@inquirer/core": ^11.2.1 + "@inquirer/type": ^4.0.7 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 747db315fce9a95495f50dad38efa7041112caf78bcdfaa62529063dd87b839446acdcf5c8fdf64fc55dd4f80919aa6196813c145ca8e05112723f0cf2312ef7 + checksum: 763e0d3539f4980d214f65b9d39c71c7b645f784ea90ec72886a07e0a05bab7bb643cd25b2dc410c2fe69b11f51949603af963bbf4feed3b671ab7aefc920920 languageName: node linkType: hard -"@inquirer/password@npm:^4.0.23": - version: 4.0.23 - resolution: "@inquirer/password@npm:4.0.23" +"@inquirer/password@npm:^5.0.12": + version: 5.1.1 + resolution: "@inquirer/password@npm:5.1.1" dependencies: - "@inquirer/ansi": ^1.0.2 - "@inquirer/core": ^10.3.2 - "@inquirer/type": ^3.0.10 + "@inquirer/ansi": ^2.0.7 + "@inquirer/core": ^11.2.1 + "@inquirer/type": ^4.0.7 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 97364970b01c85946a4a50ad876c53ef0c1857a9144e24fad65e5dfa4b4e5dd42564fbcdfa2b49bb049a25d127efbe0882cb18afcdd47b166ebd01c6c4b5e825 + checksum: b0b9994de1b49096cc1bf47910b122dd1704cd12434de304081f09090f42d83af699c202e5b8b0c12eb5f87acb7cc4c62a6d97a8781aefe7352b4695eb2b242f languageName: node linkType: hard -"@inquirer/prompts@npm:^7.10.1": - version: 7.10.1 - resolution: "@inquirer/prompts@npm:7.10.1" - dependencies: - "@inquirer/checkbox": ^4.3.2 - "@inquirer/confirm": ^5.1.21 - "@inquirer/editor": ^4.2.23 - "@inquirer/expand": ^4.0.23 - "@inquirer/input": ^4.3.1 - "@inquirer/number": ^3.0.23 - "@inquirer/password": ^4.0.23 - "@inquirer/rawlist": ^4.1.11 - "@inquirer/search": ^3.2.2 - "@inquirer/select": ^4.4.2 +"@inquirer/prompts@npm:8.4.2": + version: 8.4.2 + resolution: "@inquirer/prompts@npm:8.4.2" + dependencies: + "@inquirer/checkbox": ^5.1.4 + "@inquirer/confirm": ^6.0.12 + "@inquirer/editor": ^5.1.1 + "@inquirer/expand": ^5.0.13 + "@inquirer/input": ^5.0.12 + "@inquirer/number": ^4.0.12 + "@inquirer/password": ^5.0.12 + "@inquirer/rawlist": ^5.2.8 + "@inquirer/search": ^4.1.8 + "@inquirer/select": ^5.1.4 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: eaa59a36181406dce10270932c6c33b0e44c8e92ad2d401d1b80f15627a253f36fb9103f5628b86a46d3112c88bd5e24d0a6b38c3f4eb126cee79f9776049a7d + checksum: cde6cc23467a9ca40ca88bce8c5baae3c72e03174c9d61e2ab4e8eb977983eb3948abe2594e0cf85a162ee6ea4495b30cbfffa2a025339ae69c8cbdf2829b508 languageName: node linkType: hard -"@inquirer/rawlist@npm:^4.1.11": - version: 4.1.11 - resolution: "@inquirer/rawlist@npm:4.1.11" +"@inquirer/rawlist@npm:^5.2.8": + version: 5.3.1 + resolution: "@inquirer/rawlist@npm:5.3.1" dependencies: - "@inquirer/core": ^10.3.2 - "@inquirer/type": ^3.0.10 - yoctocolors-cjs: ^2.1.3 + "@inquirer/core": ^11.2.1 + "@inquirer/type": ^4.0.7 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 0d8f6484cfc20749190e95eecfb2d034bafb3644ec4907b84b1673646f5dd71730e38e35565ea98dfd240d8851e3cff653edafcc4e0af617054b127b407e3229 + checksum: ae0cab5a0ea3413d2ae94fff55490f13bc3e8fa3d97e4a6d3fb1386dfc7a560809d6d84ebe7d6e09e8178129d263e122214c86d584b66f78cf34db196be263a6 languageName: node linkType: hard -"@inquirer/search@npm:^3.2.2": - version: 3.2.2 - resolution: "@inquirer/search@npm:3.2.2" +"@inquirer/search@npm:^4.1.8": + version: 4.2.1 + resolution: "@inquirer/search@npm:4.2.1" dependencies: - "@inquirer/core": ^10.3.2 - "@inquirer/figures": ^1.0.15 - "@inquirer/type": ^3.0.10 - yoctocolors-cjs: ^2.1.3 + "@inquirer/core": ^11.2.1 + "@inquirer/figures": ^2.0.7 + "@inquirer/type": ^4.0.7 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 8259262fdd6f438d73721197b0338bc3807c55ce4fb348949240a2ed650d86e58223c6d4869cbf326078711cf952b0e8babb9b328cb35b7058f72a4f1d1a4eee + checksum: 3bfcb6e8bf40e4b6a3ac64f1b53c37f4cea590f23eb7278db805c87d6ff27c5ce0b477789e10a99e24aca48528c3c5c6c764abc966f42bb0bdb6204dfbaf250e languageName: node linkType: hard -"@inquirer/select@npm:^4.4.2": - version: 4.4.2 - resolution: "@inquirer/select@npm:4.4.2" +"@inquirer/select@npm:^5.1.4": + version: 5.2.1 + resolution: "@inquirer/select@npm:5.2.1" dependencies: - "@inquirer/ansi": ^1.0.2 - "@inquirer/core": ^10.3.2 - "@inquirer/figures": ^1.0.15 - "@inquirer/type": ^3.0.10 - yoctocolors-cjs: ^2.1.3 + "@inquirer/ansi": ^2.0.7 + "@inquirer/core": ^11.2.1 + "@inquirer/figures": ^2.0.7 + "@inquirer/type": ^4.0.7 peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 645bb274d71a5a1a913efd4c742f9c76665c17f5cf6b04e0c08dcd925bc86fdbe0d42218b58211cfd6d3749a71020db0fa83257aa0afb7295f859ae2648a31c6 + checksum: c1f19b11df7fc2cdf4234f4d1c393fe56925aad3f2cfb5219b5e2427a1b08e1d22d91d1eb121299303be809527d0ce2dfbcdee7e0a5654a21df38b19d3334564 languageName: node linkType: hard -"@inquirer/type@npm:^3.0.10": - version: 3.0.10 - resolution: "@inquirer/type@npm:3.0.10" +"@inquirer/type@npm:^4.0.7": + version: 4.0.7 + resolution: "@inquirer/type@npm:4.0.7" peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 57d113a9db7abc73326491e29bedc88ef362e53779f9f58a1b61225e0be068ce0c54e33cd65f4a13ca46131676fb72c3ef488463c4c9af0aa89680684c55d74c + checksum: 97769b74264a2575c12c231e3d4acf98b4ae237fc987cec6b459f88b907b1729623403b8d9fe0cf2ca21743114777c64e3031fbeff72e9da37fbf4c8985f587f languageName: node linkType: hard @@ -3285,15 +3279,6 @@ __metadata: languageName: node linkType: hard -"@nodeutils/defaults-deep@npm:1.1.0": - version: 1.1.0 - resolution: "@nodeutils/defaults-deep@npm:1.1.0" - dependencies: - lodash: ^4.15.0 - checksum: 205ff2a4ae2a00c2c640317f888c075bd6429206e26b6c9c99bb691fa05c23274fda2a1023d97fad0ee06ca82232cc058c7c1e06350a37cda5a58fe5aff00c5c - languageName: node - linkType: hard - "@npmcli/agent@npm:^4.0.0": version: 4.0.0 resolution: "@npmcli/agent@npm:4.0.0" @@ -3464,10 +3449,10 @@ __metadata: languageName: node linkType: hard -"@pkgr/core@npm:^0.2.9": - version: 0.2.9 - resolution: "@pkgr/core@npm:0.2.9" - checksum: bb2fb86977d63f836f8f5b09015d74e6af6488f7a411dcd2bfdca79d76b5a681a9112f41c45bdf88a9069f049718efc6f3900d7f1de66a2ec966068308ae517f +"@pkgr/core@npm:^0.3.6": + version: 0.3.6 + resolution: "@pkgr/core@npm:0.3.6" + checksum: 29082aa13d36f13fc41cdc64cb1feb36b630de0d6ebde84e2ff68e3d7a7f1dce4462cca91f76176c46e50bbca6b1e7f1fd9cf907af12d5d70da83bc981ca4ccf languageName: node linkType: hard @@ -3923,13 +3908,13 @@ __metadata: languageName: node linkType: hard -"@react-native/eslint-config@npm:^0.79.2": - version: 0.79.7 - resolution: "@react-native/eslint-config@npm:0.79.7" +"@react-native/eslint-config@npm:0.79.2": + version: 0.79.2 + resolution: "@react-native/eslint-config@npm:0.79.2" dependencies: "@babel/core": ^7.25.2 "@babel/eslint-parser": ^7.25.1 - "@react-native/eslint-plugin": 0.79.7 + "@react-native/eslint-plugin": 0.79.2 "@typescript-eslint/eslint-plugin": ^7.1.1 "@typescript-eslint/parser": ^7.1.1 eslint-config-prettier: ^8.5.0 @@ -3942,14 +3927,14 @@ __metadata: peerDependencies: eslint: ">=8" prettier: ">=2" - checksum: 86362f2a5ad042e94e3398c33aac3b9387a75c037207de36717be2e3c5be5cd6a0dca8b53aa8e874d2e001e2800d12b920f6691fd23221f6bab49fe6a763d821 + checksum: e9f7bbee9eb8eb872a392ef39970c5865c7fa5dba97b7e02eafe2fc9289d7eca7ea052a5043030e20e06b8e288ec93bc3dfc2d2c3caa3942b891db950677f761 languageName: node linkType: hard -"@react-native/eslint-plugin@npm:0.79.7": - version: 0.79.7 - resolution: "@react-native/eslint-plugin@npm:0.79.7" - checksum: e36362b5bee53d0dd21433eff8e42f43f452a06daff025a9d615b54e3790b59187edd1d2c3227287a0ab5155465972ce9f2412fbf8aec0731d0ea7e4ba4cdec5 +"@react-native/eslint-plugin@npm:0.79.2": + version: 0.79.2 + resolution: "@react-native/eslint-plugin@npm:0.79.2" + checksum: bf37892611bfe4e5881d12bbda10e0f0b62d0888273d344f6786cc62151dcf42408cfa36dda30e9d248850e60e3b230054aac4595ea4f412c605c8c9ec5f3db1 languageName: node linkType: hard @@ -4101,13 +4086,6 @@ __metadata: languageName: node linkType: hard -"@tootallnate/quickjs-emscripten@npm:^0.23.0": - version: 0.23.0 - resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" - checksum: c350a2947ffb80b22e14ff35099fd582d1340d65723384a0fd0515e905e2534459ad2f301a43279a37308a27c99273c932e64649abd57d0bb3ca8c557150eccc - languageName: node - linkType: hard - "@turbo/darwin-64@npm:2.10.4": version: 2.10.4 resolution: "@turbo/darwin-64@npm:2.10.4" @@ -4640,6 +4618,13 @@ __metadata: languageName: node linkType: hard +"agent-base@npm:8.0.0": + version: 8.0.0 + resolution: "agent-base@npm:8.0.0" + checksum: a7472a8597191c4541280e6cc20d09b85091b4d3bdc6c4f709d407799c8b3c67d2e10d2d07a70a03fff4601668c16e888b8b24fa68a66c594f2d7ff584074b57 + languageName: node + linkType: hard + "agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": version: 7.1.4 resolution: "agent-base@npm:7.1.4" @@ -4679,6 +4664,18 @@ __metadata: languageName: node linkType: hard +"ajv@npm:^6.14.0": + version: 6.15.0 + resolution: "ajv@npm:6.15.0" + dependencies: + fast-deep-equal: ^3.1.1 + fast-json-stable-stringify: ^2.0.0 + json-schema-traverse: ^0.4.1 + uri-js: ^4.2.2 + checksum: a8e0308f1b44c3dfd1143911353be51bf8aedc2f2bcd595061755ad241c8450a10e4b657af8ba764c5ec9ae2162010f21d5e0d43763e20d782a8171da99b967a + languageName: node + linkType: hard + "ajv@npm:^8.11.0": version: 8.17.1 resolution: "ajv@npm:8.17.1" @@ -5723,7 +5720,7 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^4.3.1": +"ci-info@npm:^4.4.0": version: 4.4.0 resolution: "ci-info@npm:4.4.0" checksum: 3418954c9ca192d4ab7f88637835f8463a327dfcb1d9fdd2434f0aba2715d8b2b0e79fd1a4297cc4a35efc5728f8fd74f3b31cb741c948469a4c07dfe8df3675 @@ -5961,7 +5958,7 @@ __metadata: languageName: node linkType: hard -"commitlint@npm:^19.6.1": +"commitlint@npm:^19.8.1": version: 19.8.1 resolution: "commitlint@npm:19.8.1" dependencies: @@ -6229,10 +6226,10 @@ __metadata: languageName: node linkType: hard -"data-uri-to-buffer@npm:^6.0.2": - version: 6.0.2 - resolution: "data-uri-to-buffer@npm:6.0.2" - checksum: 8b6927c33f9b54037f442856be0aa20e5fd49fa6c9c8ceece408dc306445d593ad72d207d57037c529ce65f413b421da800c6827b1dbefb607b8056f17123a61 +"data-uri-to-buffer@npm:7.0.0": + version: 7.0.0 + resolution: "data-uri-to-buffer@npm:7.0.0" + checksum: 9a02cc9d6ce66f46b76f9d188cd9d9e51b869a5aaaabd2755b246af4463e1ab0e1c4b4651ee0efcdb949b952ce7dec239ec035d8aa954615510f8384395c46e5 languageName: node linkType: hard @@ -6377,13 +6374,13 @@ __metadata: languageName: node linkType: hard -"default-browser@npm:^5.2.1": - version: 5.4.0 - resolution: "default-browser@npm:5.4.0" +"default-browser@npm:^5.4.0": + version: 5.5.0 + resolution: "default-browser@npm:5.5.0" dependencies: bundle-name: ^4.1.0 default-browser-id: ^5.0.0 - checksum: cac0222ca5c9a3387d25337228689652ab33679a6566995c7194a75af7e554e91ec9ac92a70bfaa8e8089eae9f466ae99267bb38601282aade89b200f50a765c + checksum: c5c5d84a4abd82850e98f06798a55dee87fc1064538bea00cc14c0fb2dccccbff5e9e07eeea80385fa653202d5d92509838b4239d610ddfa1c76a04a1f65e767 languageName: node linkType: hard @@ -6449,14 +6446,23 @@ __metadata: languageName: node linkType: hard -"degenerator@npm:^5.0.0": - version: 5.0.1 - resolution: "degenerator@npm:5.0.1" +"defu@npm:^6.1.7": + version: 6.1.7 + resolution: "defu@npm:6.1.7" + checksum: aeca3fc4a1e9f5a99c8b2cd3efa478dbbda1b344e3facfbe750e8b10d8ec48ede08560108f58363e9b8b8687d9ccb68faf0ba24833e267a0f1c0518c89fdabfe + languageName: node + linkType: hard + +"degenerator@npm:6.0.0": + version: 6.0.0 + resolution: "degenerator@npm:6.0.0" dependencies: ast-types: ^0.13.4 escodegen: ^2.1.0 esprima: ^4.0.1 - checksum: a64fa39cdf6c2edd75188157d32338ee9de7193d7dbb2aeb4acb1eb30fa4a15ed80ba8dae9bd4d7b085472cf174a5baf81adb761aaa8e326771392c922084152 + peerDependencies: + quickjs-wasi: ^0.0.1 + checksum: 388c05498ca884be65bd0ca970bbc99e964854eeb7416be75b10ff6da93c297a2a61cb36191e21e2ea1ea4d6fd04f2787382a9ee18df89b4565f8bf5c3b039fb languageName: node linkType: hard @@ -7031,12 +7037,12 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-prettier@npm:^5.2.3": - version: 5.5.5 - resolution: "eslint-plugin-prettier@npm:5.5.5" +"eslint-plugin-prettier@npm:^5.5.6": + version: 5.5.6 + resolution: "eslint-plugin-prettier@npm:5.5.6" dependencies: prettier-linter-helpers: ^1.0.1 - synckit: ^0.11.12 + synckit: ^0.11.13 peerDependencies: "@types/eslint": ">=8.0.0" eslint: ">=8.0.0" @@ -7047,7 +7053,7 @@ __metadata: optional: true eslint-config-prettier: optional: true - checksum: 49b1c25d75ded255a8707d5f06288ae86e8ab4f8e273d4aabdabf73cd0903848916d5a3598ba8be82f2c8dd06769c5e6c172503b3b9cfb2636b6fc23b9c024fb + checksum: 9e2d94d6631af110966733d19aea15699ef684b543de23d1812423b35a02203ac87ef2d8784f236950ecc4026828e98f00e7c2b41879ee9c974a0a0563f1c754 languageName: node linkType: hard @@ -7205,23 +7211,23 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^9.22.0": - version: 9.39.2 - resolution: "eslint@npm:9.39.2" +"eslint@npm:^9.39.4": + version: 9.39.4 + resolution: "eslint@npm:9.39.4" dependencies: "@eslint-community/eslint-utils": ^4.8.0 "@eslint-community/regexpp": ^4.12.1 - "@eslint/config-array": ^0.21.1 + "@eslint/config-array": ^0.21.2 "@eslint/config-helpers": ^0.4.2 "@eslint/core": ^0.17.0 - "@eslint/eslintrc": ^3.3.1 - "@eslint/js": 9.39.2 + "@eslint/eslintrc": ^3.3.5 + "@eslint/js": 9.39.4 "@eslint/plugin-kit": ^0.4.1 "@humanfs/node": ^0.16.6 "@humanwhocodes/module-importer": ^1.0.1 "@humanwhocodes/retry": ^0.4.2 "@types/estree": ^1.0.6 - ajv: ^6.12.4 + ajv: ^6.14.0 chalk: ^4.0.0 cross-spawn: ^7.0.6 debug: ^4.3.2 @@ -7240,7 +7246,7 @@ __metadata: is-glob: ^4.0.0 json-stable-stringify-without-jsonify: ^1.0.1 lodash.merge: ^4.6.2 - minimatch: ^3.1.2 + minimatch: ^3.1.5 natural-compare: ^1.4.0 optionator: ^0.9.3 peerDependencies: @@ -7250,7 +7256,7 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: bfa288fe6b19b6e7f8868e1434d8e469603203d6259e4451b8be4e2172de3172f3b07ed8943ba3904f3545c7c546062c0d656774baa0a10a54483f3907c525e3 + checksum: 474550582ab15ca0863c4624bea1978567434cc907097f0cf12e05fcb18f10e96be408da33c2e0195c037162a8b0f2dbf1bc37622509f6a2e221dcdc52ce68fe languageName: node linkType: hard @@ -7325,10 +7331,10 @@ __metadata: languageName: node linkType: hard -"eta@npm:4.5.0": - version: 4.5.0 - resolution: "eta@npm:4.5.0" - checksum: d0e8d62b793413e6c9a6f95e26170a1ca94ccbe0b7145de0b586572efcdfdc9f52c4b2996b4bb01a5b7ebe85a044d51119adb572d86c8a595b15bcc4112a586e +"eta@npm:4.5.1": + version: 4.5.1 + resolution: "eta@npm:4.5.1" + checksum: c173b78c09daa6a7e9a1c8e00c16f6be1b54863ca49b073187f9452fbb635736dc95ddd0e9d9dd163b69e1ef9b4b5fa159b77cc2794796dfbc5991129c4ee1cc languageName: node linkType: hard @@ -7402,23 +7408,6 @@ __metadata: languageName: node linkType: hard -"execa@npm:^8.0.1": - version: 8.0.1 - resolution: "execa@npm:8.0.1" - dependencies: - cross-spawn: ^7.0.3 - get-stream: ^8.0.1 - human-signals: ^5.0.0 - is-stream: ^3.0.0 - merge-stream: ^2.0.0 - npm-run-path: ^5.1.0 - onetime: ^6.0.0 - signal-exit: ^4.1.0 - strip-final-newline: ^3.0.0 - checksum: cac1bf86589d1d9b73bdc5dda65c52012d1a9619c44c526891956745f7b366ca2603d29fe3f7460bacc2b48c6eab5d6a4f7afe0534b31473d3708d1265545e1f - languageName: node - linkType: hard - "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -7664,6 +7653,22 @@ __metadata: languageName: node linkType: hard +"fast-string-truncated-width@npm:^3.0.2": + version: 3.0.3 + resolution: "fast-string-truncated-width@npm:3.0.3" + checksum: 0243070cb71fde376d4668f6dfbed17bc30a3dec3e6b866aff2c98fade74285d2552cb47bfd859cbf7eae7369b5248ec8c271043457a786e99a86704c1e2db71 + languageName: node + linkType: hard + +"fast-string-width@npm:^3.0.2": + version: 3.0.2 + resolution: "fast-string-width@npm:3.0.2" + dependencies: + fast-string-truncated-width: ^3.0.2 + checksum: 5b9019769f2b00b96d43575c202f4e035a0e55eba7669a9a32351de9fa0805d0959a2afcaec6e4db5ee9b9a4c08d8e77f95abeb04b5bae2f76635cf04ddb4b80 + languageName: node + linkType: hard + "fast-uri@npm:^3.0.1": version: 3.1.2 resolution: "fast-uri@npm:3.1.2" @@ -7671,6 +7676,15 @@ __metadata: languageName: node linkType: hard +"fast-wrap-ansi@npm:^0.2.0": + version: 0.2.2 + resolution: "fast-wrap-ansi@npm:0.2.2" + dependencies: + fast-string-width: ^3.0.2 + checksum: 663978cd61d7bd6f24050d24c9c291e80612b971ec3cc60fa4180d6505f4b2c2f4fbd8e2912aada48e23c98ae3e5010237d63bc70f2ce56ed335357ceb0932e5 + languageName: node + linkType: hard + "fast-xml-parser@npm:^4.4.1": version: 4.5.5 resolution: "fast-xml-parser@npm:4.5.5" @@ -8147,13 +8161,6 @@ __metadata: languageName: node linkType: hard -"get-stream@npm:^8.0.1": - version: 8.0.1 - resolution: "get-stream@npm:8.0.1" - checksum: 01e3d3cf29e1393f05f44d2f00445c5f9ec3d1c49e8179b31795484b9c117f4c695e5e07b88b50785d5c8248a788c85d9913a79266fc77e3ef11f78f10f1b974 - languageName: node - linkType: hard - "get-symbol-description@npm:^1.1.0": version: 1.1.0 resolution: "get-symbol-description@npm:1.1.0" @@ -8165,14 +8172,14 @@ __metadata: languageName: node linkType: hard -"get-uri@npm:^6.0.1": - version: 6.0.5 - resolution: "get-uri@npm:6.0.5" +"get-uri@npm:7.0.0": + version: 7.0.0 + resolution: "get-uri@npm:7.0.0" dependencies: basic-ftp: ^5.0.2 - data-uri-to-buffer: ^6.0.2 + data-uri-to-buffer: 7.0.0 debug: ^4.3.4 - checksum: aef94dbecde44bc9cd23f5c1b6af5bf772a3d16612c0fc37d3a4056ffd202f2cdd329746d4fdc2124813ea6c8b1c5279f3749d27226a2b161df43dbcb70082e3 + checksum: d60ec7a28b05f78297ac89344e1a6e5bc78bcb6c594bb4eb7c1bc3ebc714b6e9cfdf3273f7e8bb110dc365910321b113c6414e332f50cd1a6bf48daf55e74b9b languageName: node linkType: hard @@ -8562,7 +8569,17 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.1": +"http-proxy-agent@npm:8.0.0": + version: 8.0.0 + resolution: "http-proxy-agent@npm:8.0.0" + dependencies: + agent-base: 8.0.0 + debug: ^4.3.4 + checksum: ad68856d85c58b654cbe238dc00dde177b5085271e2ff174be65c5b58f6f4e782199683771e8c6b697127620da5dda6643008fd508627414950261a482416347 + languageName: node + linkType: hard + +"http-proxy-agent@npm:^7.0.0": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" dependencies: @@ -8572,7 +8589,17 @@ __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:8.0.0": + version: 8.0.0 + resolution: "https-proxy-agent@npm:8.0.0" + dependencies: + agent-base: 8.0.0 + debug: ^4.3.4 + checksum: 432cee2f319108111c00f05c35d16c8f2c57985efb3ee189332f0dc18f8ec660a550033c0c93222bf7cf60d0e01b87becffffd020da75865993279e9a811c804 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.5": version: 7.0.6 resolution: "https-proxy-agent@npm:7.0.6" dependencies: @@ -8596,13 +8623,6 @@ __metadata: languageName: node linkType: hard -"human-signals@npm:^5.0.0": - version: 5.0.0 - resolution: "human-signals@npm:5.0.0" - checksum: 6504560d5ed91444f16bea3bd9dfc66110a339442084e56c3e7fa7bbdf3f406426d6563d662bdce67064b165eac31eeabfc0857ed170aaa612cf14ec9f9a464c - languageName: node - linkType: hard - "iconv-lite@npm:^0.6.2": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" @@ -8612,12 +8632,12 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:^0.7.0": - version: 0.7.2 - resolution: "iconv-lite@npm:0.7.2" +"iconv-lite@npm:^0.7.2": + version: 0.7.3 + resolution: "iconv-lite@npm:0.7.3" dependencies: safer-buffer: ">= 2.1.2 < 3.0.0" - checksum: faf884c1f631a5d676e3e64054bed891c7c5f616b790082d99ccfbfd017c661a39db8009160268fd65fae57c9154d4d491ebc9c301f3446a078460ef114dc4b8 + checksum: 36b3dc2f5a25c2cac15f7df42a1f23e796f1616b2b4103b205a5c237d9b40483184a8c07bbc2d72b01ca28f4770506fad5a43203adae9c97a5d4733af684b6a8 languageName: node linkType: hard @@ -8746,26 +8766,6 @@ __metadata: languageName: node linkType: hard -"inquirer@npm:12.11.1": - version: 12.11.1 - resolution: "inquirer@npm:12.11.1" - dependencies: - "@inquirer/ansi": ^1.0.2 - "@inquirer/core": ^10.3.2 - "@inquirer/prompts": ^7.10.1 - "@inquirer/type": ^3.0.10 - mute-stream: ^2.0.0 - run-async: ^4.0.6 - rxjs: ^7.8.2 - peerDependencies: - "@types/node": ">=18" - peerDependenciesMeta: - "@types/node": - optional: true - checksum: 27dce4cd77d240cd63391be1d2a355a5d809798c10095bcee0b8a52a0a3422916befa22b0bfe81f5cb812d19da0df3f6cfbdbdef577cb5156f9ca96d9a2f76f5 - languageName: node - linkType: hard - "internal-ip@npm:^4.3.0": version: 4.3.0 resolution: "internal-ip@npm:4.3.0" @@ -9025,6 +9025,13 @@ __metadata: languageName: node linkType: hard +"is-in-ssh@npm:^1.0.0": + version: 1.0.0 + resolution: "is-in-ssh@npm:1.0.0" + checksum: d55cb39afdbca0cdc94cd493da7819c00d35048ea04fc1624aabde6e0c86aa6b91ddb38b2baf73c4b5d53cc8fbf1a8dfbf2e315594a808474b751ffb6b0d3e58 + languageName: node + linkType: hard + "is-inside-container@npm:^1.0.0": version: 1.0.0 resolution: "is-inside-container@npm:1.0.0" @@ -9192,13 +9199,6 @@ __metadata: languageName: node linkType: hard -"is-stream@npm:^3.0.0": - version: 3.0.0 - resolution: "is-stream@npm:3.0.0" - checksum: 172093fe99119ffd07611ab6d1bcccfe8bc4aa80d864b15f43e63e54b7abc71e779acd69afdb854c4e2a67fdc16ae710e370eda40088d1cfc956a50ed82d8f16 - languageName: node - linkType: hard - "is-string@npm:^1.1.1": version: 1.1.1 resolution: "is-string@npm:1.1.1" @@ -10439,7 +10439,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.15.0, lodash@npm:^4.17.21": +"lodash@npm:^4.17.21": version: 4.18.1 resolution: "lodash@npm:4.18.1" checksum: bb5f5b49aad29614e709af02b64c56b0f8b78c6a81434a3c1ae527d2f0f78ca08f9d9fb22aa825a053876c9d2166e9c01f31c356014b5e2bdc0556c057433102 @@ -10538,10 +10538,10 @@ __metadata: languageName: node linkType: hard -"macos-release@npm:^3.3.0": - version: 3.4.0 - resolution: "macos-release@npm:3.4.0" - checksum: f4c0cb8b3f93b05d73c502b4bbe2b811c44facfc9bd072c13a30ff2a8ba1cad5d9de517d10be8b31e2b917643245a81587a2eec8300e66a7364419d11402ab02 +"macos-release@npm:^3.4.0": + version: 3.5.1 + resolution: "macos-release@npm:3.5.1" + checksum: 4fd26736f230abcc4b5d2a10f7dfcd62a5b4be6353578129fa12519ff2fbd16dfc7fe4037f3cb8f5f038aa1aa3388aa7a97f1c49988258fccfb9087ead74b57a languageName: node linkType: hard @@ -11220,13 +11220,6 @@ __metadata: languageName: node linkType: hard -"mimic-fn@npm:^4.0.0": - version: 4.0.0 - resolution: "mimic-fn@npm:4.0.0" - checksum: 995dcece15ee29aa16e188de6633d43a3db4611bcf93620e7e62109ec41c79c0f34277165b8ce5e361205049766e371851264c21ac64ca35499acb5421c2ba56 - languageName: node - linkType: hard - "mimic-function@npm:^5.0.0": version: 5.0.1 resolution: "mimic-function@npm:5.0.1" @@ -11243,7 +11236,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.2, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": +"minimatch@npm:^3.0.2, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, 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: @@ -11406,10 +11399,10 @@ __metadata: languageName: node linkType: hard -"mute-stream@npm:^2.0.0": - version: 2.0.0 - resolution: "mute-stream@npm:2.0.0" - checksum: d2e4fd2f5aa342b89b98134a8d899d8ef9b0a6d69274c4af9df46faa2d97aeb1f2ce83d867880d6de63643c52386579b99139801e24e7526c3b9b0a6d1e18d6c +"mute-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "mute-stream@npm:3.0.0" + checksum: bee5db5c996a4585dbffc49e51fea10f3582d7f65441db9bc63126f16269541713c6ccb5a6fe37e08f627967b6eb28dd6b35e54a8dce53cf3837d7e010917b43 languageName: node linkType: hard @@ -11643,15 +11636,6 @@ __metadata: languageName: node linkType: hard -"npm-run-path@npm:^5.1.0": - version: 5.3.0 - resolution: "npm-run-path@npm:5.3.0" - dependencies: - path-key: ^4.0.0 - checksum: ae8e7a89da9594fb9c308f6555c73f618152340dcaae423e5fb3620026fefbec463618a8b761920382d666fa7a2d8d240b6fe320e8a6cdd54dc3687e2b659d25 - languageName: node - linkType: hard - "nullthrows@npm:^1.1.1": version: 1.1.1 resolution: "nullthrows@npm:1.1.1" @@ -11820,15 +11804,6 @@ __metadata: languageName: node linkType: hard -"onetime@npm:^6.0.0": - version: 6.0.0 - resolution: "onetime@npm:6.0.0" - dependencies: - mimic-fn: ^4.0.0 - checksum: 0846ce78e440841335d4e9182ef69d5762e9f38aa7499b19f42ea1c4cd40f0b4446094c455c713f9adac3f4ae86f613bb5e30c99e52652764d06a89f709b3788 - languageName: node - linkType: hard - "onetime@npm:^7.0.0": version: 7.0.0 resolution: "onetime@npm:7.0.0" @@ -11838,15 +11813,17 @@ __metadata: languageName: node linkType: hard -"open@npm:10.2.0": - version: 10.2.0 - resolution: "open@npm:10.2.0" +"open@npm:11.0.0": + version: 11.0.0 + resolution: "open@npm:11.0.0" dependencies: - default-browser: ^5.2.1 + default-browser: ^5.4.0 define-lazy-prop: ^3.0.0 + is-in-ssh: ^1.0.0 is-inside-container: ^1.0.0 - wsl-utils: ^0.1.0 - checksum: 64e2e1fb1dc5ab82af06c990467237b8fd349b1b9ecc6324d12df337a005d039cec11f758abea148be68878ccd616977005682c48ef3c5c7ba48bd3e5d6a3dbb + powershell-utils: ^0.1.0 + wsl-utils: ^0.3.0 + checksum: b13429859acd635ddb66408479ef980ee150eab20eaf82131962710ce3aa6612278d33c645f82d7565499ef9d031812f1606eb293cdcc1c2d3beeee07cdd2e7c languageName: node linkType: hard @@ -11894,9 +11871,9 @@ __metadata: languageName: node linkType: hard -"ora@npm:9.0.0": - version: 9.0.0 - resolution: "ora@npm:9.0.0" +"ora@npm:9.3.0": + version: 9.3.0 + resolution: "ora@npm:9.3.0" dependencies: chalk: ^5.6.2 cli-cursor: ^5.0.0 @@ -11904,10 +11881,9 @@ __metadata: is-interactive: ^2.0.0 is-unicode-supported: ^2.1.0 log-symbols: ^7.0.1 - stdin-discarder: ^0.2.2 + stdin-discarder: ^0.3.1 string-width: ^8.1.0 - strip-ansi: ^7.1.2 - checksum: 4efc9c3caa45b552bae4c9755c586eb4f39b824e456d77ec380539529439ca95f3acf9626119131f1ca76618c176088996023e257f5b82e1b86a509990f4545a + checksum: 7fb859fb31f65ecac9116947592db9e326b5d878c0bb114df5a60bb396a2914a577b7bf136fda8ff08be7930a03f82d74b6fd8e326f2de2a4a99afd5e589724f languageName: node linkType: hard @@ -11942,13 +11918,13 @@ __metadata: languageName: node linkType: hard -"os-name@npm:6.1.0": - version: 6.1.0 - resolution: "os-name@npm:6.1.0" +"os-name@npm:7.0.0": + version: 7.0.0 + resolution: "os-name@npm:7.0.0" dependencies: - macos-release: ^3.3.0 - windows-release: ^6.1.0 - checksum: d69a2060bea01dc502bd9a08802f43bebce85e95adde7740d0629a8522c16a92c05e0ee052819cac49f82aa61324ff038a3b79e015e26f122bbc08b40aa4ead3 + macos-release: ^3.4.0 + windows-release: ^7.1.0 + checksum: d4f258da5e0726d7745f8900ae263913352e162d4867da5b596ec8acfab3cc6fabcfce7ed9e816be5ced9ddb4c8b12e1c7015dd160dbf30030495f2d2133f53c languageName: node linkType: hard @@ -12065,29 +12041,31 @@ __metadata: languageName: node linkType: hard -"pac-proxy-agent@npm:^7.1.0": - version: 7.2.0 - resolution: "pac-proxy-agent@npm:7.2.0" +"pac-proxy-agent@npm:8.0.0": + version: 8.0.0 + resolution: "pac-proxy-agent@npm:8.0.0" dependencies: - "@tootallnate/quickjs-emscripten": ^0.23.0 - agent-base: ^7.1.2 + agent-base: 8.0.0 debug: ^4.3.4 - get-uri: ^6.0.1 - http-proxy-agent: ^7.0.0 - https-proxy-agent: ^7.0.6 - pac-resolver: ^7.0.1 - socks-proxy-agent: ^8.0.5 - checksum: 099c1bc8944da6a98e8b7de1fbf23e4014bc3063f66a7c29478bd852c1162e1d086a4f80f874f40961ebd5c516e736aed25852db97b79360cbdcc9db38086981 + get-uri: 7.0.0 + http-proxy-agent: 8.0.0 + https-proxy-agent: 8.0.0 + pac-resolver: 8.0.0 + quickjs-wasi: ^0.0.1 + socks-proxy-agent: 9.0.0 + checksum: ab606a2059683678fbbaf79f267b2f87f93d80b99c61cb796b779289340733c028c50376bc7428bd3313a88cc17e4e41f8346d9cd2236ac3222fb66c08bd7946 languageName: node linkType: hard -"pac-resolver@npm:^7.0.1": - version: 7.0.1 - resolution: "pac-resolver@npm:7.0.1" +"pac-resolver@npm:8.0.0": + version: 8.0.0 + resolution: "pac-resolver@npm:8.0.0" dependencies: - degenerator: ^5.0.0 + degenerator: 6.0.0 netmask: ^2.0.2 - checksum: 839134328781b80d49f9684eae1f5c74f50a1d4482076d44c84fc2f3ca93da66fa11245a4725a057231e06b311c20c989fd0681e662a0792d17f644d8fe62a5e + peerDependencies: + quickjs-wasi: ^0.0.1 + checksum: e6c00f9a3da30bd4f5d13590b7fbcb9b39acd94488cae07e9fee10d10809d7876ed29a8a712a8f38cd170721fc6b4d5bc41bb9210f756c3f7b71ff448f7fdb78 languageName: node linkType: hard @@ -12206,13 +12184,6 @@ __metadata: languageName: node linkType: hard -"path-key@npm:^4.0.0": - version: 4.0.0 - resolution: "path-key@npm:4.0.0" - checksum: 8e6c314ae6d16b83e93032c61020129f6f4484590a777eed709c4a01b50e498822b00f76ceaf94bc64dbd90b327df56ceadce27da3d83393790f1219e07721d7 - languageName: node - linkType: hard - "path-parse@npm:^1.0.5, path-parse@npm:^1.0.7": version: 1.0.7 resolution: "path-parse@npm:1.0.7" @@ -12368,6 +12339,20 @@ __metadata: languageName: node linkType: hard +"powershell-utils@npm:^0.1.0": + version: 0.1.0 + resolution: "powershell-utils@npm:0.1.0" + checksum: 778f84f00e1c37513dfdeff25325813655eb636071ba8822405be7e35caa042ac87f9fbefb5ce6abea84580528b39d083041f56b546f2c8c6d7fceac899283a5 + languageName: node + linkType: hard + +"powershell-utils@npm:^0.2.0": + version: 0.2.0 + resolution: "powershell-utils@npm:0.2.0" + checksum: 49aa144effed21a03c095d0825455e44051110006fd05cd0e41431e1f09d0e111a62fd14d6d017113cca7ac3a69d37cd5b2ae8f1c4ba2685d5562158ff66836f + languageName: node + linkType: hard + "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -12384,12 +12369,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^3.0.3": - version: 3.8.0 - resolution: "prettier@npm:3.8.0" +"prettier@npm:^3.9.4": + version: 3.9.4 + resolution: "prettier@npm:3.9.4" bin: prettier: bin/prettier.cjs - checksum: 50770d842539d5fa208bd84ecfb28ae367258b14a5e7e4b9472ba087ed9f8888a5bfd387bfd5596473529f04347670f9b1aa6f0bd8631bec1644e6a8e47c7d35 + checksum: 96b0125a8af78e1e3c2c3f916a768934a542d5fa64bf1819cbef5c9cb081b793ebe99eb202f98e230741595be9d14d73819380bd91988a7df422d704a97bb38c languageName: node linkType: hard @@ -12500,19 +12485,19 @@ __metadata: languageName: node linkType: hard -"proxy-agent@npm:6.5.0": - version: 6.5.0 - resolution: "proxy-agent@npm:6.5.0" +"proxy-agent@npm:7.0.0": + version: 7.0.0 + resolution: "proxy-agent@npm:7.0.0" dependencies: - agent-base: ^7.1.2 + agent-base: 8.0.0 debug: ^4.3.4 - http-proxy-agent: ^7.0.1 - https-proxy-agent: ^7.0.6 + http-proxy-agent: 8.0.0 + https-proxy-agent: 8.0.0 lru-cache: ^7.14.1 - pac-proxy-agent: ^7.1.0 + pac-proxy-agent: 8.0.0 proxy-from-env: ^1.1.0 - socks-proxy-agent: ^8.0.5 - checksum: d03ad2d171c2768280ade7ea6a7c5b1d0746215d70c0a16e02780c26e1d347edd27b3f48374661ae54ec0f7b41e6e45175b687baf333b36b1fd109a525154806 + socks-proxy-agent: 9.0.0 + checksum: fe3d23fe41518e4c64f2ba0d340c82cf2a4510018f35490a44c055326ddb6746977ba2799e8b299d537e711979ff0b504c01b26aec08ffaa4ffcc9a5fcab4c12 languageName: node linkType: hard @@ -12588,6 +12573,13 @@ __metadata: languageName: node linkType: hard +"quickjs-wasi@npm:^0.0.1": + version: 0.0.1 + resolution: "quickjs-wasi@npm:0.0.1" + checksum: 368cf93c44da4f5f15236e1a54775caae923a78135cc25b9d6d9fa0ddfb30a48fe385dc2e9efa2071dc7046418b6a9dc5c96f5bbbef7dbab4ee71b88e9b6c51d + languageName: node + linkType: hard + "range-parser@npm:~1.2.1": version: 1.2.1 resolution: "range-parser@npm:1.2.1" @@ -12725,27 +12717,27 @@ __metadata: version: 0.0.0-use.local resolution: "react-native-image-code-scanner@workspace:." dependencies: - "@commitlint/config-conventional": ^19.6.0 + "@commitlint/config-conventional": ^19.8.1 "@eslint/compat": ^1.2.7 - "@eslint/eslintrc": ^3.3.0 - "@eslint/js": ^9.22.0 + "@eslint/eslintrc": ^3.3.5 + "@eslint/js": ^9.39.4 "@evilmartians/lefthook": ^1.5.0 "@react-native-community/cli": 17.0.1 "@react-native/babel-preset": 0.79.2 - "@react-native/eslint-config": ^0.79.2 + "@react-native/eslint-config": 0.79.2 "@types/jest": ^29.5.5 "@types/react": ^18.3.12 - commitlint: ^19.6.1 + commitlint: ^19.8.1 del-cli: ^5.1.0 - eslint: ^9.22.0 + eslint: ^9.39.4 eslint-config-prettier: ^9.1.0 - eslint-plugin-prettier: ^5.2.3 + eslint-plugin-prettier: ^5.5.6 jest: ^29.7.0 - prettier: ^3.0.3 + prettier: ^3.9.4 react: 18.3.1 react-native: 0.79.2 react-native-builder-bob: ^0.40.13 - release-it: ^19.2.4 + release-it: ^20.2.1 turbo: ^2.10.4 typescript: ^5.3.0 peerDependencies: @@ -13041,36 +13033,36 @@ __metadata: languageName: node linkType: hard -"release-it@npm:^19.2.4": - version: 19.2.4 - resolution: "release-it@npm:19.2.4" +"release-it@npm:^20.2.1": + version: 20.2.1 + resolution: "release-it@npm:20.2.1" dependencies: - "@nodeutils/defaults-deep": 1.1.0 + "@inquirer/prompts": 8.4.2 "@octokit/rest": 22.0.1 "@phun-ky/typeof": 2.0.3 async-retry: 1.3.3 c12: 3.3.3 - ci-info: ^4.3.1 - eta: 4.5.0 + ci-info: ^4.4.0 + defu: ^6.1.7 + eta: 4.5.1 git-url-parse: 16.1.0 - inquirer: 12.11.1 issue-parser: 7.0.1 lodash.merge: 4.6.2 mime-types: 3.0.2 new-github-release-url: 2.0.0 - open: 10.2.0 - ora: 9.0.0 - os-name: 6.1.0 - proxy-agent: 6.5.0 - semver: 7.7.3 + open: 11.0.0 + ora: 9.3.0 + os-name: 7.0.0 + proxy-agent: 7.0.0 + semver: 7.7.4 tinyglobby: 0.2.15 - undici: 6.23.0 + undici: 7.28.0 url-join: 5.0.0 wildcard-match: 5.1.4 - yargs-parser: 21.1.1 + yargs-parser: 22.0.0 bin: release-it: bin/release-it.js - checksum: d225b20e99c20b183261e0ca19b8c8de9e631f87f4a5e35a5cbd2683f6b9d4ad332c34f3f412728a6fdd74b0644fc8566ce5b449718b333f196de686d267cde5 + checksum: 2e61afa453fcb7c011d8d27e641e9ba7e26c88e515680ec9ee8d76d261fa4821ea906141e81aa31c06eb592abb71f48b1acf6f8b6786244fe7bb28af2ca0c109 languageName: node linkType: hard @@ -13307,13 +13299,6 @@ __metadata: languageName: node linkType: hard -"run-async@npm:^4.0.6": - version: 4.0.6 - resolution: "run-async@npm:4.0.6" - checksum: 1338a046d4f4ea03a62dfcb426d44af8c9991221ec74983e52845cbb7ee0c685dc0e9e07cbb6958ee6a1103b7a66c0204b86e110e37909965a92e6fbb7b3b837 - languageName: node - linkType: hard - "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -13323,15 +13308,6 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.8.2": - version: 7.8.2 - resolution: "rxjs@npm:7.8.2" - dependencies: - tslib: ^2.1.0 - checksum: 2f233d7c832a6c255dabe0759014d7d9b1c9f1cb2f2f0d59690fd11c883c9826ea35a51740c06ab45b6ade0d9087bde9192f165cba20b6730d344b831ef80744 - languageName: node - linkType: hard - "safe-array-concat@npm:^1.1.3": version: 1.1.3 resolution: "safe-array-concat@npm:1.1.3" @@ -13413,12 +13389,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.7.3, semver@npm:^7.1.3, semver@npm:^7.3.4, 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": - version: 7.7.3 - resolution: "semver@npm:7.7.3" +"semver@npm:7.7.4": + version: 7.7.4 + resolution: "semver@npm:7.7.4" bin: semver: bin/semver.js - checksum: f013a3ee4607857bcd3503b6ac1d80165f7f8ea94f5d55e2d3e33df82fce487aa3313b987abf9b39e0793c83c9fc67b76c36c067625141a9f6f704ae0ea18db2 + checksum: 9b4a6a58e98b9723fafcafa393c9d4e8edefaa60b8dfbe39e30892a3604cf1f45f52df9cfb1ae1a22b44c8b3d57fec8a9bb7b3e1645431587cb272399ede152e languageName: node linkType: hard @@ -13440,6 +13416,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.1.3, semver@npm:^7.3.4, 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": + version: 7.7.3 + resolution: "semver@npm:7.7.3" + bin: + semver: bin/semver.js + checksum: f013a3ee4607857bcd3503b6ac1d80165f7f8ea94f5d55e2d3e33df82fce487aa3313b987abf9b39e0793c83c9fc67b76c36c067625141a9f6f704ae0ea18db2 + languageName: node + linkType: hard + "send@npm:^0.19.0, send@npm:~0.19.1": version: 0.19.2 resolution: "send@npm:0.19.2" @@ -13705,7 +13690,18 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^8.0.3, socks-proxy-agent@npm:^8.0.5": +"socks-proxy-agent@npm:9.0.0": + version: 9.0.0 + resolution: "socks-proxy-agent@npm:9.0.0" + dependencies: + agent-base: 8.0.0 + debug: ^4.3.4 + socks: ^2.8.3 + checksum: 7d48fdee573826a048bcfc4e29bb5315febc6f52cc84aa083a2d4134649e265c9fa2cd117bf846dd534663b9ad2238f93c05306cea626b0a74366ecbcf74c5b4 + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^8.0.3": version: 8.0.5 resolution: "socks-proxy-agent@npm:8.0.5" dependencies: @@ -13872,10 +13868,10 @@ __metadata: languageName: node linkType: hard -"stdin-discarder@npm:^0.2.2": - version: 0.2.2 - resolution: "stdin-discarder@npm:0.2.2" - checksum: 642ffd05bd5b100819d6b24a613d83c6e3857c6de74eb02fc51506fa61dc1b0034665163831873868157c4538d71e31762bcf319be86cea04c3aba5336470478 +"stdin-discarder@npm:^0.3.1": + version: 0.3.2 + resolution: "stdin-discarder@npm:0.3.2" + checksum: 07b3edbe4fb26f6d744e730069f6a5f26b0b2d1638bfc495529025077e867982a28f3f6862f0ecd10c9fe70a2dc19ca60d39b89f5cf53c28d5791a379f348481 languageName: node linkType: hard @@ -14080,13 +14076,6 @@ __metadata: languageName: node linkType: hard -"strip-final-newline@npm:^3.0.0": - version: 3.0.0 - resolution: "strip-final-newline@npm:3.0.0" - checksum: 23ee263adfa2070cd0f23d1ac14e2ed2f000c9b44229aec9c799f1367ec001478469560abefd00c5c99ee6f0b31c137d53ec6029c53e9f32a93804e18c201050 - languageName: node - linkType: hard - "strip-indent@npm:^4.0.0": version: 4.1.1 resolution: "strip-indent@npm:4.1.1" @@ -14184,12 +14173,12 @@ __metadata: languageName: node linkType: hard -"synckit@npm:^0.11.12": - version: 0.11.12 - resolution: "synckit@npm:0.11.12" +"synckit@npm:^0.11.13": + version: 0.11.13 + resolution: "synckit@npm:0.11.13" dependencies: - "@pkgr/core": ^0.2.9 - checksum: a53fb563d01ba8912a111b883fc3c701e267896ff8273e7aba9001f5f74711e125888f4039e93060795cd416122cf492ae419eb10a6a3e3b00e830917669d2cf + "@pkgr/core": ^0.3.6 + checksum: ec989ed45f3df2e7eb3141f00060669fbc6cac5649846d0579f336cee4ab047c8bac65baa75b4df991e1c1bd224675b10efbe69af3596ead5180ec81a6d4ec06 languageName: node linkType: hard @@ -14407,7 +14396,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.1, tslib@npm:^2.1.0": +"tslib@npm:^2.0.1": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: e4aba30e632b8c8902b47587fd13345e2827fa639e7c3121074d5ee0880723282411a8838f830b55100cbe4517672f84a2472667d355b81e8af165a55dc6203a @@ -14630,7 +14619,14 @@ __metadata: languageName: node linkType: hard -"undici@npm:6.23.0, undici@npm:^6.18.2": +"undici@npm:7.28.0": + version: 7.28.0 + resolution: "undici@npm:7.28.0" + checksum: 57c77c4ee75e166ef361ad238fcd88816fbc0d058aeadf4c0f689194bd17eb6d63ce25b89bd4f4d65a380ebbde784bf4886ec51acc9f8c826ecab01d8f63aaac + languageName: node + linkType: hard + +"undici@npm:^6.18.2": version: 6.23.0 resolution: "undici@npm:6.23.0" checksum: f0953920330375e76d1614381af07da9d7c21ad3244d0785b3f7bd4072635c20a1f432ef3a129baa3e4a92278ce32e9ea2ca8b5f0e0554a5739222af332c08fe @@ -15034,12 +15030,12 @@ __metadata: languageName: node linkType: hard -"windows-release@npm:^6.1.0": - version: 6.1.0 - resolution: "windows-release@npm:6.1.0" +"windows-release@npm:^7.1.0": + version: 7.1.1 + resolution: "windows-release@npm:7.1.1" dependencies: - execa: ^8.0.1 - checksum: 2af39c94d5e4e250c3239e70177f3a97291c505e364b85a7ae63ca9d06c91496e8bd3a75c55e03184d9c27e58c0a0fa21a4a8457ac72cc560d8796a75f12d0a3 + powershell-utils: ^0.2.0 + checksum: 169a862dcedd87f9c490379dab927fd5d1db9bba0f65a054721d841505bda6f90fb6232a2e1d5bdb4e60c1ef370d728c2d2e73a00950bdb86ee802f5796cad4d languageName: node linkType: hard @@ -15157,12 +15153,13 @@ __metadata: languageName: node linkType: hard -"wsl-utils@npm:^0.1.0": - version: 0.1.0 - resolution: "wsl-utils@npm:0.1.0" +"wsl-utils@npm:^0.3.0": + version: 0.3.1 + resolution: "wsl-utils@npm:0.3.1" dependencies: is-wsl: ^3.1.0 - checksum: de4c92187e04c3c27b4478f410a02e81c351dc85efa3447bf1666f34fc80baacd890a6698ec91995631714086992036013286aea3d77e6974020d40a08e00aec + powershell-utils: ^0.1.0 + checksum: 46800b92fa4974f2a846a93f0b8c409a455c35897d001a7599b5524766b603c8fb0945d2b21ad6ad27d4b0ae7e72ca2e58d832ccfcaabf659399921c6448b1d0 languageName: node linkType: hard @@ -15251,10 +15248,10 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:21.1.1, yargs-parser@npm:^21.1.1": - version: 21.1.1 - resolution: "yargs-parser@npm:21.1.1" - checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c +"yargs-parser@npm:22.0.0": + version: 22.0.0 + resolution: "yargs-parser@npm:22.0.0" + checksum: 55df0d94f3f9f933f1349f244ddf72a6978a9d5a972b69332965cdfd5ec849ff26386965512f4179065b0573cc6e8df33ca44334958a892c47fedae08a967c99 languageName: node linkType: hard @@ -15275,6 +15272,13 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c + languageName: node + linkType: hard + "yargs@npm:^15.1.0": version: 15.4.1 resolution: "yargs@npm:15.4.1" @@ -15323,13 +15327,6 @@ __metadata: languageName: node linkType: hard -"yoctocolors-cjs@npm:^2.1.3": - version: 2.1.3 - resolution: "yoctocolors-cjs@npm:2.1.3" - checksum: 207df586996c3b604fa85903f81cc54676f1f372613a0c7247f0d24b1ca781905685075d06955211c4d5d4f629d7d5628464f8af0a42d286b7a8ff88e9dadcb8 - languageName: node - linkType: hard - "yoctocolors@npm:^2.1.1": version: 2.1.2 resolution: "yoctocolors@npm:2.1.2"