diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a61272a..46187d9 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -12,4 +12,7 @@ ## Test Plan - + + +- [ ] Ran local JS PR gate: `yarn test:pr` +- [ ] If this changes compression/upload/download native behavior, ran the Harness tests on a simulator/device: `yarn test:harness:android` or `yarn test:harness:ios`. diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml index 61f25a0..9b9c733 100644 --- a/.github/workflows/build-android.yml +++ b/.github/workflows/build-android.yml @@ -1,4 +1,4 @@ -name: Build Android App +name: Build Android on: push: @@ -8,31 +8,45 @@ on: - '.github/workflows/build-android.yml' - 'android/**' - 'examples/bare/android/**' - - 'yarn.lock' - 'src/Spec/**' + - '**/react-native.config.js' + - 'package.json' + - 'yarn.lock' pull_request: paths: - '.github/workflows/build-android.yml' - 'android/**' - 'examples/bare/android/**' - - 'yarn.lock' - 'src/Spec/**' + - '**/react-native.config.js' + - 'package.json' + - 'yarn.lock' jobs: - build_example: + build: name: Build Android Example App runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: yarn + + - name: Enable Corepack + run: corepack enable + + - name: Install npm dependencies (yarn) + run: yarn install --immutable + - name: Setup JDK 17 uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 17 - - - name: Install node_modules - run: yarn install --immutable + java-package: jdk - name: Restore Gradle cache uses: actions/cache@v4 @@ -43,5 +57,12 @@ jobs: key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle- + - name: Run Gradle Build for examples/bare/android/ - run: cd examples/bare/android && ./gradlew assembleDebug --build-cache && cd ../../.. + working-directory: examples/bare/android + run: ./gradlew assembleDebug --no-daemon --build-cache + + - name: Stop Gradle Daemon + if: always() + working-directory: examples/bare/android + run: ./gradlew --stop diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index 302e1e1..940e5a2 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -1,4 +1,4 @@ -name: Build iOS App +name: Build iOS on: push: @@ -7,34 +7,57 @@ on: paths: - '.github/workflows/build-ios.yml' - 'ios/**' - - '*.podspec' - 'examples/bare/ios/**' + - 'examples/bare/Gemfile' + - 'examples/bare/Gemfile.lock' - 'src/Spec/**' + - '**/Podfile.lock' + - '**/*.podspec' + - '**/react-native.config.js' + - 'package.json' + - 'yarn.lock' pull_request: paths: - '.github/workflows/build-ios.yml' - 'ios/**' - - '*.podspec' - 'examples/bare/ios/**' + - 'examples/bare/Gemfile' + - 'examples/bare/Gemfile.lock' - 'src/Spec/**' + - '**/Podfile.lock' + - '**/*.podspec' + - '**/react-native.config.js' + - 'package.json' + - 'yarn.lock' + +env: + USE_CCACHE: 1 + RCT_NEW_ARCH_ENABLED: 1 jobs: build: name: Build iOS Example App - runs-on: macOS-latest - defaults: - run: - working-directory: examples/bare/ios + runs-on: macos-15 steps: - uses: actions/checkout@v4 - - name: Install node_modules + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: yarn + + - name: Enable Corepack + run: corepack enable + + - name: Install npm dependencies (yarn) run: yarn install --immutable - working-directory: . - - name: Restore buildcache - uses: mikehardy/buildcache-action@v1 - continue-on-error: true + - name: Restore ccache + uses: hendrikmuhs/ccache-action@v1.2 + + - name: Select Xcode 16.4 + run: sudo xcode-select -s "/Applications/Xcode_16.4.app/Contents/Developer" - name: Setup Ruby (bundle) uses: ruby/setup-ruby@v1 @@ -46,18 +69,17 @@ jobs: - name: Restore Pods cache uses: actions/cache@v4 with: - path: | - examples/bare/ios/Pods - ~/Library/Caches/CocoaPods - ~/.cocoapods + path: examples/bare/ios/Pods key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} restore-keys: | ${{ runner.os }}-pods- + - name: Install Pods + working-directory: examples/bare/ios run: bundle exec pod check || bundle exec pod install - - name: Install xcpretty - run: gem install xcpretty + - name: Build App + working-directory: examples/bare/ios run: "set -o pipefail && xcodebuild \ CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ \ -derivedDataPath build -UseModernBuildSystem=YES \ @@ -65,6 +87,6 @@ jobs: -scheme BareExample \ -sdk iphonesimulator \ -configuration Debug \ - -destination 'platform=iOS Simulator,name=iPhone 15 Pro' \ + -destination 'generic/platform=iOS Simulator' \ build \ - CODE_SIGNING_ALLOWED=NO | xcpretty" + CODE_SIGNING_ALLOWED=NO" diff --git a/.github/workflows/validate-js.yml b/.github/workflows/validate-js.yml index 18fca52..d6b5b9c 100644 --- a/.github/workflows/validate-js.yml +++ b/.github/workflows/validate-js.yml @@ -7,6 +7,7 @@ on: paths: - '.github/workflows/validate-js.yml' - 'src/**' + - '__tests__/**' - '*.json' - '*.js' - '*.lock' @@ -22,6 +23,7 @@ on: paths: - '.github/workflows/validate-js.yml' - 'src/**' + - '__tests__/**' - '*.json' - '*.js' - '*.lock' @@ -35,38 +37,23 @@ on: - 'examples/expo/*.tsx' jobs: - compile: - name: Compile JS (tsc) + validate: + name: Validate JS runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Install reviewdog - uses: reviewdog/action-setup@v1 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: yarn - - name: Install node_modules - run: yarn install --immutable + - name: Enable Corepack + run: corepack enable - - name: Run TypeScript # Reviewdog tsc errorformat: %f:%l:%c - error TS%n: %m - run: | - yarn typecheck | reviewdog -name="tsc" -efm="%f(%l,%c): error TS%n: %m" -reporter="github-pr-review" -filter-mode="nofilter" -fail-on-error -tee - env: - REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Install node_modules + run: yarn install --immutable - lint: - name: Lint JS (eslint, prettier) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Install node_modules - run: yarn install --immutable - - - name: Run ESLint - run: yarn lint - - - name: Run ESLint with auto-fix - run: yarn lint --fix - - - name: Verify no files have changed after auto-fix - run: git diff --exit-code HEAD + - name: Run PR test gate + run: yarn test:pr diff --git a/.gitignore b/.gitignore index 7b41da5..e4bed61 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,10 @@ android/keystores/debug.keystore # Turborepo .turbo/ +# React Native Harness generated files +.harness/ +examples/bare/.harness/ + # generated by bob lib/ .mtslconfig.json diff --git a/__tests__/compressor.test.ts b/__tests__/compressor.test.ts new file mode 100644 index 0000000..0ec037a --- /dev/null +++ b/__tests__/compressor.test.ts @@ -0,0 +1,377 @@ +const mockListeners: Record void>> = {}; +const mockSubscriptions: Array<{ remove: jest.Mock }> = []; + +// These unit tests validate the JavaScript wrapper contract. The native +// Compressor module is mocked, so real media decoding must be smoke-tested +// in an example app on a simulator or device. +const mockCompressor = { + image_compress: jest.fn(), + compress: jest.fn(), + cancelCompression: jest.fn(), + activateBackgroundTask: jest.fn(), + deactivateBackgroundTask: jest.fn(), + compress_audio: jest.fn(), + upload: jest.fn(), + cancelUpload: jest.fn(), + download: jest.fn(), + generateFilePath: jest.fn(), + getRealPath: jest.fn(), + getVideoMetaData: jest.fn(), + getImageMetaData: jest.fn(), + createVideoThumbnail: jest.fn(), + clearCache: jest.fn(), + getFileSize: jest.fn(), +}; + +const localVideoUri = 'file:///tmp/react-native-compressor/input-video.mp4'; +const localImageUri = 'file:///tmp/react-native-compressor/input-image.jpg'; +const localAudioUri = 'file:///tmp/react-native-compressor/input-audio.wav'; + +jest.mock('react-native', () => ({ + NativeModules: { + Compressor: mockCompressor, + }, + NativeEventEmitter: jest.fn().mockImplementation(() => ({ + addListener: jest.fn((eventName: string, callback: (event: unknown) => void) => { + const subscription = { remove: jest.fn() }; + mockListeners[eventName] = [...(mockListeners[eventName] ?? []), callback]; + mockSubscriptions.push(subscription); + return subscription; + }), + removeAllListeners: jest.fn((eventName: string) => { + mockListeners[eventName] = []; + }), + })), + Platform: { + OS: 'ios', + select: jest.fn((options: Record) => options.ios ?? options.default), + }, +})); + +const emitNativeEvent = (eventName: string, event: unknown) => { + (mockListeners[eventName] ?? []).forEach((callback) => callback(event)); +}; + +describe('react-native-compressor JS wrapper API', () => { + let api: typeof import('../src'); + let reactNative: typeof import('react-native'); + + beforeEach(() => { + jest.clearAllMocks(); + Object.keys(mockListeners).forEach((eventName) => { + mockListeners[eventName] = []; + }); + mockSubscriptions.length = 0; + reactNative = require('react-native'); + reactNative.Platform.OS = 'ios'; + api = require('../src'); + }); + + it('exports all public modules and helper functions', () => { + const publicRuntimeExports = [ + 'Audio', + 'Image', + 'UploadType', + 'UploaderHttpMethod', + 'Video', + 'backgroundUpload', + 'cancelUpload', + 'clearCache', + 'createVideoThumbnail', + 'download', + 'generateFilePath', + 'getDetails', + 'getFileSize', + 'getImageMetaData', + 'getRealPath', + 'getVideoMetaData', + 'uuidv4', + ]; + + expect(Object.keys(api.default).sort()).toEqual(publicRuntimeExports.sort()); + publicRuntimeExports.forEach((exportName) => { + expect(api.default[exportName as keyof typeof api.default]).toBe(api[exportName as keyof typeof api]); + }); + expect(api.UploadType.MULTIPART).toBe(1); + expect(api.UploaderHttpMethod.PATCH).toBe('PATCH'); + }); + + it('compresses images, strips base64 headers, forwards progress, and removes listeners', async () => { + mockCompressor.image_compress.mockImplementation(async (_value, options) => { + emitNativeEvent('downloadProgress', { + uuid: options.uuid, + data: { progress: 55 }, + }); + emitNativeEvent('downloadProgress', { + uuid: 'other-id', + data: { progress: 99 }, + }); + return 'file://compressed-image.jpg'; + }); + const downloadProgress = jest.fn(); + + await expect( + api.Image.compress('data:image/png;charset=utf-8;base64,abc123', { + quality: 0.7, + downloadProgress, + }), + ).resolves.toBe('file://compressed-image.jpg'); + + expect(mockCompressor.image_compress).toHaveBeenCalledWith( + 'abc123', + expect.objectContaining({ + quality: 0.7, + uuid: expect.any(String), + }), + ); + expect(downloadProgress).toHaveBeenCalledWith(55); + expect(downloadProgress).toHaveBeenCalledTimes(1); + expect(mockSubscriptions.at(-1)?.remove).toHaveBeenCalledTimes(1); + }); + + it('rejects empty image compression input before calling native code', async () => { + await expect(api.Image.compress('')).rejects.toThrow('Compression value is empty'); + expect(mockCompressor.image_compress).not.toHaveBeenCalled(); + }); + + it('compresses videos with defaults, cancellation id, progress callbacks, and listener cleanup', async () => { + mockCompressor.compress.mockImplementation(async (_fileUrl, options) => { + emitNativeEvent('videoCompressProgress', { + uuid: options.uuid, + data: { progress: 22 }, + }); + emitNativeEvent('downloadProgress', { + uuid: options.uuid, + data: { progress: 33 }, + }); + return 'file://compressed-video.mp4'; + }); + const onProgress = jest.fn(); + const downloadProgress = jest.fn(); + const getCancellationId = jest.fn(); + + await expect( + api.Video.compress( + localVideoUri, + { + bitrate: 1000, + getCancellationId, + downloadProgress, + stripAudio: true, + }, + onProgress, + ), + ).resolves.toBe('file://compressed-video.mp4'); + + expect(mockCompressor.compress).toHaveBeenCalledWith( + localVideoUri, + expect.objectContaining({ + uuid: expect.any(String), + bitrate: 1000, + compressionMethod: 'auto', + maxSize: 640, + stripAudio: true, + }), + ); + expect(getCancellationId).toHaveBeenCalledWith(expect.any(String)); + expect(onProgress).toHaveBeenCalledWith(22); + expect(downloadProgress).toHaveBeenCalledWith(33); + mockSubscriptions.slice(-2).forEach((subscription) => { + expect(subscription.remove).toHaveBeenCalledTimes(1); + }); + }); + + it('forwards manual video compression options and minimum file size', async () => { + mockCompressor.compress.mockResolvedValue('file://manual.mp4'); + + await api.Video.compress(localVideoUri, { + compressionMethod: 'manual', + maxSize: 720, + minimumFileSizeForCompress: 10, + progressDivider: 5, + }); + + expect(mockCompressor.compress).toHaveBeenCalledWith( + localVideoUri, + expect.objectContaining({ + compressionMethod: 'manual', + maxSize: 720, + minimumFileSizeForCompress: 10, + progressDivider: 5, + }), + ); + }); + + it('proxies video cancellation and background task lifecycle', async () => { + mockCompressor.activateBackgroundTask.mockImplementation(async () => { + emitNativeEvent('backgroundTaskExpired', { expired: true }); + return 'activated'; + }); + mockCompressor.deactivateBackgroundTask.mockResolvedValue('deactivated'); + const onExpired = jest.fn(); + + api.Video.cancelCompression('video-id'); + await expect(api.Video.activateBackgroundTask(onExpired)).resolves.toBe('activated'); + await expect(api.Video.deactivateBackgroundTask()).resolves.toBe('deactivated'); + + expect(mockCompressor.cancelCompression).toHaveBeenCalledWith('video-id'); + expect(mockCompressor.activateBackgroundTask).toHaveBeenCalledWith({}); + expect(mockCompressor.deactivateBackgroundTask).toHaveBeenCalledWith({}); + expect(onExpired).toHaveBeenCalledWith({ expired: true }); + }); + + it('compresses audio with defaults and custom options', async () => { + mockCompressor.compress_audio.mockResolvedValueOnce('file://audio.aac').mockResolvedValueOnce('file://audio-high.aac'); + + await expect(api.Audio.compress(localAudioUri)).resolves.toBe('file://audio.aac'); + await expect(api.Audio.compress(localAudioUri, { quality: 'high', bitrate: 128000 })).resolves.toBe('file://audio-high.aac'); + + expect(mockCompressor.compress_audio).toHaveBeenNthCalledWith(1, localAudioUri, { quality: 'medium' }); + expect(mockCompressor.compress_audio).toHaveBeenNthCalledWith(2, localAudioUri, { + quality: 'high', + bitrate: 128000, + }); + }); + + it('wraps native utility functions and normalizes image metadata', async () => { + reactNative.Platform.OS = 'android'; + mockCompressor.generateFilePath.mockResolvedValue('/tmp/output.jpg'); + mockCompressor.getRealPath.mockResolvedValue('/real/video.mp4'); + mockCompressor.getVideoMetaData.mockResolvedValue({ width: 1920, height: 1080 }); + mockCompressor.getImageMetaData.mockResolvedValue({ + ImageWidth: '640', + ImageLength: '480', + Orientation: '1', + size: 123, + extension: 'jpg', + }); + mockCompressor.createVideoThumbnail.mockResolvedValue({ path: 'thumb.jpg' }); + mockCompressor.clearCache.mockResolvedValue('cleared'); + mockCompressor.getFileSize.mockResolvedValue('10'); + + await expect(api.generateFilePath('jpg')).resolves.toBe('file:///tmp/output.jpg'); + await expect(api.getRealPath('content://video', 'video')).resolves.toBe('/real/video.mp4'); + await expect(api.getVideoMetaData('file://video.mp4')).resolves.toEqual({ width: 1920, height: 1080 }); + await expect(api.getImageMetaData(localImageUri)).resolves.toEqual({ + ImageWidth: 640, + ImageHeight: 480, + Orientation: 1, + size: 123, + extension: 'jpg', + exif: { + ImageWidth: '640', + ImageLength: '480', + Orientation: '1', + size: 123, + extension: 'jpg', + }, + }); + await expect(api.createVideoThumbnail(localVideoUri, { headers: { Authorization: 'token' } })).resolves.toEqual({ + path: 'thumb.jpg', + }); + await expect(api.clearCache('/tmp/cache')).resolves.toBe('cleared'); + await expect(api.getFileSize(localImageUri)).resolves.toBe('10'); + }); + + it('uses iOS image metadata keys when running on iOS', async () => { + mockCompressor.getImageMetaData.mockResolvedValue({ + PixelWidth: 1024, + PixelHeight: 768, + Orientation: 6, + size: 456, + extension: 'heic', + }); + + await expect(api.getImageMetaData('file:///tmp/react-native-compressor/input-image.heic')).resolves.toMatchObject({ + ImageWidth: 1024, + ImageHeight: 768, + Orientation: 6, + size: 456, + extension: 'heic', + }); + }); + + it('downloads files, strips Android file prefixes, reports progress, and removes listeners', async () => { + reactNative.Platform.OS = 'android'; + mockCompressor.download.mockImplementation(async (_fileUrl, options) => { + emitNativeEvent('downloadProgress', { + uuid: options.uuid, + data: { progress: 88 }, + }); + return '/downloads/file.mp4'; + }); + const downloadProgress = jest.fn(); + + await expect(api.download('file:///storage/input.mp4', downloadProgress, 10)).resolves.toBe('/downloads/file.mp4'); + + expect(mockCompressor.download).toHaveBeenCalledWith('/storage/input.mp4', { + uuid: expect.any(String), + progressDivider: 10, + }); + expect(downloadProgress).toHaveBeenCalledWith(88); + expect(mockSubscriptions.at(-1)?.remove).toHaveBeenCalledTimes(1); + }); + + it('uploads files with options, progress, cancellation id, abort handling, and Android path normalization', async () => { + reactNative.Platform.OS = 'android'; + mockCompressor.upload.mockImplementation(async (_fileUrl, options) => { + emitNativeEvent('uploadProgress', { + uuid: options.uuid, + data: { written: 4, total: 10 }, + }); + return { status: 200 }; + }); + const onProgress = jest.fn(); + const getCancellationId = jest.fn(); + const abortController = new AbortController(); + + await expect( + api.backgroundUpload( + 'https://example.com/upload', + 'file:///storage/input.mp4', + { + uploadType: api.UploadType.MULTIPART, + fieldName: 'file', + httpMethod: api.UploaderHttpMethod.PATCH, + headers: { Authorization: 'token' }, + parameters: { album: 'demo' }, + getCancellationId, + }, + onProgress, + abortController.signal, + ), + ).resolves.toEqual({ status: 200 }); + + const uploadOptions = mockCompressor.upload.mock.calls[0][1]; + abortController.abort(); + + expect(mockCompressor.upload).toHaveBeenCalledWith( + '/storage/input.mp4', + expect.objectContaining({ + uuid: expect.any(String), + method: 'PATCH', + url: 'https://example.com/upload', + uploadType: api.UploadType.MULTIPART, + fieldName: 'file', + headers: { Authorization: 'token' }, + parameters: { album: 'demo' }, + }), + ); + expect(getCancellationId).toHaveBeenCalledWith(uploadOptions.uuid); + expect(onProgress).toHaveBeenCalledWith(4, 10); + expect(mockCompressor.cancelUpload).toHaveBeenCalledWith(uploadOptions.uuid, false); + expect(mockSubscriptions.at(-1)?.remove).toHaveBeenCalledTimes(1); + }); + + it('cancels one upload or all uploads through the native module', () => { + api.cancelUpload('upload-id'); + api.cancelUpload(undefined, true); + + expect(mockCompressor.cancelUpload).toHaveBeenNthCalledWith(1, 'upload-id', false); + expect(mockCompressor.cancelUpload).toHaveBeenNthCalledWith(2, '', true); + }); + + it('generates uuid-like ids', () => { + expect(api.uuidv4()).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/); + }); +}); diff --git a/eslint.config.mjs b/eslint.config.mjs index d26869c..e93d12f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -24,6 +24,6 @@ export default defineConfig([ }, }, { - ignores: ['node_modules/', 'lib/', 'examples/'], + ignores: ['node_modules/', 'lib/', 'examples/', '.harness/'], }, ]); diff --git a/examples/bare/Gemfile.lock b/examples/bare/Gemfile.lock index ce53b4d..f78af3f 100644 --- a/examples/bare/Gemfile.lock +++ b/examples/bare/Gemfile.lock @@ -63,7 +63,7 @@ GEM cocoapods-try (1.2.0) colored2 (3.1.2) concurrent-ruby (1.3.3) - connection_pool (3.0.2) + connection_pool (2.5.5) drb (2.2.3) escape (0.0.4) ethon (0.18.0) diff --git a/examples/bare/harness/native-compressor.harness.ts b/examples/bare/harness/native-compressor.harness.ts new file mode 100644 index 0000000..c5f2276 --- /dev/null +++ b/examples/bare/harness/native-compressor.harness.ts @@ -0,0 +1 @@ +import '../../../harness/native-compressor.harness'; diff --git a/examples/bare/harnessCallableModules.js b/examples/bare/harnessCallableModules.js new file mode 100644 index 0000000..b393275 --- /dev/null +++ b/examples/bare/harnessCallableModules.js @@ -0,0 +1,12 @@ +import { registerCallableModule } from 'react-native'; +import RCTDeviceEventEmitter from 'react-native/Libraries/EventEmitter/RCTDeviceEventEmitter'; +import RCTNativeAppEventEmitter from 'react-native/Libraries/EventEmitter/RCTNativeAppEventEmitter'; +import RCTLog from 'react-native/Libraries/Utilities/RCTLog'; +import HMRClient from 'react-native/Libraries/Utilities/HMRClient'; + +if (global.RN_HARNESS) { + registerCallableModule('RCTDeviceEventEmitter', RCTDeviceEventEmitter); + registerCallableModule('RCTNativeAppEventEmitter', RCTNativeAppEventEmitter); + registerCallableModule('RCTLog', RCTLog); + registerCallableModule('HMRClient', HMRClient); +} diff --git a/examples/bare/index.js b/examples/bare/index.js index 752e7ee..03dddd1 100644 --- a/examples/bare/index.js +++ b/examples/bare/index.js @@ -2,6 +2,7 @@ * @format */ +import './harnessCallableModules'; import { AppRegistry } from 'react-native'; import App from './src/App'; import { name as appName } from './app.json'; diff --git a/examples/bare/ios/Podfile.lock b/examples/bare/ios/Podfile.lock index 04aec5b..5c76269 100644 --- a/examples/bare/ios/Podfile.lock +++ b/examples/bare/ios/Podfile.lock @@ -1384,7 +1384,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - react-native-compressor (1.16.2): + - react-native-compressor (1.18.1): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2543,7 +2543,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FBLazyVector: 26fd21c75314e101f280d401e97f27d54f3f7064 - hermes-engine: fb3d22f8c6c8b6ddf4aa9801a82c1e90349421e5 + hermes-engine: eaa65d42895b52c6d680c0aebfccfa50baccce3d RCTDeprecation: c7a2768f905d76ca6d2cfefb26e4349eacbdfca3 RCTRequired: 5e502c3553cfbed090a991c444448da452fb752e RCTSwiftUI: 5ce3ccbdc58b78cc4ebbaace01709ec22d58e131 @@ -2552,7 +2552,7 @@ SPEC CHECKSUMS: React: 13cf8451582adb1bb324306e1893b91d1cba28c6 React-callinvoker: 91e6a605826b684ad2e623811253b4d0c4196bef React-Core: 46818de5f211b2a2759ac823b591af8a0a95c2c1 - React-Core-prebuilt: 8554476c8fc6c803aad9226419927dafd496dcd6 + React-Core-prebuilt: 7393118ba8d9419fbaf6e4083c2ede4837d5d29c React-CoreModules: a6a37afee48d4a31ab398640b0795462647d5c67 React-cxxreact: 2ec3e2f7a8ae9303460d4ba94cde183ea90d64cd React-debug: 0d21117b897ce0359c9d2c9dfe952f237476a14a @@ -2581,7 +2581,7 @@ SPEC CHECKSUMS: React-Mapbuffer: 1fc10d873f00aa895836c316a9737ce7d43875ce React-microtasksnativemodule: 85ac7286ff84e8fc8e1956233748b8d4b2a6dcea react-native-cameraroll: 4d4fcff8a057235ce7a9f57d4566409207216a86 - react-native-compressor: e98e8f94c6fd8598b38f29a152c4cc3bb07cb41c + react-native-compressor: baa9ad85164b535ecbb2a930e89fb8172cd19575 react-native-document-picker: 15cca2d1a6bfb6d0d3ff0283bd9b903e57cb028b react-native-get-random-values: 68792987aef40e8aa72dc448d97e009d1f440c88 react-native-image-picker: 23540feacc79c63c60857f318fdfa8477c26e70a @@ -2620,7 +2620,7 @@ SPEC CHECKSUMS: ReactAppDependencyProvider: 22e2265d86a4e871e5e858f4e7ef1c8d01103680 ReactCodegen: 5bd23df5c8ad6c87df0bc8ccd391bd37bf6c92d5 ReactCommon: a804bb8d1dcf3ecdec3a77eb8bba19b7863bbbdb - ReactNativeDependencies: 8948c56c033b69d0afa1c8c6efe8c0c5b263445c + ReactNativeDependencies: a17cceda80e02709ea6124c4abae3917dc3aa06b ReactNativeFs: 21026144ac71a65acf6855c52b1a8eb31012bb5d RNCMaskedView: eb2b2e538afa907f05a5848a1a1ac26092e6fec9 RNReanimated: c4e6659e58b793885ae6da476cb514fc913e7b85 diff --git a/examples/bare/jest.harness.config.mjs b/examples/bare/jest.harness.config.mjs new file mode 100644 index 0000000..e49690b --- /dev/null +++ b/examples/bare/jest.harness.config.mjs @@ -0,0 +1,7 @@ +export default { + preset: 'react-native-harness', + roots: ['/harness'], + testMatch: ['/harness/**/*.harness.{js,ts,tsx}'], + testPathIgnorePatterns: ['/node_modules/', '/../../node_modules/', '/../../lib/'], + watchman: false, +}; diff --git a/examples/bare/metro.config.js b/examples/bare/metro.config.js index 29d8a49..bac11a3 100644 --- a/examples/bare/metro.config.js +++ b/examples/bare/metro.config.js @@ -1,6 +1,12 @@ const path = require('path'); const { getDefaultConfig } = require('@react-native/metro-config'); const { withMetroConfig } = require('react-native-monorepo-config'); +const baseJSBundleModule = require('metro/private/DeltaBundler/Serializers/baseJSBundle'); +const bundleToStringModule = require('metro/private/lib/bundleToString'); +const getAllFilesModule = require('metro/private/DeltaBundler/Serializers/getAllFiles'); +const baseJSBundle = baseJSBundleModule.default ?? baseJSBundleModule; +const bundleToString = bundleToStringModule.default ?? bundleToStringModule; +const getAllFiles = getAllFilesModule.default ?? getAllFilesModule; const root = path.resolve(__dirname, '..', '..'); @@ -15,4 +21,61 @@ const config = withMetroConfig(getDefaultConfig(__dirname), { dirname: __dirname, }); +config.resolver.useWatchman = false; +config.resolver.extraNodeModules = { + ...config.resolver.extraNodeModules, + '@dr.pogodin/react-native-fs': path.dirname( + require.resolve('@dr.pogodin/react-native-fs/package.json') + ), +}; + +const upstreamSerializer = config.serializer; +let mainEntryPointModules = new Set(); + +const serializeBundle = (entryPoint, preModules, graph, options) => { + const serializedBundle = bundleToString( + baseJSBundle(entryPoint, preModules, graph, options) + ); + + return typeof serializedBundle === 'string' + ? serializedBundle + : serializedBundle.code; +}; + +config.serializer = { + ...upstreamSerializer, + customSerializer: async (entryPoint, preModules, graph, options) => { + if (options.modulesOnly) { + return serializeBundle(entryPoint, preModules, graph, { + ...options, + processModuleFilter: (module) => { + if (options.processModuleFilter && !options.processModuleFilter(module)) { + return false; + } + + return !mainEntryPointModules.has(module.path); + }, + }); + } + + mainEntryPointModules = new Set( + await getAllFiles(preModules, graph, options) + ); + + const bundle = serializeBundle(entryPoint, preModules, graph, options); + const setupModuleMatch = bundle.match( + /},(\d+),\[[^\]]*\],"[^"]*InitializeCore\.js"\);/ + ); + + if (!setupModuleMatch) { + return bundle; + } + + return bundle.replace( + /__r\((\d+)\);\s*\/\/# sourceMappingURL/, + `__r(${setupModuleMatch[1]});\n__r($1);\n//# sourceMappingURL` + ); + }, +}; + module.exports = config; diff --git a/examples/bare/package.json b/examples/bare/package.json index ad78541..de94642 100644 --- a/examples/bare/package.json +++ b/examples/bare/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "android": "react-native run-android", - "ios": "react-native run-ios", + "ios": "react-native run-ios --simulator \"${IOS_SIMULATOR:-iPhone 17 Pro}\"", "lint": "eslint .", "start": "react-native start", "test": "jest" diff --git a/examples/bare/rn-harness.config.mjs b/examples/bare/rn-harness.config.mjs new file mode 100644 index 0000000..4abc788 --- /dev/null +++ b/examples/bare/rn-harness.config.mjs @@ -0,0 +1,30 @@ +import { androidEmulator, androidPlatform } from '@react-native-harness/platform-android'; +import { applePlatform, appleSimulator } from '@react-native-harness/platform-apple'; + +const androidDevice = process.env.RN_HARNESS_ANDROID_DEVICE ?? 'Pixel_8_API_35'; +const iosDevice = process.env.RN_HARNESS_IOS_DEVICE ?? 'iPhone 17 Pro'; +const iosVersion = process.env.RN_HARNESS_IOS_VERSION ?? '26.4'; + +const config = { + entryPoint: './index.js', + appRegistryComponentName: 'BareExample', + defaultRunner: 'android', + metroPort: Number(process.env.RN_HARNESS_METRO_PORT ?? 8082), + bridgeTimeout: Number(process.env.RN_HARNESS_BRIDGE_TIMEOUT ?? 30000), + bundleStartTimeout: Number(process.env.RN_HARNESS_BUNDLE_START_TIMEOUT ?? 30000), + forwardClientLogs: true, + runners: [ + androidPlatform({ + name: 'android', + device: androidEmulator(androidDevice), + bundleId: 'com.bareexample', + }), + applePlatform({ + name: 'ios', + device: appleSimulator(iosDevice, iosVersion), + bundleId: 'org.reactjs.native.example.BareExample', + }), + ], +}; + +export default config; diff --git a/harness/native-compressor.harness.ts b/harness/native-compressor.harness.ts new file mode 100644 index 0000000..e4fddf0 --- /dev/null +++ b/harness/native-compressor.harness.ts @@ -0,0 +1,190 @@ +import { beforeAll, describe, expect, it } from 'react-native-harness'; +import { getEnforcing } from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; +import CompressorApi, { + Audio, + Image, + UploadType, + UploaderHttpMethod, + Video, + backgroundUpload, + cancelUpload, + clearCache, + createVideoThumbnail, + download, + generateFilePath, + getDetails, + getFileSize, + getImageMetaData, + getRealPath, + getVideoMetaData, + uuidv4, +} from '../src'; +import * as PublicApi from '../src'; + +type ReactNativeFsModule = { + getConstants(): { + CachesDirectoryPath: string; + }; + exists(path: string): Promise; + writeFile(path: string, b64: string, options: Record): Promise; +}; + +const getTurboModule = (name: string): T => { + return getEnforcing(name); +}; + +const ReactNativeFs = getTurboModule('ReactNativeFs'); + +const publicRuntimeExports = [ + 'Audio', + 'Image', + 'UploadType', + 'UploaderHttpMethod', + 'Video', + 'backgroundUpload', + 'cancelUpload', + 'clearCache', + 'createVideoThumbnail', + 'download', + 'generateFilePath', + 'getDetails', + 'getFileSize', + 'getImageMetaData', + 'getRealPath', + 'getVideoMetaData', + 'uuidv4', +]; + +const imageFixtureBase64 = + '/9j/4AAQSkZJRgABAgAAAQABAAD//gAQTGF2YzYyLjI4LjEwMAD/2wBDAAgEBAQEBAUFBQUFBQYGBgYGBgYGBgYGBgYHBwcICAgHBwcGBgcHCAgICAkJCQgICAgJCQoKCgwMCwsODg4RERT/xABMAAEBAAAAAAAAAAAAAAAAAAAABwEBAQAAAAAAAAAAAAAAAAAABQcQAQAAAAAAAAAAAAAAAAAAAAARAQAAAAAAAAAAAAAAAAAAAAD/wAARCAAQABADASIAAhEAAxEA/9oADAMBAAIRAxEAPwCOAL+Kf//Z'; + +const videoFixtureBase64 = + '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AAwAAAABAAAAAQAAAA0AAAACAAAAAQAAAA4AAAABAAAAAQAAAA8AAAACAAAAAQAAABAAAAABAAAAAQAAABIAAAACAAAAAQAAABMAAAABAAAAAQAAABQAAAACAAAAAQAAABUAAAABAAAAAQAAABYAAAACAAAAAQAAABcAAAABAAAAAQAAABgAAAACAAAAAQAAABkAAAABAAAAAQAAABsAAAACAAAAAQAAABwAAAABAAAAAQAAAB0AAAACAAAAAQAAAB4AAAABAAAAAQAAAB8AAAACAAAAAQAAACAAAAABAAAAAQAAACIAAAACAAAAAQAAACMAAAABAAAAAQAAACQAAAACAAAAAQAAACUAAAABAAAAAQAAACYAAAACAAAAAQAAACcAAAABAAAAAQAAACkAAAACAAAAAQAAACoAAAABAAAAAQAAACsAAAACAAAAAQAAACwAAAABAAAAAQAAAC0AAAACAAAAAQAAAC4AAAABAAAAAQAAAC8AAAACAAAAAQAAADAAAAABAAAAAQAAADIAAAACAAAAAQAAADMAAAABAAAAAQAAADQAAAACAAAAAQAAADUAAAABAAAAAQAAADYAAAACAAAAAQAAADcAAAABAAAAAQAAADkAAAACAAAAAQAAADoAAAABAAAAAQAAADsAAAACAAAAAQAAADwAAAABAAAAAQAAAD0AAAACAAAAAQAAAXRzdHN6AAAAAAAAAAAAAABYAAABOgAAAW8AAADeAAAA+QAAATYAAAE0AAAA+wAAAPsAAAEaAAABJQAAARgAAAEKAAABZwAAAO8AAAEaAAABOAAAAQQAAADsAAABEgAAAQQAAAEvAAABJQAAATQAAAEOAAABFgAAARIAAAEvAAABMAAAASMAAAEsAAABEgAAARYAAAFEAAAA3gAAAS8AAAELAAABJQAAAOsAAADzAAABFQAAASkAAAE6AAABIwAAATkAAAD2AAABNwAAAQ4AAAExAAAA3wAAAPYAAAEKAAABDgAAAS0AAAE3AAABQwAAAPoAAAEgAAABBQAAATQAAAEOAAABDAAAAQwAAAD9AAAA/gAAAU8AAAEWAAABBgAAASMAAAD7AAABAAAAAR8AAAEeAAABMwAAASgAAAEJAAABBAAAARIAAAEYAAABGwAAARsAAAEQAAABLAAAAScAAAEKAAABCwAAATgAAAFPAAAABQAA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'; + +const audioFixtureBase64 = + 'AAAAHGZ0eXBNNEEgAAACAE00QSBpc29taXNvMgAAAAhmcmVlAAAw7m1kYXTeAgBMYXZjNjIuMjguMTAwAAJgr1uqLC0JD1Tjl6399Ti+K1vipqpLS6lScx1dxoEAwq5fmvqOkta9peY8G7YyuDUcnilsHq/e/POmukebuweYuI6KoEHo3gvZWxdVZ90N0j11xrs7G4q42b0nlXXeNZTr2c6FnOvYnHY3FdF490Xat5yrKbliblZblicdjbFcbNoOKuNisNynbVnOhZTjsbitBxWg2K42Kw3Kw2qdtVhuVxsVpnrTPWmtY3QsTl1hy6w3K42K02K0x1K/VmmjatG00bTUtaqsdVY6lpn2rRtWjasphoVVCqoVVCrlMMphlMMphoVVCqoVVCrlsMpu1MMphoVVdKrpVCrlt2tu5KJJqpqpqpqqKqJKJKKqKpqpqpqqKqKqIooooooooooooooooooooooouAFmmtnSXaJtVGSQTbrMyNuuEb/8frV8dL69XX3/9HHtqQTU/6fjomrsV/rf3Xxq5SoE+rw0YGHjy5lqJkQxSGrmShNI4uwSCgnu2e0w7ixjlYMg2FswHrSnKbFATcdWcpsUybjrDYZ5q2dO3QNajWynyeGGJuZeHbjhhjhMZGWOEzgwMDXXcJCQkGBgZ3cJCQkGHMu7hJxoGZBrzu+QSE5L8/MDZGaoaRKDel0FqcBwakTh3sHizsGgcTMyCSo2Cin0fT1EpoIGTifYek7F6zvX5XKcLXtdrWdWWRkmLKsx0aqs07JNZFsxZRsc1VWadkmrVkxZRrZSqkpFs1YsmJoyajVSlUpVNpFs1bMcMa2rbDHDHCbHDHDHCtnZ2kmdHmxwxrpJ2c3YgdnZ2dmrZ2dnq+QihiiPr+lCyZoRLp86Z4m9KHN3r07LcutOtaDcqXHXGtPtarMdGxz6/NVRlUSUtKJ0zonXIScOHIZoM+ABEPetNU0LC17nx7/Ofr9fHOvMtU1ElSRI86lxJF0uroKV+q/bc6i9Z/04jzDEZND8I5u8sW/rbO+Wa7LtP3vwPGbDS0XYvvv737brWOl13jvqvwPovGayAys2K2rKrEqDkc5471XacS+gSVm2rasqsSoNjZc52nEzq1CSsWKxVijikWLWdnY1SSgykp6ejlRSJmRnZ2NUkoGm0lPTypyixatZ1qSSAM2bNmxoJEyxatWqkF0aZJmyo0eiK1atWJlagaVKmzJceT4YYYPUjU4zTMxV4duFb1HNjjommTgA6Des0MtFC0VC0VB1hf5//t/P+n/X78Xessl3TUqqP2lVKmKur3LUJ6kGQqp4q2CN0Ec0ZQoXjic+909B53e/30e/oelzdFW+lzdlRvzS6zRc3RPNVltMfzZRDlspNZP3yeX+73MK5KNnhhhTqY26me3Bi8TVLRBks/SMKQxnEKYeHFcltROAxls/wDUOx4fpGr4XqiqD9k0knDVWcMQ0Gc5pSuiJx21OiE9V9mUSurgUXOc7hq62knnD74nPTp822NdHlwPisrgGiu3uVbf7O04uQl80yiwPuL5dfsbc/qE1czqvPlO2SzAWgx0kyabKS+Mmr2ust54A5jes9Vg1BgWqL/T/9vf/8vvTqF3kalXG5VN8eupVP+iUihTtzZRz10zq5BcXvp0wFQjQ9yove/4PQ89p4LZee5+3YWig6KamZmmYkHAQOmY1BKalGn0aarShwLIzQe0pTwKyUxW2WSDp0qa2K8jxiu1r/o+GbGXlSyh+SqRjgs5hMJVJXtXaF5mQZsfPa/UWxrtUfgMJy9XXIeauECGLjsP0WDzM2tkbWpqdiNehqTFtJm7JD1PrytUdNWgrmg5367SNI3sfheLiSJKBhVoUHp8TA0QewlrfCMssdRNgcKXPfvyLes0N0Cx+fY3yutmNWuFYVLGo9IB3BC+WQ/Pos3cdVGsAzdx6wptRAzhlRopHd7tKK+T0yHboHgi0IK3RXpkWf26eGRT3jWJLqW/eaMJNs9DgAOo3rPVYGwtCwdCwtCwdCgdCwdGuvf/+36//X24nEusNVXHPGXSVU3T9kVVVzWtKE801mnW23dDLudE35aV0zWh8a2Ljcui6Fruhdd3rQdetNyrNi13Ks50KdflN6hSzKolUtNBAHBrZlCkB0f8sctGIcopk55KpayFgpG1u12zEDAoHQpacouCYkBkQ8FYEiBtajCGiX9SUBc2TcRtIhDJHSgF33O7YjAADRggD+IUQRZzSA7uh5pGtWoNiqrEevo05rG3l4pMCDUIRW0mAqXCE+AJlEQIRcARr+3IYw4MW4wYJfrQ/WNBvrx9l5BVF+PHlE2mFcpEt4SZk1co+8mrng+7PBZD320a5911kmws3sdZBO3ezXkHLfWIO8MEC7M/OfrHNZHRjpYVxCm50MhTmM3AA3DestIYtkokE0kB0cC071fv//e9//n/m74pnFcc2nFJsbgippUyqgBT28YRC74kd77hb8nr+qK86O2DY+AyeEyfKMnhKnMe4JmYHeGdLrl+pGsqwSmi0fszBm/8c0IGylcLMVMtPB5OMvb7EPIDsp/8HQoJEdcJdlPIaN0aV3imga+xsUhWDVROneivqZrG4S1uY3zhg+1Yk2GZBXNtzkgQVk2Zf1zWgFq7awkyiNpPsuu9WPUQJNZyeu/XdIwsz9kKoJKSUjLSGmq8upCTFbtNSqZG0WP6+PFVs+8hXyTzi5ZaNHS9AqnflR9BhQZmJAVbvllqHgNFh4ADiN610eyUTQ0LQ0PXK58f/3vn/5/9pq60yTWtytYkBql4oJRS5Q9e7V5q7pwqmR+b1GfR8QXeQF8XcW7OzDs7MPF2jcWxXOrLYp3OuO2rGto3Kri/J2zFlIvzHJKbERoWBpuQz0hdINizFIF9RGayTjFlc2oU7XppmOHuaJ8dDA4A51pw4pRXndyFtJemr+OFuFxcVBxI3y+OsAZ1S5+z1gzz2DVWuBIe1am49/vsopVMsUv9Nigmq3oc4zYzK69kS8hpsi+tX2NbG5LAvNZlf+87eaKsZrqlys6rDzyOjqimhobBDTcpMTHkOiN5a2UvGWRpzILAo0MzgAOY3rYxrRQ9DAtFAdDAdDQdQ1z4//pd/+f+frXFbyyXoSpzqP/ZKVUlKSoD7gwvNExyAi+OQT4KiRqvepbrdHVetOw9aex82bfqjHdkXnHk+n0ap86c4ryzqvPVxN9oVN0rml4A0fDOk1zQm/TlMtjNMkPhqzhkRyc2d7pY/gKk+Y7bKyNR8Igq0XcsH3DPLpWliRfeMCNiRwzQXG86mP2HMSwlSwZXYsSyN/7CSFCqFM6sezoOPZ0EImsD+H5gqbgvKDzidZoc2kfc1AGBcZlgWaDJKO8USvXV/ES7nb9TQ606/Nqw09TVMJ8nVp0Hzame+zEjcurlH0liGvi3FwbAyzc21RfaSeJJDQrAyrts/K9ZNCBpdMxg+AOY3rPTrDRIVq2+vn/+nP/L8fGuLoq7q5l3tSc3V5MgCUEgaR0lTtPfC+dMHpilKRSZyOPfYjFlwOS8DnXtPKuO51+TxrOcVheCfb7EyVdjmteUlNUyk1SNmZq40UgYM138CSIehDS23JZ6u3JGCTq2+OYG5OgIEovtm9QZcaacs4wPnN/jM37CcXCx8GIjIt5bK+hIWnhmFHPO1TMtiY8QwJ8I4p+jBpkBxhb+Poo0ZLrfTa5U9js8+oFodSnuidmT2fg7FmqefBX4Ei5jiVqfcYZRUuySRva2FolvV6pshGdQKywwZT9ZH36TYaYY3c7aY/ifhoR0VZbwfxw0niN7ebLs0gYxbQ6q+RFkauLR6tUnTbQlSc409wHyWTMkDupwKI+AA5DetdRgmigOhYOhoOoZfz//c+3/3/2V+u6l1XWWXkrNUqoBJWVd5KkoKZwqNau3rqJD1an+DloLzwpSqQq+PH3NFfgG9/eXlaLtbLJXElylzEtqbalLCxVYBRK9arfEynpXtqd8kmJRemf7twvYpL+fWaQ9TPH7JBqpFWw1RzNJwNjqS0xhantqpVAGoPQFRBzZbVo6OtIb+ySJGTUmVkudNUR1RbhbfK1SE1mxg4dNCXavpxlkG7hhMrRp7Cop17W2Isqkb9+SpiVaCwAvH8JknjjRUIN8A1fej5xRdVYoaLqsiJprIfrFQxJSSxX7GO3Sl3vHH4yaec6vY07X39Mdns7EiuArrLuJyYlqsw2JKIWTH2fg+AOA3rRSrDStDBdY7rv/+nP/1+OevMZe3BM1eGSBVKKRQXMHcXbs3SLR0RU/53vjpJQGy2I98G1jUoG3W307mO/ZjZt1zKy27YalbRHlg1ZVlNhUJpoEhEtjHMakzVGfLnKKddwo0CEtZr14qQKCVIxv5+dZuz4nwCWZbQN2szJZoHORLkhY57Th0sEYYOq9OYZZorLX1ygywIBlbFWCrw/BK6E3kWcHHNoIBQNHDQe2L31c0ONqxmd+9XoZ4St8assQpRmJKRd7cSmW5ensmT4MSonXdtP3O9Nr7Dp0CyyZews6YImbUGSRL4tVPwTSydMlWESrRCuraJmLZCVH0hWHsd0RQgRQNj8AA4Des9TgrLIehILxPf/9vz/+P+DrLvL76u5V98ZUlV31lu9bm72qBQd1d0xaQcdVSXtNZzqKWEzS1JK/l3RdatNWpdaz25XF+fYaU/KZIlsZbANjijKqFNEDRBAlCMggqj+0YFpCwR8C0WCTT2cFCUm8w+J4Xr6+EWp73q7J7MUqu3uQWdReHJKUKDsk0uBjL+mFrr84ASCFMVRJltf/SMFhtUqrmUDwhukuK88Wh3JV4StdjWORS2nIxDXOYjZWjRmGwIUwmFOepaeBOgk8IeAz+DRMTSZNCjSqeYGCIliMV6OQ13WTSc6P90x3N29m33AuyZ5DjJ3oPKeubuiXdjh5/DLF/7T14zlLYOMvTOCt0r3ORuHF7M7Hc6WKsK6t0PdRslOlB8N99YhVTFm8nYsqLYuRTeHY2M5Ya1gRFFYMwSFfcJMbw4TjaAneZ/g155/rFKNv4t7Py1eb9GMpGW7DvvlYlPADiN6zxHQwKg6Fh6Fi6l8zv/+Lx/1/6yvO9Z8cqvLzVwDF5sUpIKSUJtpZRem+oILmkifJJEAFmrQ+vsOz0l3Q5eos7BtBOCGVhEio6ScNGaGBEEkUMpYgxh735Wi1NCXAEMJSzpvITkQepGC844lgXAlHfJqptjdJukMU27PC2SAYlk6dl67eFDec0TC1OLRTgSRoP6bBuIwaKKql6t73JrQ4YLcEcr8Gq9iUq/KnOYrnxOeX4WZT3/a4Dig3ptNLAPKq9K6acQ3mnd6jisqluthoZcWGiu+sCamZVZVunoULTiqimI6ReVcUVUmmfAOg3rPU4PoWDo2Fp2ZX/9Tf/v/Pxc4vx1tdNSkrEjmP6r2YJKDFcWnmmorFjlSARepUWZt2uMqNjvPehdZyrjO3ZTjvVde4Hbvccq9x8+UqlJ+U508xyWOMpnEcAmJDFniW/8o46ROVKgjYRkoQoWSdxubzPzXRLzSWkZrpmppbQGIASOrdRsxk899r7SU4wzOtq52wLVwD4M8pJUdAv3xscKUzfStrRu5ciCXFjDRLo7Aht0FdL3pLFRvSCNNZ4UKhE2Fp6yXHbopAa0EuRaoRG4ti2lYeXtbIdHmYtXpMNUkJooMt9ocZ5oBKKY2RkRssglJiFhAa4EF8t26aSu+ujRoX4yW08rMP2tktS++0hAjIjQhAKtVfAAOw3rPRWNRIOxYCxNCwtCwdG147//seP/v/1qdXuS+eFcVqsolYVdB+1JQBid2T2sJ2eXUfI+yj3JxW0ZXOR7RnYJ9pJ2Ruwby230SQla7Kq2tsohBjM7tEb+gGQA2R6BYTkcp0FAPw1aay/d7yIS6NcbTMMjVJGq6zfudHoDMk3dEpA4zNHoX0I8mW58Cjk6UZe8SqTIUROHSjsnpBpFUNzTaYQQMeNgjpa2LAxvbUVaOghyfwsq7EY4PGuFRJ1fHCtEuZZDmjG8aNMTz3bTFQfLYTJJOTLZJHBahtaSHrg7mh4ISjvUApSe+cFJoKD1JjNSe/CLK7NQHg+gaYibjPdAU1Rmlq2akxvpC95jfIEsfMSOmcdhP2hkMFRW6lDsBzrA0WZqYz990h0Vg5HVeNLyzNIXBreAOI3rRFNCxdEw9GwdI9a+3/9K/+//lXftLhrLzWWyEBqlSrmRkwlD1rtWDRjc1sj0+z7FuwSpqQNfZ9+bgtM7bJqqmqyZ1GqZvzaVGcadNnBaopSSj1VFSwvJVH0qoTa+SKSgisM54TONkpn/LXtEkGlYYxAKCScL2NR58xUou7EQKiWmXFjHisxHATbebftOfCy2drfLYjshUC/eWsaXPLWBCXW02H16zWLAn52FkKk1KrZ+WupbVqtyP2g2LmcppvGQ4JIa+Ju9DeJlfCa8VrVqHxScouC9ZLdo7K75bpaO2JaIqsMTriQbyKy26aR59pd/lrOwarImAgrwucp7TlVpeAA4DetcHslH0UB0UB1jue//9z3/8/+dcaxeXNZdal4y6gTCqRSTeqDCqpv8eVftiLnZJ90l0bx/gXG8aVXGl73pqPHG2xpH4uO9feFMTYTQGwe9XALJtngVNvc06iu2/UTEKa5hRXywpFd5GSt94PRaMH3bgOjpubcSLrL7VkXhDXarvmy7PoP5Zo1eqPZ7jRpyScTHYNvzVrkr+hpmpaUal4DzISVTksX+Rt+iHt1/zwdxe/XU+B9syGsKIBUUHSQGlUf1kwrHIcDg644k1kfKStLXy9S3Hl+HDOgrLqzv5d9jU+nhdisYaFDgADgN62Qa0UXQwHR0HVN69//62v/n+b1NVkZqXeXKm4qoHIUqVe5JQ7h9Oz7v753xJuSYB6romDoY+dzNy663jnreO1tW56pWwqVsLDcO5N0zpGbpF+aWjdM72FrJpQiHIsZjIfDHhja2MtjjqFr5tUpLPpWuCQkqPvno0epq2FprhEaKDtYp5WHM7eQRD1uy2mCw+55q/mmLP02S5skVUq9z5tf0CdkcTmDv6fBPmCpl4xqqO3uAMqV19aLWMlHO9hhHgZNymvZpYG8i3ayPBZCxBTXC19OPxR5k+vCKnvF34YUowYdtUEEmg3bRrfTt5hFg+FlW/Vp6j8+it6UHKQycRYI+xYt7W0KSCAf6CPHacAA5jetdKsNFsWhoWioOsXn2/9P2/9v99X1dMmcVxeQb1T/URSpKLoLZwrknrrQuwit6P67j0O2/8KMqkaSzbSWbaS2jtLYOOus8u0nhqWOsM9YblSQ7gYzMFlyg5QS2ZKklsgDgvbXcspIEpxyqVA01dmZhB5z/mpRT+T5NfNlJnLGXfJPSjB2jXs3tdtzhfobWQo8D4xAhcjwIRD/5c/75PrG70cHC3n85Bu6+LKUQ8lgoEGpPrvYKrR3I/1/W7XF08CPoUjjR2VpevmMdThmG6fFzJq9CvKiitbtTy5maVVl16rz+5opoLLZHJC63LHMLEHnUIvjLzWef6+td3rOdOwouADcN62QazUXQ0PQ0HQ0HVO5+n/7e//r/m76Jmaur43xe8q8gcVlSKiqkB3F37S2Zeesio7ByPP9fKwE3RwuWL7yxR+ZOQ9ic93xkfSl96c39JT9S75UmCsSq8A4E0DSyDgPq8rtWtf5k2jG+fOj6fOCoSqIRlZQsjWOMdsokg6q+3YhhsdxcV38hq1tPzSf5z6DHWbsvdJyD/31qnzq4XpTm+xccj6wqtNatYtRj3KvbvmKGqmFF3ml9QQjNarzOt3BYzY9T6VYrFlRXTOv4Ox01dr9LfYlPnT8OrGYGITFMo56+Ut8/WnzZ/pPCZrmFtWbiTWbbgdZw3cLMxT0DmGxPhV6ExFfy5wxJyDYW5NgVT03WTEutU8ASIDTVmRW3iCxmAF1WQjni4arDpaxwADkN6z1OD6GA6Fg6GA6Fg6Y3+n/15/9f51fC6VxStb6xVVdJuARVRkVAbG7p5q7xxbCS5DYa1HWDnxajS8djduxu3Y3zLoO3bTr2NyqNjp3Kn1+eX6FNPKqECUlImI1kOMpkoVdnPuBmEawBLZqRNPDNOXsoeielGI0scIpbaoy1TGWw4rEpmjnIbEM2gUbLICOXUdgUFDDQTC27IBShRExqfkIEIk2gTJubYNlWBTmAqC4Oa4R0jmGhDSsfxYSyjakBaZBApMuMp0eMljJClxwwnorfan00DLxo4anYs3t5EuGxpAnFSGG0TlBhhG9cJCSKUW8nHnTCX65uG3SSY44UH8do7UOb05VOCi5ylgQiKGn2om77Vy0gVQ2CM1kyWXjjbXwAOY3rXB7JRISw9KgdGV4//pd/+f/nk9srLTVaoisQZKof6lbsCNQgciSPBFL5pQkwtYgVfIS/O+4LbniLwTsvSGL0xy6FNhuJdtPgRGOQ4TjFqnsMo3pGoWVys8rKE1mcSQhmmjtoSDnECjC10ErgDcc6JYSpBBgjd+mRe1GWapQCLdMpgZZl5kxBYG1vzQ3RGX525NGAGfSy8Cd4BfGiueXeHIAms6rLt6NTAXwCcEWssZaVOuhsMalVFzBm795OCrG0hIC4CahlVIUmvouIIiezzJzdKbV3qmPVZVLFaPuUIIh5cW2IEwNsyR3mpVfhlBRmy4CELdKMi0Wppw4WHzj1w2zowl1t+lqYqrUsB8nbG0LpNMTW0ZZ5WzpweSVONvA6ZzyqsNjr0+lO7jQAtdcY4AA6jetdPYmhYuhYWoXnf/9Px/5/+XH8f7ViLqLqJNqj9qrpVLoAWziWkeysRtpJ9QUpOH4uf1OGI4lzDsYUbjHSWkcSxs9ItlLJS2SrmK5KUZmKVRKZiESWK5cGnZXZmRt3CDCpjZoyUcGYYG7cOhT5YOYHSMWakSJw3TGUCsAU4UmiXlIqcOyJ8kGRni2+jmGHoUhjO9xghvmeXV08OiU9nPXPT0ou/d6gpfE8K9tXlivVRy12GVYPfdF0Y375D44/el+aoqaUTlutMgnFaaQ/TPXY1Gaza68aKGuxayarbhbRolseWaOPGvfZGd1yRGc2XVSajsaDdTdTlo6wBJ4k7r9BvI9/X5Gvd+OdnAA4jetFNsNGg+sVv1//Z/b/v/9s83W5doW1lSqBVQKUFNTA6Y2jSk3xG7PrFz4OvqJftockbL0C5/Kdv9I7Pnmh0tbpH9xLJKEciOpTggtSNChshIUKWiYvYVD+A2GGMnAwYIL2YEETzaPV5FdvHRxFTcDYSVtO+sn8PQ2mqVJrz1ZuIUK3oXu6gHhaKxs+ap2VcT1aEQWDGlfMcr0+SXOmdTGmrRKVq7OZKlwwXZXo3CN4CcRtmd9/tmKylibrKRmFtG6emlWaqlICZAbmTAKXOk3ivKEGrp6fcT71HXxomJOsdzvWcbU7yZpL9jssmrQAnkjkwWdVyouCjHhrUq8vVxsFOtokVKAaL1XFZN6RBAIZNOo4ADgN6z1OB6GB6Fg6GA6GA6hmc//36//L93Gr3rKziVpeKm5AlRUVRMkqh6127v7t3RVysdbgZ/We3BJcGP4rpOZYXtWY9q6Tv3A792nj2cxzyfIz1dtyl6JTKZjEJi5MjODQhoOXpuY0MEljDJ0CYWIinwhujVVyTru89HmXluTIZB+assQfb1weKoMK+tmCrCySUpAzHmCq9XQzb86NJUxRb6m5uXv1UsWDv5tyi1BhuTAIGLLEm502iTjFZFwaNSpilxghJQSovvC7mLxGRqJZHHcnTkSBPyCzlzUkgPibafZkRuUmqlhKvyMC7Anp3qHwup2+2M8HkCYcYcWzpiaKaKRko0hIlFI2FmIxD9C8hwA4jes9OsMP0zB0bee//x+3/t9fi/N5kVrOEilQVkpUClDdqGjta1VZMbRqN+O+KIParb9wgy3LjSrzeqr5dy3WtJ8u1m5aS/VWOqrZxDClMSmJ4qoFM4EQABNr1SXoxkA4MEpgMxNrCiZjlzbWFinQEObk0OKl6C+G2sOOfYnx1+DAfHnaWzVTwz6MWj+lKLiy8t5mOtor6KqVrZwobVPepIVOKyu1S2zWr+1RyicurGCtn2em6d+fMqfCp5tN6I7n19GReuSFOjnw3MC6F6qotc+iOYTL7iSdqRYE5DNDOI28ZDpoDpqDV0JCioby0g04H6xMoA7SkSAUzBYgQPLvMzRVD6JH4iOMUMuVIAtl9CkkngSo0lXCUoZBCBj0mCRhhfTJCDoSVqJjvQb2n4A3DetMOocDYmhYOhYehYOhYOkeN8//2vH/z/mc9aqrvd71KcVW7Vl1BFqlYVKDtXzqlaWjmcKG8q8lCjdI0u0Rux9ycHyJv2X66rx2Xw72r/STZU0MMGEEm6TgRoQxgmhSJDhaTp/clElXnMbzbpK6L5jVuwk0va7SIaWW/TUM28xQIVW5N12tMYMLBv0ol0/mmlij6I4ZkUlMJQinzmQrhEFinE3y1nGRsjmeGTzERBhxYj9bBUWFCLNnQS54VYNVZvuWms6/09OqyRimUi+0aCfhbfh1+R81qq30Jd2T4XNek8kYTfvVRXPJONY7EC6mo9Em4vK8vC6+Szlfl375rpFxurZ2sRjk2mlKWUSZdGWfCbV/4sElwtsmqRmWqwaIMc1omQwkrzmLjfbeqz8AOg3rRFGNAdCw9CgdEgdGyvn/+Lx/+f4Xxe5MmqjfSm+MVFR/qqVUqUFs4tMNNTDIRVUJrSBM6G3Q9NUsmyu113a677vhbnO6Ou1uNNjTSnp1SkTbMlkiZVeE8MlLC0vxfS1ClCoJbp0KCJ5LxpZn+XeUILnggceVeAdltOs4zSEIUYxAjIxQU4re/JluaA4BWqgW28pFBUJlMkSmo4KpqRw36xOnTLSdp9dby28vC2m9HLZyUqLqnWo5rQcolhu8QRo7JUeSwhly07+pOfbCQu0uq1E3Zm8FL75VqdM6FCxb65pxpZRQLpgGxnvhSSLwl38RpnivPPMGiWJWaUtg7otemSm+NYjcEyi16Vte2Lat+vgV0T5+e+PU7X3w0lxTnvAAOA3rWx6DCWPoWLoWDpH58+P/7vf/t/zK8138arKRWsuueKMtUBqVKVSUJGo1kjgETe+6W/P7buGreRFXJT9ORm7GunInEHP2eizvUrIsK/iPk67ZuK7eddvIIwkTv5EKW6IBfwgThZAMUBNQB/ya8otNcNAkZknJwk0E8RNS3sNegLCQxQv2tiUDAcxYmYJnH5hlBOJNT0DjrauRDCM+cog8LFUvVVLSdOjocXME1g3myXzi/UY3S1QMiRb/fg9NPbun2joZa5EOpypFgqspTGzI/DS9MvcVck3MxmrerWlkRPcoTwKywvFb4OSqWg5HztBLYGa2qBopd0S5e8gpg5KLODG7Vb+qsXie5TnSgLrTAaWijwxshJoKY55itCefRIARhJc9rHJPFycAPA3rPD2eRNEgdG8fH5//s9/+f6/VeaQmWlWVuXVSombvI+kVBBoKmS21TI8XUZ9JWAVTkwtpn2tzjVP2u54W5pVNVUtTZ1GkzKhE81RaZaC5FKK+iJ4pHwGWjPkueC8IgTpR0peBfYcYnxH8EGtq9UllzbtsgYLad0SDXwsHRflXXNWhLGXadgA6EbjWcPYVKD9VugiEb76E+wwm/rHYQQE6zAtFTkgXwAmcQdsyCbWK7TjdWMvfaSW5mILfZKDhWBGo1V0JQjZ2o9cUrPIFkPSOYNL1EdeEW3AjzTHhSmVI9QWVvQMCL6lMxUUsd8tcLNJu03Vvo/f7syaP4831JVc917tjzw81Bw/jfJgnigRwADiN6zxKg6FkaFh6Fg6Fg6R2/j/8PX/f/yme2aajmXldczUB3l1N2KEZJQdUaLbYf74McTBWhaBIq/jB4faeHrJ7Gv1mPaX3m3r5CYiyDzWqJREeiURlJooFEDiuodU5DBgDwJmhmiA4D8xBfZXjPjcxTjuJbZeSXElyCDpHTr3w4AESMOCB9asCmTgBewUdzGu5yI5U7khC++UZu6EUtzkkPKpa6Xkwgc64hqpSmoArfZasl9uFvfHTWfcYLbhX31SG6mpWhVRUrxc73vXQVEgUDjedJ3gk5NPdFnGa6zMqqbzkLZfLTXacmNcSxalzgE20Bvi2mym5msefKetrxVJeRDMTPakwU0O9MwOUmu+gMw0yC0vAOQ3rPTISzEJoUE5rx//Yf+/69Z5urlUtUkwUqKohkU/8g4142p2nptjJBlB6nYedwLe+xHl6yNeeWWRkq7XsLJSMkxVCmlpQroVIKgOyMthykWTgZwAHL5cqUmjYTpwjBQMeSkKfA95f4UWlTkTFjDJBOp0oPHJmOjItwAMUd+KJiKi0yAaShGf8YFwluILUqMqi0S1COQWnnFlOhC4HDRy6NffPr5uxME9kgkdR4oFVAGVZCslzoeNIHgpZVz0c2EreoKDVCRSq7rhBmqqoGo6bBJqAvsfESnGcbkWzISpg3dhJWCy5wU7oJq2GsAbBUAyhLxOocxlOBeJ6wmOaDlBa9UNKdfBJGpsqtmUSBBomUfD5XpUQiHeRl+9Pwfz4OBq4GW4hvN7wdAqt55pbjPG+iUrM+OZUccO1N+YFJiTOJTgANo3rPD2OhNCgdCgtGgdK/bM/9P0/9v34+f1uarVbzrXjWxFRkFjIKpJQ9d+WVVZVVhd2usmQktNv/CjKvX9BXmPxX71+JfJKJfRFNfm1RMZ5omUiWRHiiJGpYQ2hnKWp5MAoSU1mTSxAG0r6IliSyU5LhZ2kugRdIoRiQDeCoKVoAjVcNgTIaWKsMirdQZFXpd3CapUoeHGcsHM2L8WrHZNYCGEHGq34bQfVfzqDtau987/C+SHY0QQRpfFpfgp6NDiGiyI/SSqDKYbelGEs2burqWfN+7snvmhbouAAOo3rPTIYxNCw0FokDo32/X3//s+//8OLnElIripmjEbvEp+xuorcBBptRIctUqp8F8LO9iTgovjlHBc/z2nqUDz1DUqGSoalNemqRKkYuXYk0YlUlGMqkSk54IwJp1zkmmKqxxU0hMzzotlUwbstjuPxbeSTmJxAum51TYTLrk6uWEhvDlQCIaqokunVFUEwBwhMUUDnHlhJkGLhYUjHlMxg3BcrSvVgEQJFVydVq6qGGiX4pVQESjpwsHWQSpZjQlDPA0B57vRWgLpUmo3WlGSo9dIteEKZ3ORMPkIiwCTOsbpLLJVtlO+zPuS5JcprJKrJ6aZMb66uq+rbobn32TgSE5rFA6X5lmmU899EhNBC729pQAYugGb5+ux0Qf4UgfysmtFz94DR8ThwAfwAOI3rPFWDoWKg9CgtCgdI753//Z/P//rqV7bu5qs3xi2+OdY/5V4VMopGDursmYphcThL2mlukEsBeKEzDb52bnWsaL22bna0pkolplqKUy1RjENGnSUpoJcFFoGBMSeccSzuSQcsqKNx1oPDI+IAyHHXCMJkWgvkJ4Z4yk3LfCtxnKIC6IeYUtcBlIdgFZJjDOuFwDucxeMcws5pwTT0Ic67hTO+txF1FaDctl4y3T9qgUwpYjU02rfM5MtdPEWt2Yg48w13EaRcbodRu7I4wZ0Cd+goMgsoHYmmTv4JrqhrGXkBl3djW988X0DUMFn7C9SKq5KJF/6dj0McRRcpm1xSp+L74r3QyfgANo3rPE2foUDpH2vx//ez/y/zq7rOJeu8vW9GKioqopkgtAOMeem+3I7gD1zE3JMWRKFw+Ql9nsPL2HZ7Dl6S7sE2E4IVJgJYMKmcLGFhFMik3eGsxZdHtFvCfcikWkEAWt+Ozz8I7foKegWVIjNGoU5UKcrC1GKlwhxnwpzbq8qlJIIbgBAI5UbxNhfVIurVZgEmVpynmheLQLhXhqr51/FTtXbcSyOFpbBWV37UbZSRFIwScM3IrMQw8lrZWA1HCKcbFJWtM6daamamirCzNr4VRey0LyHa71ZhETRfNRXHA3NSG69Mp1QA0u91Nkh9r4bLEZldyGoWImGd0SikJ65Cihsce4EoiXKFc3K1EQgNUcNbAoDOxPBQ80Wl1HfB5L5/fcA6Des8PY+hYyC1LH2//s+v/P/rl9VU1db35vvhzrIfpl71tVRSwYjmKmaapmmhJgUqiLQYFfyMr2faxara1Xs7rc42TOmzmUiSmWgypsBNUTSRpZN96ktQYxqUkwXBOimtziCX3pV56Ef6rMAx5aQau/I7JuptHg/OQIFTIAsvafXLRUdcFf7ui0CZXHSpoxTzoh3+SrGF3zqdVpTFk+WFcjCiSsxd9yZQmpkbOtLxwkPJT7TC8FmkgIooAwVAqvtRLrZVshMKnUnRkqQ6aFEDOyLBSklEhpTwgZYG0S0BaCoCkM+mZrmsRvAAOQ3rNVIHomDooDoYDoWDqB+n/9zv/2/665/Fbu643XFLqVkqAKJKyry6AHW1FKby5ZbPLqnP4qKGrfZHZRj9sx6g37h0PKK3jJ+QkzAaRyoQEOaDGAJS5A0+FO4ABkivLd9uTOwVyKi/fFDsw6HmSBu9vI+g8jYGWRhnX4VIFgFkmEW8BceNMQjK3QppUEzwimFrtsls6c0MOQUyhwfOTKjInQxsMcBMIH69KofL88LTgxgVVePIYpx1VT1hRTH5qpcglLblDb91sSRcghhcSNvToZVhTQRnTEIuGU1TpUAoswhZkzeR6Ur1fr5nfJZy4z8AOQ3rPTrDS9DQdY58/p//T3/+n+jpqp7/ESXzwojLrIDaFJUSh2VxrT1O/qvqKPcbOdQd4HKEIP4LlPBZH1bsPBcDnVl6tx3JZzPYVVAx0iVbQhQkTSl01f3meYmErMdlhf0jl+eQRTTCYlerTBLX+dHxqZa/YapwUMhtTyYODeIldvocuqG3Tqast31uE3v1yCsqjsMBXHVGQ1NGQSc7avdA/AJbCzjZY9yjsMYrjatDZv5RH2mrWRYzj0zfByWfTLInhsprOKg3IiN7ryZw/yRsMw7RmsWBoefath+Yip2iy++lDwqmp4dKvpaK9Mbmo5+uOxGgUBRsnhFfVm54roi708Ixc3bG1vrjZtbGZGpklhJ3gDgN6z1bQwPQwHQwHQwHRsHSPfj5//t/P/3/zxv213K153Frqm5cCrdwzF5dUmDFsJVppkrbBtpJPTJZC8cIUu13jkeWQ914+kXuq6Hcb1eJZIQBRBYJQBTh0ZPWK5TODiamzMvVP7aF+rr8S2eF6UsgqELZ3rLmk99V7AJHzzSsZCXCP1eyFgBW3CpdOxsFUFT7qa0V5bWBjYXzMzKu9L5spnGsMX9y0Bmphym1+NfIEAMgbpYYDMY1ysCQrUdQyEliLZGRQN4S0F0yAZQarG1SfzW2ei9zbeJnxapdNEkizfWKpgSQG0xDefsTbSvBSDVU6divIeWxdyNVG4t/x2zC45KryE9lK4kEvJUmoZMimEUVC/e3agoN38Bcgg8mGdHfePmrnxk4ADiN61xJiwLQsLQsTRu79f/2vH/6+3fnqburMl76mUKqVCBZGVaA3DuKbZGyzYSh/NyCuqUnjR+xvWsczN1SknGZ5vqlZGueVsdNRssNKxStbIM5EqpaG02JMI4MKBk/eMY7IgjG0AgIkI2GKkX68VxSwI0k0sKtARZIyOtZ2kNeW8uMQkQk23exmSQyztGEibqpoURCZoAbZXOvwhkqnNO9OhTVQoMEIvtyo5bZVlUyI6FTl68sIWygIU2JQWyFLVSaNf0taaa6Tee/TvSUfoYklOeVsJocmOKN1CyFTyvgEU9OigdpZ5JEx9HxTy0TBSkRFPnRTUmyQmG/WqIaRijOLyriaRjaqiiy+0Bayl9NtuD2vOjjBkrWwFDqxWVERjbDu4MaSPlc1TVHPbawMDSDlWN81NC4ty4AQJXrdSmEg9EkG6f/Sv/NOOpl1LZV0vG5Hyu4454zrnVSICcKGTiRSalk2NJqUTUkmcfOz1rqh4kbk8ZdJ16ROdKJxoROG8ms5OCvH5aDFaACBUkFsIQHkGrIIJZgY0sPzv8+TAD/z+z7Pl1zsHqn9T9p1jGLcn2P83/P+/63YypDEZxtGIsCgFm3n5+Pn1YRiQnJxooARNt27dubLQEMMIxooIRHZt27fKDXPPPNVCJ8ssoQFlsAGIw8PbgAAChaHh64AAAU4eXbcQYktDw/HLWAAqMPPh9whIYjDz72AAggwP4+fqAAAhT7+fdQAIyZL7OfACG0XF9fp8fkAzKePX/y+PoHidRV+Xi87lAwqMO6Y/7CAEATxb/TkPfoOMmI/TroVaAO65lT7PpfQWTIzV2ip+aASDYN61/ozdeJ6M1XuLGjuABKJum1UZkxN/Wa5Q6w+fX1qrcMu5/+Ff7XfxJKM/Hrm3WU5a6hOocrGjW1JsnCwc2r5xQpKS4yiOy272HQvBZHC0W83LgrKxZPqqSYsWQoz6qUmmzFkZrpdpJpJscKXrprpwxkmxwxwdnZ2kZ2dqXZ2fZOr5ZYvp9Oc76ssUP0+ieadQi2cw+m7nXQaV3TgtxngWAIjdQAqvXanbH8uScDbgBKuj0mIP9yNmzdYp8BxVoXHUwzrIwaERA0kM9lSDITpInLj5FSnl40OfrqdMsHostSC/ruppWnCtLP/D9X85/15+6RTJUx1Md2NqnTW7NN6oGOnhRjuj9IZTzxpfIxYowowpZWG3NbOFWTSVWVWYBZRZQBDIvBInAMa3nL+C+q5LCvUDUtPAQe6b/wGdzljrFbfIeEu4WGWqlJoGBr3ABGIG0cAAAA69tb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAD6AABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAC2XRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAEAAAAAAAAD6AAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAA+gAAAQAAAEAAAAAAlFtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAACwRFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAH8bWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAHAc3RibAAAAGpzdHNkAAAAAAAAAAEAAABabXA0YQAAAAAAAAABAAAAAAAAAAAAAQAQAAAAAKxEAAAAAAA2ZXNkcwAAAAADgICAJQABAASAgIAXQBUAAAAAAX5PAAF+TwWAgIAFEghW5QAGgICAAQIAAAAgc3R0cwAAAAAAAAACAAAALAAABAAAAAABAAAARAAAABxzdHNjAAAAAAAAAAEAAAABAAAALQAAAAEAAADIc3RzegAAAAAAAAAAAAAALQAAAToAAAFvAAAA3gAAAPkAAAE2AAABNAAAAPsAAAD7AAABGgAAASUAAAEYAAABCgAAAWcAAADvAAABGgAAATgAAAEEAAAA7AAAARIAAAEEAAABLwAAASUAAAE0AAABDgAAARYAAAESAAABLwAAATAAAAEjAAABLAAAARIAAAEWAAABRAAAAN4AAAEvAAABCwAAASUAAADrAAAA8wAAARUAAAEpAAABOgAAAUYAAAFBAAAABQAAABRzdGNvAAAAAAAAAAEAAAAsAAAAGnNncGQBAAAAcm9sbAAAAAIAAAAB//8AAAAcc2JncAAAAAByb2xsAAAAAQAAAC0AAAABAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY2Mi4xMi4xMDA='; + +let imageUri = ''; +let videoUri = ''; +let audioUri = ''; + +const writeFixture = async (fileName: string, base64: string) => { + const path = `${ReactNativeFs.getConstants().CachesDirectoryPath}/${fileName}`; + await ReactNativeFs.writeFile(path, base64, {}); + return `file://${path}`; +}; + +const expectFileOutput = async (uri: string) => { + expect(typeof uri).toBe('string'); + expect(uri.length).toBeGreaterThan(0); + + const path = uri.replace('file://', ''); + await expect(ReactNativeFs.exists(path)).resolves.toBe(true); +}; + +describe('react-native-compressor native harness', () => { + beforeAll(async () => { + imageUri = await writeFixture('compressor-harness-image.jpg', imageFixtureBase64); + videoUri = await writeFixture('compressor-harness-video.mp4', videoFixtureBase64); + audioUri = await writeFixture('compressor-harness-audio.m4a', audioFixtureBase64); + }); + + it('exports the public API from src/index.tsx', () => { + expect(Object.keys(CompressorApi).sort()).toEqual(publicRuntimeExports.sort()); + expect(Object.keys(PublicApi).sort()).toEqual(['default', ...publicRuntimeExports].sort()); + expect(CompressorApi.Video).toBe(Video); + expect(CompressorApi.Audio).toBe(Audio); + expect(CompressorApi.Image).toBe(Image); + expect(CompressorApi.backgroundUpload).toBe(backgroundUpload); + expect(CompressorApi.cancelUpload).toBe(cancelUpload); + expect(CompressorApi.clearCache).toBe(clearCache); + expect(CompressorApi.createVideoThumbnail).toBe(createVideoThumbnail); + expect(CompressorApi.download).toBe(download); + expect(CompressorApi.generateFilePath).toBe(generateFilePath); + expect(CompressorApi.getDetails).toBe(getDetails); + expect(CompressorApi.getFileSize).toBe(getFileSize); + expect(CompressorApi.getImageMetaData).toBe(getImageMetaData); + expect(CompressorApi.getRealPath).toBe(getRealPath); + expect(CompressorApi.getVideoMetaData).toBe(getVideoMetaData); + expect(CompressorApi.uuidv4).toBe(uuidv4); + expect(CompressorApi.UploadType.MULTIPART).toBe(UploadType.MULTIPART); + expect(CompressorApi.UploaderHttpMethod.PATCH).toBe(UploaderHttpMethod.PATCH); + }); + + it('generates native output file paths and resolves local file paths', async () => { + const generatedPath = await generateFilePath('jpg'); + expect(generatedPath).toContain('.jpg'); + expect(generatedPath.startsWith('file://')).toBe(true); + + await expect(getRealPath(imageUri, 'image')).resolves.toBe(imageUri); + await expect(getRealPath(videoUri, 'video')).resolves.toBe(videoUri); + }); + + it('compresses a real image fixture through the exported Image API', async () => { + const output = await Image.compress(imageUri, { + compressionMethod: 'manual', + maxWidth: 8, + maxHeight: 8, + quality: 0.8, + }); + + await expectFileOutput(output); + + const metadata = await getImageMetaData(output); + expect(metadata.ImageWidth).toBeGreaterThan(0); + expect(metadata.ImageHeight).toBeGreaterThan(0); + expect(metadata.size).toBeGreaterThan(0); + expect(metadata.exif).toBeTruthy(); + + await expect(getFileSize(output)).resolves.toMatch(/^\d+$/); + }); + + it('reads metadata from a real video fixture through the exported metadata API', async () => { + const metadata = await getVideoMetaData(videoUri); + expect(metadata.width).toBeGreaterThan(0); + expect(metadata.height).toBeGreaterThan(0); + expect(metadata.duration).toBeGreaterThan(0); + expect(metadata.extension).toBe('mp4'); + expect(metadata.size).toBeGreaterThan(0); + }); + + it('creates and clears a thumbnail for a real video fixture', async () => { + const thumbnail = await createVideoThumbnail(videoUri, {}); + + expect(thumbnail.path).toContain('/thumbnails/'); + expect(thumbnail.mime).toBe('image/jpeg'); + expect(thumbnail.width).toBeGreaterThan(0); + expect(thumbnail.height).toBeGreaterThan(0); + await expectFileOutput(`file://${thumbnail.path}`); + + await expect(clearCache('thumbnails/')).resolves.toBe('done'); + }); + + it('compresses a real audio fixture through the exported Audio API', async () => { + const output = await Audio.compress(audioUri, { quality: 'low' }); + + await expectFileOutput(output); + await expect(getFileSize(output)).resolves.toMatch(/^\d+$/); + }); + + it('manages exported background task and cancellation APIs without crashing', async () => { + const taskId = await Video.activateBackgroundTask(); + expect(taskId === null || typeof taskId === 'number').toBe(true); + + const deactivatedTask = await Video.deactivateBackgroundTask(); + expect(deactivatedTask == null).toBe(true); + expect(() => Video.cancelCompression('missing-compression-id')).not.toThrow(); + expect(() => cancelUpload('', true)).not.toThrow(); + }); + + it('covers exported validation and failure paths without network calls', async () => { + await expect(Image.compress('')).rejects.toThrow('Compression value is empty'); + await expect(getFileSize('file:///missing-file.jpg')).rejects.toBeTruthy(); + await expect(backgroundUpload('https://example.invalid/upload', imageUri, {} as never)).rejects.toBeTruthy(); + await expect(getDetails(imageUri)).rejects.toBeTruthy(); + }); + + it('generates uuid-like ids through the exported helper', () => { + expect(uuidv4()).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/); + }); +}); diff --git a/jest.config.js b/jest.config.js index 9a0cc64..7395ba1 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,4 +1,5 @@ module.exports = { preset: 'react-native', + watchman: false, modulePathIgnorePatterns: ['/examples/bare/node_modules', '/examples/expo/node_modules', '/lib/'], }; diff --git a/lefthook.yml b/lefthook.yml index 065a491..c7f2cdc 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -2,11 +2,11 @@ pre-commit: parallel: true commands: lint: - files: git diff --name-only @{push} + files: git diff --name-only --cached --diff-filter=ACMR glob: "*.{js,ts,jsx,tsx}" run: npx eslint {files} types: - files: git diff --name-only @{push} + files: git diff --name-only --cached --diff-filter=ACMR glob: "*.{js,ts, jsx, tsx}" run: npx tsc --noEmit commit-msg: diff --git a/package.json b/package.json index 39e7679..7d27da8 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,9 @@ ], "scripts": { "test": "jest", + "test:pr": "yarn test --runInBand && yarn typecheck && yarn lint", + "test:harness:android": "cd examples/bare && react-native-harness --config ./jest.harness.config.mjs --harnessRunner android --verbose", + "test:harness:ios": "cd examples/bare && react-native-harness --config ./jest.harness.config.mjs --harnessRunner ios --verbose", "typecheck": "tsc --noEmit", "lint": "eslint \"**/*.{js,ts,tsx}\"", "prepack": "bob build", @@ -88,6 +91,8 @@ "@eslint/js": "^10.0.1", "@expo/config-plugins": "^55.0.8", "@jest/globals": "^30.0.0", + "@react-native-harness/platform-android": "1.1.0", + "@react-native-harness/platform-apple": "1.1.0", "@react-native/babel-preset": "0.85.0", "@react-native/eslint-config": "0.85.0", "@react-native/jest-preset": "0.85.0", @@ -105,6 +110,7 @@ "react": "19.2.3", "react-native": "0.85.0", "react-native-builder-bob": "^0.41.0", + "react-native-harness": "1.1.0", "release-it": "^19.2.4", "turbo": "^2.8.21", "typescript": "^6.0.2" diff --git a/src/index.tsx b/src/index.tsx index 68d8925..41405b3 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -52,6 +52,7 @@ export default { getImageMetaData, getFileSize, backgroundUpload, + cancelUpload, createVideoThumbnail, clearCache, download, diff --git a/yarn.lock b/yarn.lock index fab91d1..4787926 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1981,6 +1981,27 @@ __metadata: languageName: node linkType: hard +"@clack/core@npm:1.0.0-alpha.7": + version: 1.0.0-alpha.7 + resolution: "@clack/core@npm:1.0.0-alpha.7" + dependencies: + picocolors: "npm:^1.0.0" + sisteransi: "npm:^1.0.5" + checksum: 10c0/e3d706101c57252d41c77abfb16d1f163bc9e4b81cacbc70c022a859c500ceb7024049d8ff907c8869dcaa9e141e4ef6bb57b83c46027fcdc188072f7b0cc728 + languageName: node + linkType: hard + +"@clack/prompts@npm:1.0.0-alpha.9": + version: 1.0.0-alpha.9 + resolution: "@clack/prompts@npm:1.0.0-alpha.9" + dependencies: + "@clack/core": "npm:1.0.0-alpha.7" + picocolors: "npm:^1.0.0" + sisteransi: "npm:^1.0.5" + checksum: 10c0/e3022a5cf8e07661628a46f6cc811c2f8f03fb49e5d5a1b6243f08960264f5a61fc9546a348eb2fe0e8fdfb15c4bbea78474291d06a0eb153ea9e491ae42b116 + languageName: node + linkType: hard + "@commitlint/cli@npm:^20.5.0": version: 20.5.0 resolution: "@commitlint/cli@npm:20.5.0" @@ -3435,7 +3456,7 @@ __metadata: languageName: node linkType: hard -"@jest/test-result@npm:30.3.0": +"@jest/test-result@npm:30.3.0, @jest/test-result@npm:^30.2.0": version: 30.3.0 resolution: "@jest/test-result@npm:30.3.0" dependencies: @@ -4436,6 +4457,196 @@ __metadata: languageName: node linkType: hard +"@react-native-harness/babel-preset@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/babel-preset@npm:1.1.0" + dependencies: + "@babel/plugin-transform-class-static-block": "npm:^7.27.1" + babel-plugin-istanbul: "npm:^7.0.1" + peerDependencies: + "@babel/core": ^7.22.0 + "@babel/plugin-transform-react-jsx": "*" + checksum: 10c0/19e8c5549345f47f2fba527a487130cbf275f39407254bdd6b34d7883ec4d9fd619eebc1a73f80174f1c996b017dfb187ed5939019bc5ad954504abc9d3f79b5 + languageName: node + linkType: hard + +"@react-native-harness/bridge@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/bridge@npm:1.1.0" + dependencies: + "@react-native-harness/platforms": "npm:1.1.0" + "@react-native-harness/tools": "npm:1.1.0" + birpc: "npm:^2.4.0" + pixelmatch: "npm:^7.1.0" + pngjs: "npm:^7.0.0" + ssim.js: "npm:^3.5.0" + tslib: "npm:^2.3.0" + ws: "npm:^8.18.2" + checksum: 10c0/5571e02e18e41a68a9ea931a5c811f6d0a6fd1c0ba9b3f26f6c8cb46afd22e899cc2662d69aee78885021092e6fa3dd103292f2ca9e077803b124e08f3ff8367 + languageName: node + linkType: hard + +"@react-native-harness/bundler-metro@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/bundler-metro@npm:1.1.0" + dependencies: + "@react-native-harness/babel-preset": "npm:1.1.0" + "@react-native-harness/config": "npm:1.1.0" + "@react-native-harness/runtime": "npm:1.1.0" + "@react-native-harness/tools": "npm:1.1.0" + "@react-native/metro-config": "npm:*" + connect: "npm:^3.7.0" + nocache: "npm:^4.0.0" + tslib: "npm:^2.3.0" + peerDependencies: + metro: "*" + metro-cache: "*" + metro-config: "*" + metro-resolver: "*" + checksum: 10c0/ac91d5bc20d65b731f0cc479a3fa65af7d2a308802e82e35871b8e0c997bb0941919ebed9cb48ad85408707a5caa9873d50d84799d07f6626607e43cca05528a + languageName: node + linkType: hard + +"@react-native-harness/cli@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/cli@npm:1.1.0" + dependencies: + "@react-native-harness/bridge": "npm:1.1.0" + "@react-native-harness/config": "npm:1.1.0" + "@react-native-harness/platforms": "npm:1.1.0" + "@react-native-harness/tools": "npm:1.1.0" + tslib: "npm:^2.3.0" + peerDependencies: + jest-cli: "*" + checksum: 10c0/1ad6f8da533aa6a5d887ac5f03ebf3e3d97cde173841298b8b012dd8624922c8e67fb1f673c74fc76b08cec8359d3dab5fc380799f30b51bac0260d23e308142 + languageName: node + linkType: hard + +"@react-native-harness/config@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/config@npm:1.1.0" + dependencies: + "@react-native-harness/plugins": "npm:1.1.0" + "@react-native-harness/tools": "npm:1.1.0" + tslib: "npm:^2.3.0" + zod: "npm:^3.25.67" + checksum: 10c0/3ce3b0c967687f91a61267bdb610a69af9d750d709ab04343d10d1bc691acd8aaf908fd3d7b02bd2943f870803b17fe57bf5658e6718f04b6299d17cd1aa32fa + languageName: node + linkType: hard + +"@react-native-harness/jest@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/jest@npm:1.1.0" + dependencies: + "@jest/test-result": "npm:^30.2.0" + "@react-native-harness/bridge": "npm:1.1.0" + "@react-native-harness/bundler-metro": "npm:1.1.0" + "@react-native-harness/config": "npm:1.1.0" + "@react-native-harness/platforms": "npm:1.1.0" + "@react-native-harness/plugins": "npm:1.1.0" + "@react-native-harness/tools": "npm:1.1.0" + chalk: "npm:^4.1.2" + jest-message-util: "npm:^30.2.0" + jest-util: "npm:^30.2.0" + p-limit: "npm:^7.1.1" + tslib: "npm:^2.3.0" + yargs: "npm:^17.7.2" + checksum: 10c0/3e488ba6a4d6bfd9711b9ee89d8db1b1e4b26bf5635f36c3ae4da3fe06ababf936a2981940107dd4248ea5a6ba7fea979f201652bb8e16a3d57889d933f554ef + languageName: node + linkType: hard + +"@react-native-harness/metro@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/metro@npm:1.1.0" + dependencies: + tslib: "npm:^2.3.0" + peerDependencies: + metro: "*" + checksum: 10c0/df8c7a0ff86454901f7e754c42b289091f3d2088fb5c15db1f237dcde9cd0d9d134c93af58d071c1d27df61eca0812d6961c8c9e62f89eee03640663edf0daf8 + languageName: node + linkType: hard + +"@react-native-harness/platform-android@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/platform-android@npm:1.1.0" + dependencies: + "@react-native-harness/config": "npm:1.1.0" + "@react-native-harness/platforms": "npm:1.1.0" + "@react-native-harness/tools": "npm:1.1.0" + tslib: "npm:^2.3.0" + zod: "npm:^3.25.67" + checksum: 10c0/92367499b2b5de016e3b5a33433185b6ada5ae0ebc98044c595bd349fae6390333aa64e67191e9d52ab916aee513aae90020e82b58430f1e8d5aee943a1834e3 + languageName: node + linkType: hard + +"@react-native-harness/platform-apple@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/platform-apple@npm:1.1.0" + dependencies: + "@react-native-harness/config": "npm:1.1.0" + "@react-native-harness/platforms": "npm:1.1.0" + "@react-native-harness/tools": "npm:1.1.0" + tslib: "npm:^2.3.0" + zod: "npm:^3.25.67" + checksum: 10c0/844db602ff4991b549cc70b5f29a7b3fb2c07cb3b663e2d33ff7dc804fb40d7c3924f72722bf2d7680abd880657adc021ffbdf69f465898525d003f4924e8afe + languageName: node + linkType: hard + +"@react-native-harness/platforms@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/platforms@npm:1.1.0" + dependencies: + tslib: "npm:^2.3.0" + checksum: 10c0/4b63ca5277b7ed93f937f86fabce51857da1699bb1572edf7c6b3e867db3d62146ff5fa03e5a78f0193903ab8a4e1311caffadf794076b207be62b89fcc4415d + languageName: node + linkType: hard + +"@react-native-harness/plugins@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/plugins@npm:1.1.0" + dependencies: + "@react-native-harness/bridge": "npm:1.1.0" + "@react-native-harness/platforms": "npm:1.1.0" + "@react-native-harness/tools": "npm:1.1.0" + hookable: "npm:^6.1.0" + tslib: "npm:^2.3.0" + checksum: 10c0/b1210c13f7bf4dad638abbb998d4c20ee55499af5d004a5e71ad26308dd5bdcec4f797a9b9cb03213f4b2b3b59779aa5a903790a1d405b45defea3e57d886381 + languageName: node + linkType: hard + +"@react-native-harness/runtime@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/runtime@npm:1.1.0" + dependencies: + "@react-native-harness/bridge": "npm:1.1.0" + "@vitest/expect": "npm:4.0.16" + "@vitest/spy": "npm:4.0.16" + chai: "npm:^6.2.2" + event-target-shim: "npm:^6.0.2" + react-native-url-polyfill: "npm:^3.0.0" + use-sync-external-store: "npm:^1.6.0" + zustand: "npm:^5.0.5" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/543067f68d3a6e2ab2ea82210cc5a2d8a93ead4c7fb9689208a03595433e6ec48ec5bfa6e4a03af2cbf381eb69ae1cea513506a8937c3ab6d7a5e2d9555476c6 + languageName: node + linkType: hard + +"@react-native-harness/tools@npm:1.1.0": + version: 1.1.0 + resolution: "@react-native-harness/tools@npm:1.1.0" + dependencies: + "@clack/prompts": "npm:1.0.0-alpha.9" + nano-spawn: "npm:^1.0.2" + picocolors: "npm:^1.1.1" + tslib: "npm:^2.3.0" + peerDependencies: + react-native: "*" + checksum: 10c0/b7e4ac2dd3591e5eb5816fd18c98b68f3f7c9622d27d50c74eb301ade79664d7f852745cdd93a322ce3b997d0240328390a94a847d605cbe354b98a3194fa40b + languageName: node + linkType: hard + "@react-native-masked-view/masked-view@npm:^0.3.2": version: 0.3.2 resolution: "@react-native-masked-view/masked-view@npm:0.3.2" @@ -4487,6 +4698,16 @@ __metadata: languageName: node linkType: hard +"@react-native/babel-plugin-codegen@npm:0.85.2": + version: 0.85.2 + resolution: "@react-native/babel-plugin-codegen@npm:0.85.2" + dependencies: + "@babel/traverse": "npm:^7.29.0" + "@react-native/codegen": "npm:0.85.2" + checksum: 10c0/40b04f16742bcef29107b0d519dc9fd2382cb3dc18b33bf240395a477bae8fe83a9a787e830dd136d465a7cd731473c276f47f9398c79b589262642f470cb42e + languageName: node + linkType: hard + "@react-native/babel-preset@npm:0.81.5": version: 0.81.5 resolution: "@react-native/babel-preset@npm:0.81.5" @@ -4585,6 +4806,49 @@ __metadata: languageName: node linkType: hard +"@react-native/babel-preset@npm:0.85.2": + version: 0.85.2 + resolution: "@react-native/babel-preset@npm:0.85.2" + dependencies: + "@babel/core": "npm:^7.25.2" + "@babel/plugin-proposal-export-default-from": "npm:^7.24.7" + "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" + "@babel/plugin-syntax-export-default-from": "npm:^7.24.7" + "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" + "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" + "@babel/plugin-transform-async-generator-functions": "npm:^7.25.4" + "@babel/plugin-transform-async-to-generator": "npm:^7.24.7" + "@babel/plugin-transform-block-scoping": "npm:^7.25.0" + "@babel/plugin-transform-class-properties": "npm:^7.25.4" + "@babel/plugin-transform-classes": "npm:^7.25.4" + "@babel/plugin-transform-destructuring": "npm:^7.24.8" + "@babel/plugin-transform-flow-strip-types": "npm:^7.25.2" + "@babel/plugin-transform-for-of": "npm:^7.24.7" + "@babel/plugin-transform-modules-commonjs": "npm:^7.24.8" + "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.24.7" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.24.7" + "@babel/plugin-transform-optional-catch-binding": "npm:^7.24.7" + "@babel/plugin-transform-optional-chaining": "npm:^7.24.8" + "@babel/plugin-transform-private-methods": "npm:^7.24.7" + "@babel/plugin-transform-private-property-in-object": "npm:^7.24.7" + "@babel/plugin-transform-react-display-name": "npm:^7.24.7" + "@babel/plugin-transform-react-jsx": "npm:^7.25.2" + "@babel/plugin-transform-react-jsx-self": "npm:^7.24.7" + "@babel/plugin-transform-react-jsx-source": "npm:^7.24.7" + "@babel/plugin-transform-regenerator": "npm:^7.24.7" + "@babel/plugin-transform-runtime": "npm:^7.24.7" + "@babel/plugin-transform-typescript": "npm:^7.25.2" + "@babel/plugin-transform-unicode-regex": "npm:^7.24.7" + "@react-native/babel-plugin-codegen": "npm:0.85.2" + babel-plugin-syntax-hermes-parser: "npm:0.33.3" + babel-plugin-transform-flow-enums: "npm:^0.0.2" + react-refresh: "npm:^0.14.0" + peerDependencies: + "@babel/core": "*" + checksum: 10c0/01ebc62f5ba743f485f9a123bc033e2ea5dcc5368a8d12dfb5cf552ea3a35668a8ccd79fbdb688d85aefbb3b5d383dcf8c3e8cdb11b907a4f897e1ed7bd184ea + languageName: node + linkType: hard + "@react-native/codegen@npm:0.81.5": version: 0.81.5 resolution: "@react-native/codegen@npm:0.81.5" @@ -4908,6 +5172,32 @@ __metadata: languageName: node linkType: hard +"@react-native/metro-babel-transformer@npm:0.85.2": + version: 0.85.2 + resolution: "@react-native/metro-babel-transformer@npm:0.85.2" + dependencies: + "@babel/core": "npm:^7.25.2" + "@react-native/babel-preset": "npm:0.85.2" + hermes-parser: "npm:0.33.3" + nullthrows: "npm:^1.1.1" + peerDependencies: + "@babel/core": "*" + checksum: 10c0/ac298747d5097de8a73541374eaf64246c592c28d843621c3ceaf94c3ebdd60fa70ab563d561422fc442ecf7bb72d4512cfc94456fe8226e6dfdfe4c24ac3a6a + languageName: node + linkType: hard + +"@react-native/metro-config@npm:*": + version: 0.85.2 + resolution: "@react-native/metro-config@npm:0.85.2" + dependencies: + "@react-native/js-polyfills": "npm:0.85.2" + "@react-native/metro-babel-transformer": "npm:0.85.2" + metro-config: "npm:^0.84.0" + metro-runtime: "npm:^0.84.0" + checksum: 10c0/08ad7e8108239d1c98cc197d7c904536f3c6243d0a41d8c06c9651cfe2b2a44ac6efaddb8dca2f0acb966a017cbd159705f3bfdb4edc610e109caf5d36fb5530 + languageName: node + linkType: hard + "@react-native/metro-config@npm:0.85.0": version: 0.85.0 resolution: "@react-native/metro-config@npm:0.85.0" @@ -5297,6 +5587,13 @@ __metadata: languageName: node linkType: hard +"@standard-schema/spec@npm:^1.0.0": + version: 1.1.0 + resolution: "@standard-schema/spec@npm:1.1.0" + checksum: 10c0/d90f55acde4b2deb983529c87e8025fa693de1a5e8b49ecc6eb84d1fd96328add0e03d7d551442156c7432fd78165b2c26ff561b970a9a881f046abb78d6a526 + languageName: node + linkType: hard + "@tootallnate/quickjs-emscripten@npm:^0.23.0": version: 0.23.0 resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" @@ -5409,6 +5706,23 @@ __metadata: languageName: node linkType: hard +"@types/chai@npm:^5.2.2": + version: 5.2.3 + resolution: "@types/chai@npm:5.2.3" + dependencies: + "@types/deep-eql": "npm:*" + assertion-error: "npm:^2.0.1" + checksum: 10c0/e0ef1de3b6f8045a5e473e867c8565788c444271409d155588504840ad1a53611011f85072188c2833941189400228c1745d78323dac13fcede9c2b28bacfb2f + languageName: node + linkType: hard + +"@types/deep-eql@npm:*": + version: 4.0.2 + resolution: "@types/deep-eql@npm:4.0.2" + checksum: 10c0/bf3f811843117900d7084b9d0c852da9a044d12eb40e6de73b552598a6843c21291a8a381b0532644574beecd5e3491c5ff3a0365ab86b15d59862c025384844 + languageName: node + linkType: hard + "@types/estree@npm:^1.0.6": version: 1.0.8 resolution: "@types/estree@npm:1.0.8" @@ -5857,6 +6171,46 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@npm:4.0.16": + version: 4.0.16 + resolution: "@vitest/expect@npm:4.0.16" + dependencies: + "@standard-schema/spec": "npm:^1.0.0" + "@types/chai": "npm:^5.2.2" + "@vitest/spy": "npm:4.0.16" + "@vitest/utils": "npm:4.0.16" + chai: "npm:^6.2.1" + tinyrainbow: "npm:^3.0.3" + checksum: 10c0/add4dde3548b6f65b6d7d364607713f9db258642add248a23805fa1172e48d76a7822080249efb882120b408772684569b2e78581ed3d5f282e215d7f21be183 + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:4.0.16": + version: 4.0.16 + resolution: "@vitest/pretty-format@npm:4.0.16" + dependencies: + tinyrainbow: "npm:^3.0.3" + checksum: 10c0/11243e9c2d2d011ae23825c6b7464a4385a4a4efc4ceb28b7854bb9d73491f440b89d12f62c5c9737d26375cf9585b11bc20183d4dea4e983e79d5e162407eb9 + languageName: node + linkType: hard + +"@vitest/spy@npm:4.0.16": + version: 4.0.16 + resolution: "@vitest/spy@npm:4.0.16" + checksum: 10c0/2502918e703d60ef64854d0fa83ebf94da64b80e81b80c319568feee3d86069fd46e24880a768edba06c8caba13801e44005e17a0f16d9b389486f24d539f0bf + languageName: node + linkType: hard + +"@vitest/utils@npm:4.0.16": + version: 4.0.16 + resolution: "@vitest/utils@npm:4.0.16" + dependencies: + "@vitest/pretty-format": "npm:4.0.16" + tinyrainbow: "npm:^3.0.3" + checksum: 10c0/bba35b4e102be03e106ced227809437573aa5c5f64d512301ca8de127dcb91cbedc11a2e823305f8ba82528c909c10510ec8c7e3d92b3d6d1c1aec33e143572a + languageName: node + linkType: hard + "@vscode/sudo-prompt@npm:^9.0.0": version: 9.3.2 resolution: "@vscode/sudo-prompt@npm:9.3.2" @@ -6324,6 +6678,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^2.0.1": + version: 2.0.1 + resolution: "assertion-error@npm:2.0.1" + checksum: 10c0/bbbcb117ac6480138f8c93cf7f535614282dea9dc828f540cdece85e3c665e8f78958b96afac52f29ff883c72638e6a87d469ecc9fe5bc902df03ed24a55dba8 + languageName: node + linkType: hard + "ast-types@npm:^0.13.4": version: 0.13.4 resolution: "ast-types@npm:0.13.4" @@ -6730,6 +7091,13 @@ __metadata: languageName: node linkType: hard +"birpc@npm:^2.4.0": + version: 2.9.0 + resolution: "birpc@npm:2.9.0" + checksum: 10c0/2462d0d67061f95bae213b0b9b323a6643ff749f7457a25242897c99e31355f1bd522c17f83ecf57506351e3e28b4e38c12a39b8beddee2dd0cbf78f9b9876ce + languageName: node + linkType: hard + "bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" @@ -7067,6 +7435,13 @@ __metadata: languageName: node linkType: hard +"chai@npm:^6.2.1, chai@npm:^6.2.2": + version: 6.2.2 + resolution: "chai@npm:6.2.2" + checksum: 10c0/e6c69e5f0c11dffe6ea13d0290936ebb68fcc1ad688b8e952e131df6a6d5797d5e860bc55cef1aca2e950c3e1f96daf79e9d5a70fb7dbaab4e46355e2635ed53 + languageName: node + linkType: hard + "chalk@npm:^2.0.0, chalk@npm:^2.0.1, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -9081,6 +9456,13 @@ __metadata: languageName: node linkType: hard +"event-target-shim@npm:^6.0.2": + version: 6.0.2 + resolution: "event-target-shim@npm:6.0.2" + checksum: 10c0/e40b89effb1bd0fdd44dd198e3b61669c2bff89b680a8156c50480b5df8d4364208d1db09fbe823211005651558415691cb3ff72c22618eb154e55282b8b0481 + languageName: node + linkType: hard + "execa@npm:^4.0.3": version: 4.1.0 resolution: "execa@npm:4.1.0" @@ -10467,6 +10849,13 @@ __metadata: languageName: node linkType: hard +"hookable@npm:^6.1.0": + version: 6.1.1 + resolution: "hookable@npm:6.1.1" + checksum: 10c0/bb46cd9ffc0a997af21febd97835da4e59a6989adec73dc3c215fcc44c7ac01de4781f251c3d420bf45862d2592a695f5f0de095b6f5df52db8afb5166938212 + languageName: node + linkType: hard + "hosted-git-info@npm:^7.0.0": version: 7.0.2 resolution: "hosted-git-info@npm:7.0.2" @@ -11760,7 +12149,7 @@ __metadata: languageName: node linkType: hard -"jest-message-util@npm:30.3.0": +"jest-message-util@npm:30.3.0, jest-message-util@npm:^30.2.0": version: 30.3.0 resolution: "jest-message-util@npm:30.3.0" dependencies: @@ -11957,7 +12346,7 @@ __metadata: languageName: node linkType: hard -"jest-util@npm:30.3.0": +"jest-util@npm:30.3.0, jest-util@npm:^30.2.0": version: 30.3.0 resolution: "jest-util@npm:30.3.0" dependencies: @@ -13962,6 +14351,13 @@ __metadata: languageName: node linkType: hard +"nano-spawn@npm:^1.0.2": + version: 1.0.3 + resolution: "nano-spawn@npm:1.0.3" + checksum: 10c0/ea18857e493710a50ded333dd71677953bd9bd9e6a17ade74af957763c50a9a02205ef31bc0d6784f5b3ad82db3d9f47531e9baac2acf01118f9b7c35bd9d5de + languageName: node + linkType: hard + "nanoid@npm:^3.3.11, nanoid@npm:^3.3.7, nanoid@npm:^3.3.8": version: 3.3.11 resolution: "nanoid@npm:3.3.11" @@ -14052,6 +14448,13 @@ __metadata: languageName: node linkType: hard +"nocache@npm:^4.0.0": + version: 4.0.0 + resolution: "nocache@npm:4.0.0" + checksum: 10c0/b06d66d3906ed7d02e262f3c357e0305d7775c12825014a109e5e7d1e92975aee44aed1aedf8bad6c1d7f22488f1ffabf9e4a253f3636435703abd7bfa26de6b + languageName: node + linkType: hard + "node-exports-info@npm:^1.6.0": version: 1.6.0 resolution: "node-exports-info@npm:1.6.0" @@ -14583,6 +14986,15 @@ __metadata: languageName: node linkType: hard +"p-limit@npm:^7.1.1": + version: 7.3.0 + resolution: "p-limit@npm:7.3.0" + dependencies: + yocto-queue: "npm:^1.2.1" + checksum: 10c0/8030f0ccc67d80d9c12f2b4ed277c5ede151f80a09cb1b143ab0ee597940784f2cf6e07e92d54c023fe9836784329750c25c6cd4488a7a326c0ae0ec2efca982 + languageName: node + linkType: hard + "p-locate@npm:^4.1.0": version: 4.1.0 resolution: "p-locate@npm:4.1.0" @@ -14845,6 +15257,17 @@ __metadata: languageName: node linkType: hard +"pixelmatch@npm:^7.1.0": + version: 7.2.0 + resolution: "pixelmatch@npm:7.2.0" + dependencies: + pngjs: "npm:^7.0.0" + bin: + pixelmatch: bin/pixelmatch + checksum: 10c0/d8a2454ecf3a977738d6050ed087effa0a9f1b53a0da92d37381a74bb04db5c21e14cfcd41965fbdac4a5967aa5ff2525e2afe9826a77eae043b8339dafc53b6 + languageName: node + linkType: hard + "pkg-dir@npm:^4.2.0": version: 4.2.0 resolution: "pkg-dir@npm:4.2.0" @@ -14883,6 +15306,13 @@ __metadata: languageName: node linkType: hard +"pngjs@npm:^7.0.0": + version: 7.0.0 + resolution: "pngjs@npm:7.0.0" + checksum: 10c0/0d4c7a0fd476a9c33df7d0a2a73e1d56537628a668841f6995c2bca070cf30819f9254a64363266bc14ef2fee47659dd3b4f2b18eec7ab65143015139f497b38 + languageName: node + linkType: hard + "possible-typed-array-names@npm:^1.0.0": version: 1.1.0 resolution: "possible-typed-array-names@npm:1.1.0" @@ -15374,6 +15804,8 @@ __metadata: "@eslint/js": "npm:^10.0.1" "@expo/config-plugins": "npm:^55.0.8" "@jest/globals": "npm:^30.0.0" + "@react-native-harness/platform-android": "npm:1.1.0" + "@react-native-harness/platform-apple": "npm:1.1.0" "@react-native/babel-preset": "npm:0.85.0" "@react-native/eslint-config": "npm:0.85.0" "@react-native/jest-preset": "npm:0.85.0" @@ -15391,6 +15823,7 @@ __metadata: react: "npm:19.2.3" react-native: "npm:0.85.0" react-native-builder-bob: "npm:^0.41.0" + react-native-harness: "npm:1.1.0" release-it: "npm:^19.2.4" turbo: "npm:^2.8.21" typescript: "npm:^6.0.2" @@ -15422,6 +15855,23 @@ __metadata: languageName: node linkType: hard +"react-native-harness@npm:1.1.0": + version: 1.1.0 + resolution: "react-native-harness@npm:1.1.0" + dependencies: + "@react-native-harness/babel-preset": "npm:1.1.0" + "@react-native-harness/cli": "npm:1.1.0" + "@react-native-harness/jest": "npm:1.1.0" + "@react-native-harness/metro": "npm:1.1.0" + "@react-native-harness/runtime": "npm:1.1.0" + tslib: "npm:^2.3.0" + bin: + harness: bin.js + react-native-harness: bin.js + checksum: 10c0/1bb9c80b8ad59d7aab56852741990028d8b283ffb76e00c4428312e23b584463074003de08bef39fa1a406338577d504a8d4f7228d80cdd72bb70053edfd79d3 + languageName: node + linkType: hard + "react-native-image-picker@npm:^8.2.1": version: 8.2.1 resolution: "react-native-image-picker@npm:8.2.1" @@ -15538,6 +15988,17 @@ __metadata: languageName: node linkType: hard +"react-native-url-polyfill@npm:^3.0.0": + version: 3.0.0 + resolution: "react-native-url-polyfill@npm:3.0.0" + dependencies: + whatwg-url-without-unicode: "npm:8.0.0-3" + peerDependencies: + react-native: "*" + checksum: 10c0/a1e539c2a28dc48125ada8bf29f3536ee2c149e4a5e3d205858755783afafe7f871ce1de8b66cb1c4cc05e15e212c74c49e93ddde856cda63fcf660cf943522a + languageName: node + linkType: hard + "react-native-video@npm:6.19.1": version: 6.19.1 resolution: "react-native-video@npm:6.19.1" @@ -16838,6 +17299,13 @@ __metadata: languageName: node linkType: hard +"ssim.js@npm:^3.5.0": + version: 3.5.0 + resolution: "ssim.js@npm:3.5.0" + checksum: 10c0/9e7101da17395d3acbd417aac712d8f156522e79059a27cb38882eedd5a8868e31871c8f58bec3a150f8cf0660883cf22310cbd2fd63b408c1fd0ab02fda9fbc + languageName: node + linkType: hard + "ssri@npm:^10.0.0": version: 10.0.6 resolution: "ssri@npm:10.0.6" @@ -17386,6 +17854,13 @@ __metadata: languageName: node linkType: hard +"tinyrainbow@npm:^3.0.3": + version: 3.1.0 + resolution: "tinyrainbow@npm:3.1.0" + checksum: 10c0/f11cf387a26c5c9255bec141a90ac511b26172981b10c3e50053bc6700ea7d2336edcc4a3a21dbb8412fe7c013477d2ba4d7e4877800f3f8107be5105aad6511 + languageName: node + linkType: hard + "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -17465,7 +17940,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.4.0": +"tslib@npm:^2.0.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 @@ -17947,7 +18422,7 @@ __metadata: languageName: node linkType: hard -"use-sync-external-store@npm:^1.5.0": +"use-sync-external-store@npm:^1.5.0, use-sync-external-store@npm:^1.6.0": version: 1.6.0 resolution: "use-sync-external-store@npm:1.6.0" peerDependencies: @@ -18370,7 +18845,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.12.1": +"ws@npm:^8.12.1, ws@npm:^8.18.2": version: 8.20.0 resolution: "ws@npm:8.20.0" peerDependencies: @@ -18558,6 +19033,13 @@ __metadata: languageName: node linkType: hard +"yocto-queue@npm:^1.2.1": + version: 1.2.2 + resolution: "yocto-queue@npm:1.2.2" + checksum: 10c0/36d4793e9cf7060f9da543baf67c55e354f4862c8d3d34de1a1b1d7c382d44171315cc54abf84d8900b8113d742b830108a1434f4898fb244f9b7e8426d4b8f5 + languageName: node + linkType: hard + "yoctocolors-cjs@npm:^2.1.3": version: 2.1.3 resolution: "yoctocolors-cjs@npm:2.1.3" @@ -18587,3 +19069,31 @@ __metadata: checksum: 10c0/860d25a81ab41d33aa25f8d0d07b091a04acb426e605f396227a796e9e800c44723ed96d0f53a512b57be3d1520f45bf69c0cb3b378a232a00787a2609625307 languageName: node linkType: hard + +"zod@npm:^3.25.67": + version: 3.25.76 + resolution: "zod@npm:3.25.76" + checksum: 10c0/5718ec35e3c40b600316c5b4c5e4976f7fee68151bc8f8d90ec18a469be9571f072e1bbaace10f1e85cf8892ea12d90821b200e980ab46916a6166a4260a983c + languageName: node + linkType: hard + +"zustand@npm:^5.0.5": + version: 5.0.12 + resolution: "zustand@npm:5.0.12" + peerDependencies: + "@types/react": ">=18.0.0" + immer: ">=9.0.6" + react: ">=18.0.0" + use-sync-external-store: ">=1.2.0" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + checksum: 10c0/304c1dfb6033d758ddc7606c15df8566e6cace83ee4eeec721e8975f7813fd4cca2c68ff6b3eaa1841a9e53f0cf8486007217479785eccb845e3596de4df7f1e + languageName: node + linkType: hard