diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index e3922c399..000000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -src/plugins/types/filterTypes.ts -src/screens/reader/components/ReaderBottomSheet/ReaderValueChange.tsx -# These two files cause the @typescript-eslint/no-unused-vars rule to fail diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index a39a0051c..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = { - root: true, - extends: ['@react-native'], - overrides: [ - { - // Test files only - plugins: ['jest'], - files: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], - extends: ['plugin:testing-library/react', 'plugin:jest/recommended'], - }, - { - files: ['*.js', '*.jsx', '*.ts', '*.tsx'], - rules: { - 'no-shadow': 'off', - 'no-undef': 'off', - 'no-console': 'error', - '@typescript-eslint/no-shadow': 'warn', - 'react-hooks/exhaustive-deps': 'warn', - 'curly': ['error', 'multi-line', 'consistent'], - 'no-useless-return': 'error', - 'block-scoped-var': 'error', - 'no-var': 'error', - 'prefer-const': 'error', - 'no-dupe-else-if': 'error', - 'no-duplicate-imports': 'error', - '@react-native/no-deep-imports': 0, - }, - }, - ], -}; diff --git a/.github/ISSUE_TEMPLATE/report_issue.yml b/.github/ISSUE_TEMPLATE/report_issue.yml index 6bde773fa..a9008bbed 100644 --- a/.github/ISSUE_TEMPLATE/report_issue.yml +++ b/.github/ISSUE_TEMPLATE/report_issue.yml @@ -45,7 +45,7 @@ body: label: LNReader version description: You can find your LNReader version in **More → About**. placeholder: | - Example: "2.0.3" + Example: "2.1.2" validations: required: true @@ -88,7 +88,7 @@ body: required: true - label: If this is an issue with a plugin, I should be opening an issue in the [plugins repository](https://github.com/lnreader/lnreader-plugins/issues/new/choose). required: true - - label: I have updated the app to version **[2.0.3](https://github.com/lnreader/lnreader/releases/latest)**. + - label: I have updated the app to version **[2.1.2](https://github.com/lnreader/lnreader/releases/latest)**. required: true - label: I will fill out all of the requested information in this form. required: true diff --git a/.github/ISSUE_TEMPLATE/request_feature.yml b/.github/ISSUE_TEMPLATE/request_feature.yml index 888c2aa26..7047645b5 100644 --- a/.github/ISSUE_TEMPLATE/request_feature.yml +++ b/.github/ISSUE_TEMPLATE/request_feature.yml @@ -34,7 +34,7 @@ body: required: true - label: If this is an issue with a source, I should be opening an issue in the [sources repository](https://github.com/lnreader/lnreader-sources/issues/new/choose). required: true - - label: I have updated the app to version **[2.0.3](https://github.com/lnreader/lnreader/releases/latest)**. + - label: I have updated the app to version **[2.1.2](https://github.com/lnreader/lnreader/releases/latest)**. required: true - label: I will fill out all of the requested information in this form. required: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd13ed7c4..4c9ae11ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,94 +1,208 @@ -name: Build +name: Build Preview + on: - push: - branches: - - master + schedule: + - cron: '30 0 * * *' workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: + actions: read + contents: read + +env: + CI: 'true' + EXPO_NO_TELEMETRY: '1' + NODE_ENV: 'production' + jobs: + check-preview: + name: Check Preview + runs-on: ubuntu-24.04 + outputs: + should-build: ${{ steps.preview.outputs.should-build }} + + steps: + - name: Check For Existing Preview + id: preview + uses: actions/github-script@v8 + with: + script: | + if (context.eventName === 'workflow_dispatch') { + core.notice('Manual dispatch requested; building a new preview.') + core.setOutput('should-build', 'true') + return + } + + const shortSha = context.sha.slice(0, 8) + const artifactName = `LNReader-${shortSha}` + const { data: { artifacts } } = await github.rest.actions.listArtifactsForRepo( + { + owner: context.repo.owner, + repo: context.repo.repo, + name: artifactName, + per_page: 100, + }, + ) + const previewExists = artifacts.some( + artifact => + !artifact.expired && + artifact.workflow_run?.head_sha === context.sha, + ) + + core.notice( + previewExists + ? `A preview artifact already exists for ${shortSha}.` + : `No preview artifact exists for ${shortSha}; starting a build.`, + ) + core.setOutput('should-build', String(!previewExists)) + build-android: - name: Build App - runs-on: ubuntu-latest + name: APK + needs: check-preview + if: needs.check-preview.outputs.should-build == 'true' + runs-on: ubuntu-24.04 timeout-minutes: 60 + steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 0 + persist-credentials: false + + - name: Set Up pnpm + uses: pnpm/action-setup@v6 - - name: Setup Node.js - uses: actions/setup-node@v4 + - name: Set Up Node.js + uses: actions/setup-node@v7 with: - node-version: '20' + node-version: '22' + cache: pnpm + cache-dependency-path: pnpm-lock.yaml - - name: Setup pnpm - uses: pnpm/action-setup@v4 + - name: Set Up Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: '17' - - name: Get pnpm Store Directory - shell: bash + - name: Install Dependencies + run: pnpm install --frozen-lockfile --prefer-offline + + - name: Configure Release Environment + env: + MYANIMELIST_CLIENT_ID: ${{ vars.MYANIMELIST_CLIENT_ID }} + ANILIST_CLIENT_ID: ${{ vars.ANILIST_CLIENT_ID }} run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + pnpm generate:env:release \ + --build-type "GitHub Action" \ + --node-env "production" - - name: Setup pnpm Cache - uses: actions/cache@v4 - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + - name: Generate Native Android Project + run: | + pnpm exec expo prebuild \ + --platform android \ + --clean \ + --no-install \ + --non-interactive - - name: Setup Java - uses: actions/setup-java@v4 + - name: Configure Gradle + uses: gradle/actions/setup-gradle@v6 with: - distribution: 'zulu' - java-version: '17' + cache-provider: enhanced + cache-read-only: >- + ${{ github.ref_name != github.event.repository.default_branch }} + cache-cleanup: on-success + add-job-summary: always - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - with: - cache-read-only: false + - name: Generate Preview Metadata + run: | + short_sha="${GITHUB_SHA::8}" + base_version_name="$(node --print "require('./app.json').expo.version")" + base_version_code="$(node --print "require('./app.json').expo.android.versionCode")" - - name: Install Dependencies - run: pnpm install --frozen-lockfile + echo "PRERELEASE_VERSION_CODE=$((base_version_code + GITHUB_RUN_NUMBER))" >> "$GITHUB_ENV" + echo "PRERELEASE_VERSION_NAME=${base_version_name}-pre.r${GITHUB_RUN_NUMBER}.${short_sha}" >> "$GITHUB_ENV" + echo "PRERELEASE_ARTIFACT_NAME=LNReader-${short_sha}" >> "$GITHUB_ENV" + echo "PRERELEASE_APK_NAME=LNReader-r${GITHUB_RUN_NUMBER}-${short_sha}.apk" >> "$GITHUB_ENV" - - name: Create Environment File - run: | - cat > .env << EOF - MYANIMELIST_CLIENT_ID=${{ vars.MYANIMELIST_CLIENT_ID }} - ANILIST_CLIENT_ID=${{ vars.ANILIST_CLIENT_ID }} - GIT_HASH=$(git rev-parse --short HEAD) - RELEASE_DATE=$(date --utc +'%d/%m/%y %I:%M %p %Z') - BUILD_TYPE=Github Action - EOF - - - name: Make Gradlew Executable - run: cd android && chmod +x ./gradlew - - - name: Set Environment Variables + - name: Build Preview APK + working-directory: android run: | - set -x - echo "COMMIT_COUNT=$(git rev-list --count HEAD)" >> $GITHUB_ENV - echo "COMMIT_ID=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + mkdir -p app/build/intermediates/sourcemaps/react/preRelease - - name: Build Android Release + ./gradlew :app:assemblePreRelease \ + --build-cache \ + --parallel \ + -PpreReleaseVersionCode="$PRERELEASE_VERSION_CODE" \ + -PpreReleaseVersionName="$PRERELEASE_VERSION_NAME" \ + --stacktrace + + mv \ + app/build/outputs/apk/preRelease/app-preRelease.apk \ + "app/build/outputs/apk/preRelease/$PRERELEASE_APK_NAME" + + - name: Upload Preview Sourcemap + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PRERELEASE_ARTIFACT_NAME }}-sourcemap + path: android/app/build/generated/sourcemaps/react/preRelease/index.android.bundle.map + if-no-files-found: error + retention-days: 14 + compression-level: 0 + - name: Upload Preview APK + id: upload-nightly + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PRERELEASE_ARTIFACT_NAME }} + path: android/app/build/outputs/apk/preRelease/${{ env.PRERELEASE_APK_NAME }} + if-no-files-found: error + retention-days: 14 + compression-level: 0 + + - name: Check Discord Notification + id: discord env: - COMMIT_COUNT: ${{ env.COMMIT_COUNT }} - COMMIT_ID: ${{ env.COMMIT_ID }} + DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_NIGHTLY_WEBHOOK }} run: | - sed -i 's/lnreader/lnreader-r${{ env.COMMIT_COUNT }}(${{ env.COMMIT_ID }})/g' android/app/src/main/res/values/strings.xml - cd android && ./gradlew assembleRelease -PcustomAppId=com.rajarsheechatterjee.LNReader.commit_${{ env.COMMIT_ID }} --build-cache - mv app/build/outputs/apk/release/app-release.apk app/build/outputs/apk/release/LNReader-r${{ env.COMMIT_COUNT }}-${{ env.COMMIT_ID }}.apk + if [ -n "$DISCORD_WEBHOOK_URL" ]; then + echo "configured=true" >> "$GITHUB_OUTPUT" + else + echo "configured=false" >> "$GITHUB_OUTPUT" + fi - - name: Upload Release Artifact + - name: Notify Discord + if: steps.discord.outputs.configured == 'true' env: - COMMIT_COUNT: ${{ env.COMMIT_COUNT }} - COMMIT_ID: ${{ env.COMMIT_ID }} - uses: actions/upload-artifact@v4 - with: - name: LNReader-r${{ env.COMMIT_COUNT }}-${{ env.COMMIT_ID }} - path: android/app/build/outputs/apk/release/LNReader-r${{ env.COMMIT_COUNT }}-${{ env.COMMIT_ID }}.apk - retention-days: 30 + DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_NIGHTLY_WEBHOOK }} + ARTIFACT_URL: ${{ steps.upload-nightly.outputs.artifact-url }} + run: | + short_sha="${GITHUB_SHA::8}" + message="[${GITHUB_REPOSITORY}] New nightly build published: \`${short_sha}\`" + + payload=$(jq -n \ + --arg title "$message" \ + --arg url "$ARTIFACT_URL" \ + '{ + username: "GitHub", + avatar_url: "https://github.com/github.png", + embeds: [{ + author: { + name: "github-actions[bot]", + icon_url: "https://github.com/github.png" + }, + title: $title, + url: $url, + color: 3092790 + }] + }') + + curl --fail-with-body \ + --request POST \ + --header "Content-Type: application/json" \ + --data "$payload" \ + "$DISCORD_WEBHOOK_URL" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 90290ee3d..21188e863 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '24' - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -44,5 +44,8 @@ jobs: - name: Install Dependencies run: pnpm install --frozen-lockfile + - name: Generate Environment Variables + run: pnpm generate:env:debug + - name: Run ESLint run: pnpm run lint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..42c314c8a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,232 @@ +name: Release Android +run-name: Draft ${{ inputs.bump }} Android release + +on: + workflow_dispatch: + inputs: + bump: + description: Version component to increment + required: true + default: Patch + type: choice + options: + - Patch + - Minor + - Major + +concurrency: + group: release-android + cancel-in-progress: false + +permissions: + contents: write + +env: + CI: 'true' + EXPO_NO_TELEMETRY: '1' + NODE_ENV: 'production' + RELEASE_BUMP: ${{ inputs.bump }} + +jobs: + release-android: + name: Build Draft Release + if: >- + github.ref == 'refs/heads/master' && + github.actor_id == vars.RELEASE_ACTOR_ID && + github.triggering_actor == vars.RELEASE_ACTOR_LOGIN + environment: release + runs-on: ubuntu-24.04 + timeout-minutes: 90 + + steps: + - name: Checkout Repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 + persist-credentials: true + + - name: Set Up pnpm + uses: pnpm/action-setup@v6 + + - name: Set Up Node.js + uses: actions/setup-node@v7 + with: + node-version: '22' + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Set Up Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: '17' + + - name: Install Dependencies + run: pnpm install --frozen-lockfile --prefer-offline + + - name: Resolve Release Version + id: version + run: | + RELEASE_VERSION="$( + node scripts/resolve-release-version.cjs "$RELEASE_BUMP" + )" + + echo "Releasing $RELEASE_BUMP version: $RELEASE_VERSION" + echo "RELEASE_VERSION=$RELEASE_VERSION" >> "$GITHUB_ENV" + echo "version=$RELEASE_VERSION" >> "$GITHUB_OUTPUT" + + - name: Validate Release + env: + GH_TOKEN: ${{ github.token }} + run: | + if ! [[ "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Version must use stable semver, for example 2.0.4" + exit 1 + fi + + if git rev-parse --verify --quiet "refs/tags/v$RELEASE_VERSION"; then + echo "Tag v$RELEASE_VERSION already exists" + exit 1 + fi + + if gh release view "v$RELEASE_VERSION" >/dev/null 2>&1; then + echo "Release v$RELEASE_VERSION already exists" + exit 1 + fi + + - name: Prepare Release Commit + run: | + pnpm release:prepare "$RELEASE_VERSION" + + if git diff --quiet -- \ + package.json \ + app.json \ + .github/ISSUE_TEMPLATE/report_issue.yml \ + .github/ISSUE_TEMPLATE/request_feature.yml; then + CURRENT_VERSION="$(node -p "require('./package.json').version")" + if [ "$CURRENT_VERSION" != "$RELEASE_VERSION" ]; then + echo "No release changes were generated for $RELEASE_VERSION" + exit 1 + fi + echo "Release version $RELEASE_VERSION is already committed; resuming" + else + git diff --check + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add \ + package.json \ + app.json \ + .github/ISSUE_TEMPLATE/report_issue.yml \ + .github/ISSUE_TEMPLATE/request_feature.yml + git commit -m "chore: Release v$RELEASE_VERSION" + fi + + git fetch origin master + REMOTE_VERSION="$( + node -e " + const { execFileSync } = require('child_process'); + const contents = execFileSync( + 'git', + ['show', 'origin/master:package.json'], + { encoding: 'utf8' }, + ); + process.stdout.write(JSON.parse(contents).version); + " + )" + + if [ "$REMOTE_VERSION" = "$RELEASE_VERSION" ]; then + echo "Release commit is already on master; resuming from it" + git checkout --detach origin/master + echo "RELEASE_ALREADY_PUSHED=true" >> "$GITHUB_ENV" + else + echo "RELEASE_ALREADY_PUSHED=false" >> "$GITHUB_ENV" + fi + + echo "RELEASE_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV" + + - name: Configure Release Environment + env: + MYANIMELIST_CLIENT_ID: ${{ vars.MYANIMELIST_CLIENT_ID }} + ANILIST_CLIENT_ID: ${{ vars.ANILIST_CLIENT_ID }} + run: | + pnpm generate:env:release \ + --build-type "Release" \ + --node-env "production" \ + --myanimelist-client-id "$MYANIMELIST_CLIENT_ID" \ + --anilist-client-id "$ANILIST_CLIENT_ID" + + - name: Generate Native Android Project + run: | + pnpm exec expo prebuild \ + --platform android \ + --clean \ + --no-install + + - name: Configure Gradle + uses: gradle/actions/setup-gradle@v6 + with: + cache-provider: enhanced + cache-cleanup: on-success + add-job-summary: always + + - name: Build Release APKs + working-directory: android + run: | + mkdir -p app/build/intermediates/sourcemaps/react/release + + ./gradlew :app:assembleRelease \ + --build-cache \ + --parallel \ + -PreleaseAbiSplits=true \ + --stacktrace + + - name: Package Release APKs + run: pnpm release:package "$RELEASE_VERSION" + + - name: Upload Sourcemaps + run: | + cp android/app/build/generated/sourcemaps/react/release/index.android.bundle.map \ + "release-artifacts/LNReader-v${RELEASE_VERSION}-sourcemap.map" + + - name: Verify APK Signatures + run: | + APKSIGNER="$(find "$ANDROID_HOME/build-tools" -type f -name apksigner | + sort -V | + tail -1)" + + if [ -z "$APKSIGNER" ]; then + echo "Android apksigner was not found" + exit 1 + fi + + for APK in release-artifacts/*.apk; do + "$APKSIGNER" verify "$APK" + done + + - name: Upload Release APKs + uses: actions/upload-artifact@v4 + with: + name: LNReader-v${{ steps.version.outputs.version }} + path: release-artifacts/ + if-no-files-found: error + retention-days: 30 + compression-level: 0 + + - name: Push Release Commit + run: | + if [ "$RELEASE_ALREADY_PUSHED" = "true" ]; then + echo "Release commit is already on master" + else + git push origin HEAD:master + fi + + - name: Create Draft GitHub Release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release create "v$RELEASE_VERSION" \ + release-artifacts/* \ + --target "$RELEASE_SHA" \ + --title "LNReader v$RELEASE_VERSION" \ + --generate-notes \ + --draft diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 65d3c4b4a..94bcae4ea 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -1,4 +1,4 @@ -name: Testing +name: Tests on: push: branches: @@ -12,28 +12,28 @@ concurrency: cancel-in-progress: true jobs: - build: - name: Test + test: + name: Jest runs-on: ubuntu-latest timeout-minutes: 10 steps: - - name: Checkout Repository + - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '24' - name: Setup pnpm uses: pnpm/action-setup@v4 - - name: Get pnpm Store Directory + - name: Get pnpm Store Path shell: bash run: | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - name: Setup pnpm Cache + - name: Cache pnpm uses: actions/cache@v4 with: path: ${{ env.STORE_PATH }} @@ -44,5 +44,8 @@ jobs: - name: Install Dependencies run: pnpm install --frozen-lockfile + - name: Generate Environment + run: pnpm generate:env:debug + - name: Run Tests run: pnpm run test diff --git a/.github/workflows/types.yml b/.github/workflows/types.yml index ae3e49bd0..9131b2b5b 100644 --- a/.github/workflows/types.yml +++ b/.github/workflows/types.yml @@ -1,36 +1,39 @@ -# Temporarily Disabled name: Type Check on: - workflow_dispatch: - # push: - # branches: - # - master - # pull_request: - # branches: - # - master + push: + branches: + - master + pull_request: + branches: + - master + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: - build: - name: Type Checking + type-check: + name: TypeScript runs-on: ubuntu-latest + timeout-minutes: 10 steps: - - name: Checkout Repository + - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '24' - name: Setup pnpm uses: pnpm/action-setup@v4 - - name: Get pnpm Store Directory + - name: Get pnpm Store Path shell: bash run: | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - name: Setup pnpm Cache + - name: Cache pnpm uses: actions/cache@v4 with: path: ${{ env.STORE_PATH }} @@ -41,5 +44,8 @@ jobs: - name: Install Dependencies run: pnpm install --frozen-lockfile + - name: Generate Environment + run: pnpm generate:env:debug + - name: Check Types run: pnpm run type-check diff --git a/.gitignore b/.gitignore index b58ccbf0a..92b5b38c5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,8 @@ npm-debug.* *.mobileprovision *.orig.* web-build/ -.env +.*env +.env.local reader_playground/index.html # macOS .DS_Store @@ -89,8 +90,27 @@ flake.lock # pnpm .pnpm-store +src/generated/**/* + .cursor/ .agents/ .claude/ .jj/ .sisyphus/ + +# Rock +.rock/ + +# TypeScript +*.tsbuildinfo + +*/**/org.eclipse.buildship.core.prefs +*/**/org.eclipse.jdt.core.prefs +*/**/.classpath +*/**/.project +android/ +release-artifacts/ +!modules/**/android/ +!plugins/android/ +ios/ +!modules/**/ios/ diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 000000000..2ca6b9dae --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +pnpm run type-check diff --git a/App.tsx b/App.tsx index 0fcfb58fe..df4f9a1b1 100644 --- a/App.tsx +++ b/App.tsx @@ -1,15 +1,15 @@ import 'react-native-url-polyfill/auto'; import { enableFreeze } from 'react-native-screens'; - -enableFreeze(true); - -import React, { Suspense, useEffect } from 'react'; +import { PropsWithChildren, Suspense, useEffect, useMemo } from 'react'; import { StatusBar, StyleSheet } from 'react-native'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; -import LottieSplashScreen from 'react-native-lottie-splash-screen'; +import * as SplashScreen from 'expo-splash-screen'; import { SafeAreaProvider } from 'react-native-safe-area-context'; -import { Provider as PaperProvider } from 'react-native-paper'; -import * as Notifications from 'expo-notifications'; +import { + MD3DarkTheme, + MD3LightTheme, + Provider as PaperProvider, +} from 'react-native-paper'; import AppErrorBoundary, { ErrorFallback, @@ -18,46 +18,93 @@ import AppErrorBoundary, { import Main from './src/navigators/Main'; import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; import { useInitDatabase } from '@database/db'; +import { useInitializeAppServices } from '@hooks/common/useInitializeAppServices'; +import { opSqliteAdapter } from './src/rozenite/opSqliteAdapter'; +import { useRozeniteSqlitePlugin } from '@rozenite/sqlite-plugin'; +import { ThemeProvider, useTheme } from '@hooks/persisted/useTheme'; + +enableFreeze(true); +const sqliteAdapters = __DEV__ && opSqliteAdapter ? [opSqliteAdapter] : []; + +/** + * The Android window background is resolved from the system light/dark setting, + * so it is bright white whenever the system is light – regardless of the theme + * picked inside the app. Nothing between it and the navigators paints a + * background of its own, so every frame in which no screen is drawn (mounting a + * nested navigator, freezing the screen being left) shows through as a flash. + * Painting the root view keeps the window covered at all times. + */ +const ThemedRootView = ({ children }: PropsWithChildren) => { + const theme = useTheme(); + + return ( + + {children} + + ); +}; + +const ThemedPaperProvider = ({ children }: PropsWithChildren) => { + const theme = useTheme(); + const paperTheme = useMemo(() => { + const baseTheme = theme.isDark ? MD3DarkTheme : MD3LightTheme; -Notifications.setNotificationHandler({ - handleNotification: async () => { return { - shouldPlaySound: false, - shouldSetBadge: false, - shouldShowBanner: true, - shouldShowList: true, + ...baseTheme, + colors: { + ...baseTheme.colors, + ...theme, + }, }; - }, -}); + }, [theme]); + return {children}; +}; const App = () => { - const state = useInitDatabase(); + useRozeniteSqlitePlugin({ adapters: sqliteAdapters }); + const { success: databaseReady, error: databaseError } = useInitDatabase(); + const { ready: servicesReady, error: servicesError } = + useInitializeAppServices(Boolean(databaseReady)); useEffect(() => { - if (state.success || state.error) { - LottieSplashScreen.hide(); + if ((databaseReady && servicesReady) || databaseError || servicesError) { + SplashScreen.hideAsync(); } - }, [state.success, state.error]); + }, [databaseReady, databaseError, servicesReady, servicesError]); + + const initializationError = databaseError || servicesError; + + if (initializationError) { + return ( + + null} /> + + ); + } - if (state.error) { - return null} />; + if (!databaseReady || !servicesReady) { + return null; } return ( - - - - - - -
- - - - - + + + + + + + +
+ + + + + + ); }; diff --git a/CONTRIBUTING-NIX.md b/CONTRIBUTING-NIX.md index 8d6d55843..b0973dc32 100644 --- a/CONTRIBUTING-NIX.md +++ b/CONTRIBUTING-NIX.md @@ -6,7 +6,7 @@ This document outlines how to set up your React Native development environment u This was tested on an Arch Linux system with AMD CPU and GPU. There are likely problems with NVIDIA GPUs. -Compatibility with Microsoft WSL2 is not guaranteed, but should work withoud major issues, except for the emulator. +Compatibility with Microsoft WSL2 is not guaranteed, but should work without major issues, except for the emulator. ## Prerequisites diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b4dfd384..1fe7b41ed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,14 +2,14 @@ Contributions are welcome and are greatly appreciated! -## Setup your environment with nix +## Setup Your Environment with Nix If you are on a Linux system, you can install the nix package manager and use the nix flakes to set up your development environment. -See [CONTRIBUTING-NIX.md](CONTRIBUTING-NIX.md) +See [CONTRIBUTING-NIX.md](CONTRIBUTING-NIX.md). -## Setting up your environment +## Setting Up Your Environment -After forking to your own github org or account, do the following steps to get started: +After forking to your own GitHub organization or account, take the following steps to get started: ```bash # prerequisites @@ -35,7 +35,7 @@ pnpm run build:release:android ### Developing on Android -You will need an Android device or emulator connected to your computer as well as an IDE of your choice. (eg: vscode) +You will need an Android device or emulator connected to your computer as well as an IDE of your choice (e.g., VS Code). ```bash # prerequisites @@ -57,7 +57,7 @@ pnpm run dev:android This codebase's linting rules are enforced using [ESLint](http://eslint.org/). It is recommended that you install an eslint plugin for your editor of choice when working on this -codebase, however you can always check to see if the source code is compliant by running: +codebase, however, you can always check to see if the source code is compliant by running: ```bash pnpm run lint diff --git a/DEBUG_PRODUCTION.md b/DEBUG_PRODUCTION.md new file mode 100644 index 000000000..25e1f3442 --- /dev/null +++ b/DEBUG_PRODUCTION.md @@ -0,0 +1,73 @@ +# Debugging Production Builds + +Symbolicate production stack traces from LNReader using uploaded sourcemaps. + +## Prerequisites + +- **Node.js** (v18 or later) +- The **sourcemap** matching the crashing APK version +- A **stack trace** from either `adb logcat` (device attached) or pasted from a Discord / GitHub issue report + +## Getting the Sourcemap + +### Release builds + +Sourcemaps are uploaded as GitHub release assets. Download the one matching the crashing version: + +``` +LNReader-v{version}-sourcemap.map +``` + +Example for `v2.5.0`: + +1. Go to the [releases page](https://github.com/lnreader/lnreader/releases) +2. Find release `v2.5.0` +3. Under **Assets**, download `LNReader-v2.5.0-sourcemap.map` + +### Preview / nightly builds + +Sourcemaps are uploaded as a separate artifact alongside the preview APK, named `LNReader-{sha}-sourcemap`. + +1. Go to the [Actions tab](https://github.com/lnreader/lnreader/actions) → **Build Preview** +2. Find the workflow run matching the build hash +3. Under **Artifacts**, download the one with the `-sourcemap` suffix + +## Symbolicating a Stack Trace + +Save the raw stack trace to a file (from `adb logcat`, or paste from Discord/GitHub into `trace.txt`), then run: + +```bash +npx metro-symbolicate < trace.txt +``` + +Or pipe it directly: + +```bash +adb logcat -d | npx metro-symbolicate +``` + +## Example + +Before (Hermes-obfuscated): + +``` +com.lnreader E Error: Unexpected token + at 93795 (address at index.js:1:729193) + at 28471 (address at index.js:1:219482) + at 10384 (address at index.js:1:81729) +``` + +After symbolication: + +``` +com.lnreader E Error: Unexpected token + at handleResponse (src/api/plugins.ts:120:12) + at fetchPlugin (src/hooks/usePlugins.ts:45:8) + at getPluginList (src/screens/browse/BrowseScreen.tsx:89:20) +``` + +## Troubleshooting + +- **Version mismatch**: the sourcemap must match the exact APK build. A mismatched version produces wrong file names and line numbers. +- **No sourcemap artifact**: verify the workflow that built the APK ran successfully and that the "Upload Sourcemaps" / "Upload Preview Sourcemap" step completed without errors. +- **Hermes bytecode**: Hermes compiles JS to bytecode. The sourcemap is generated during this step and maps bytecode offsets back to source. `metro-symbolicate` handles this transparently. diff --git a/Gemfile b/Gemfile index 20d5e24af..51515233e 100644 --- a/Gemfile +++ b/Gemfile @@ -13,4 +13,5 @@ gem 'concurrent-ruby', '< 1.3.4' gem 'bigdecimal' gem 'logger' gem 'benchmark' -gem 'mutex_m' \ No newline at end of file +gem 'mutex_m' +gem 'nkf' diff --git a/README.md b/README.md index 06d4941f7..fe142f4ae 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ GitHub release (latest by date) - - GitHub release (latest SemVer) + + Build Preview

diff --git a/TESTING.md b/TESTING.md index 21ae843b7..e6ecea276 100644 --- a/TESTING.md +++ b/TESTING.md @@ -9,13 +9,13 @@ This guide explains how to write tests in this React Native project using Jest a The project has global mocks configured in Jest. These are automatically applied: -- `__mocks__/` - Global mocks for native modules (react-native-mmkv, react-navigation, all database queries, etc.) +- `test/mocks/` - Global mocks for native modules (react-native-mmkv, react-navigation, all database queries, etc.) - `src/hooks/__mocks__/index.ts` - Hook-specific mocks (showToast, getString, parseChapterNumber, etc.) - `src/hooks/__tests__/mocks.ts` - Extended mocks for persisted hooks ### Using @test-utils -There's a custom render wrapper at `__tests-modules__/test-utils.tsx` with: +There is a custom render wrapper at `test/test-utils.tsx` with: - `render` - wraps with GestureHandlerRootView, SafeAreaProvider, PaperProvider, etc. - `renderNovel` - includes NovelContextProvider @@ -40,7 +40,7 @@ jest.mock('@hooks/persisted/usePlugins'); ### 2. Mock Functions Not Working -If `mockReturnValue` throws "not a function", create mock functions at module level: +If `mockReturnValue` throws "not a function", create mock functions at the module level: ```typescript // CORRECT: Module-level mock functions diff --git a/__mocks__/nativeModules.js b/__mocks__/nativeModules.js deleted file mode 100644 index fa3c82cdf..000000000 --- a/__mocks__/nativeModules.js +++ /dev/null @@ -1,67 +0,0 @@ -// require('react-native-gesture-handler/jestSetup'); -// require('react-native-reanimated').setUpTests(); - -jest.mock('@specs/NativeFile', () => ({ - __esModule: true, - default: { - writeFile: jest.fn(), - readFile: jest.fn(() => ''), - copyFile: jest.fn(), - moveFile: jest.fn(), - exists: jest.fn(() => true), - mkdir: jest.fn(), - unlink: jest.fn(), - readDir: jest.fn(() => []), - downloadFile: jest.fn().mockResolvedValue(), - getConstants: jest.fn(() => ({ - ExternalDirectoryPath: '/mock/external', - ExternalCachesDirectoryPath: '/mock/caches', - })), - }, -})); - -jest.mock('@specs/NativeEpub', () => ({ - __esModule: true, - default: { - parseNovelAndChapters: jest.fn(() => ({ - name: 'Mock Novel', - cover: null, - summary: null, - author: null, - artist: null, - chapters: [], - cssPaths: [], - imagePaths: [], - })), - }, -})); - -jest.mock('@specs/NativeTTSMediaControl', () => ({ - __esModule: true, - default: { - showMediaNotification: jest.fn(), - updatePlaybackState: jest.fn(), - updateProgress: jest.fn(), - dismiss: jest.fn(), - addListener: jest.fn(), - removeListeners: jest.fn(), - }, -})); - -jest.mock('@specs/NativeVolumeButtonListener', () => ({ - __esModule: true, - default: { - addListener: jest.fn(), - removeListeners: jest.fn(), - }, -})); - -jest.mock('@specs/NativeZipArchive', () => ({ - __esModule: true, - default: { - zip: jest.fn().mockResolvedValue(), - unzip: jest.fn().mockResolvedValue(), - remoteUnzip: jest.fn().mockResolvedValue(), - remoteZip: jest.fn().mockResolvedValue(''), - }, -})); diff --git a/__mocks__/react-native-nitro-modules.js b/__mocks__/react-native-nitro-modules.js deleted file mode 100644 index f6a21b70a..000000000 --- a/__mocks__/react-native-nitro-modules.js +++ /dev/null @@ -1,10 +0,0 @@ -// Mock for react-native-nitro-modules in Jest environment -jest.mock('react-native-nitro-modules', () => ({ - __esModule: true, - default: { - createHybridObject: jest.fn(() => { - // Return a mock object that won't be used since MMKV has its own mock - return {}; - }), - }, -})); diff --git a/android/app/build.gradle b/android/app/build.gradle deleted file mode 100644 index 691301293..000000000 --- a/android/app/build.gradle +++ /dev/null @@ -1,137 +0,0 @@ -apply plugin: "com.android.application" -apply plugin: "com.facebook.react" -apply plugin: 'org.jetbrains.kotlin.android' - -/** - * This is the configuration block to customize your React Native Android app. - * By default you don't need to apply any configuration, just uncomment the lines you need. - */ -react { - /* Folders */ - // The root of your project, i.e. where "package.json" lives. Default is '../..' - // root = file("../../") - // The folder where the react-native NPM package is. Default is ../../node_modules/react-native - // reactNativeDir = file("../../node_modules/react-native") - // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen - // codegenDir = file("../../node_modules/@react-native/codegen") - // The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js - // cliFile = file("../../node_modules/react-native/cli.js") - - /* Variants */ - // The list of variants to that are debuggable. For those we're going to - // skip the bundling of the JS bundle and the assets. By default is just 'debug'. - // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. - // debuggableVariants = ["liteDebug", "prodDebug"] - - /* Bundling */ - // A list containing the node command and its flags. Default is just 'node'. - // nodeExecutableAndArgs = ["node"] - // - // The command to run when bundling. By default is 'bundle' - // bundleCommand = "ram-bundle" - // - // The path to the CLI configuration file. Default is empty. - // bundleConfig = file(../rn-cli.config.js) - // - // The name of the generated asset file containing your JS bundle - // bundleAssetName = "MyApplication.android.bundle" - // - // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' - // entryFile = file("../js/index.android.js") - // - // A list of extra flags to pass to the 'bundle' commands. - // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle - // extraPackagerArgs = [] - - /* Hermes Commands */ - // The hermes compiler command to run. By default it is 'hermesc' - // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" - // - // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" - // hermesFlags = ["-O", "-output-source-map"] - - /* Autolinking */ - autolinkLibrariesWithApp() -} - -/** - * Set this to true to Run Proguard on Release builds to minify the Java bytecode. - */ -def enableProguardInReleaseBuilds = false - -/** - * The preferred build flavor of JavaScriptCore (JSC). - * - * For example, to use the international variant, you can use: - * `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+` - * - * The international variant includes ICU i18n library and necessary data - * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that - * give correct results when using with locales other than en-US. Note that - * this variant is about 6MiB larger per architecture than default. - */ -def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+' - -ext { - versionMajor = 2 - versionMinor = 0 - versionPatch = 3 -} - -android { - ndkVersion rootProject.ext.ndkVersion - buildToolsVersion rootProject.ext.buildToolsVersion - compileSdk rootProject.ext.compileSdkVersion - namespace "com.rajarsheechatterjee.LNReader" - defaultConfig { - applicationId project.hasProperty('customAppId') ? project.getProperty('customAppId') : 'com.rajarsheechatterjee.LNReader' - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - // Generated version code. Supports versions up to 1024.1024.2048 - versionCode ((((versionMajor << 10) | versionMinor) << 11) | versionPatch) - versionName "$versionMajor.$versionMinor.$versionPatch" - } - signingConfigs { - debug { - storeFile file('debug.keystore') - storePassword 'android' - keyAlias 'androiddebugkey' - keyPassword 'android' - } - } - buildTypes { - debug { - signingConfig signingConfigs.debug - applicationIdSuffix 'debug' - versionNameSuffix '-debug' - } - release { - // Caution! In production, you need to generate your own keystore file. - // see https://reactnative.dev/docs/signed-apk-android. - signingConfig signingConfigs.debug - minifyEnabled enableProguardInReleaseBuilds - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" - } - } - kotlinOptions { - jvmTarget = '17' - } - externalNativeBuild { - cmake { - path "src/main/jni/CMakeLists.txt" - } - } -} - -dependencies { - // The version of react-native is set by the React Native Gradle Plugin - implementation("com.facebook.react:react-android") - implementation 'androidx.core:core-ktx:1.15.0' - implementation 'androidx.media:media:1.7.0' - - if (hermesEnabled.toBoolean()) { - implementation("com.facebook.react:hermes-android") - } else { - implementation jscFlavor - } -} \ No newline at end of file diff --git a/android/app/debug.keystore b/android/app/debug.keystore deleted file mode 100644 index 364e105ed..000000000 Binary files a/android/app/debug.keystore and /dev/null differ diff --git a/android/app/google-services.json b/android/app/google-services.json deleted file mode 100644 index 4e0ee2c73..000000000 --- a/android/app/google-services.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "project_info": { - "project_number": "523872485654", - "project_id": "lnreader-backup", - "storage_bucket": "lnreader-backup.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:523872485654:android:169cd062458db962ce1037", - "android_client_info": { - "package_name": "com.rajarsheechatterjee.LNReader" - } - }, - "oauth_client": [ - { - "client_id": "523872485654-14jtut8orr7dbrk2chea279d7k8889sr.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "com.rajarsheechatterjee.LNReader", - "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625" - } - }, - { - "client_id": "523872485654-liarmq8nl0g5an2cki3bpg9jc0d8a21j.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyBH_j-0Jyo9aFJ_KV8Nr3te2hs_L_ZYhrE" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "523872485654-liarmq8nl0g5an2cki3bpg9jc0d8a21j.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - } - ], - "configuration_version": "1" -} diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro deleted file mode 100644 index c83ce9df1..000000000 --- a/android/app/proguard-rules.pro +++ /dev/null @@ -1,14 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: --keep class com.swmansion.reanimated.** { *; } --keep class com.facebook.react.turbomodule.** { *; } --keep class com.facebook.hermes.unicode.** { *; } --keep class com.facebook.jni.** { *; } diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index a40bc8153..000000000 --- a/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/android/app/src/debug/res/values/string.xml b/android/app/src/debug/res/values/string.xml deleted file mode 100644 index 6dec539d1..000000000 --- a/android/app/src/debug/res/values/string.xml +++ /dev/null @@ -1,3 +0,0 @@ - - LNReader debug - \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 2d8e7b563..000000000 --- a/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/app/src/main/assets/css/tts.css b/android/app/src/main/assets/css/tts.css deleted file mode 100644 index 91244534b..000000000 --- a/android/app/src/main/assets/css/tts.css +++ /dev/null @@ -1,48 +0,0 @@ -#TTS-Controller { - position: fixed; - top: 50%; - left: 20px; - opacity: 0.5; -} - -#TTS-Controller button { - background: color-mix( - in srgb, - var(--readerSettings-theme) 85%, - var(--readerSettings-textColor) 15% - ); - outline: none; - border-width: 1px; - border-style: solid; - border-color: color-mix( - in srgb, - var(--readerSettings-textColor) 30%, - var(--readerSettings-theme) 70% - ); - display: flex; - justify-items: center; - align-items: center; - padding: 4px; - border-radius: 100%; - transition: 0.5s; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); -} - -#TTS-Controller.active { - opacity: 1; -} - -#TTS-Controller.active button { - padding: 16px; -} - -#TTS-Controller svg { - fill: var(--readerSettings-textColor); - width: 20px; - height: 20px; -} - -#TTS-Controller.active svg { - width: 24px; - height: 24px; -} diff --git a/android/app/src/main/assets/js/icons.js b/android/app/src/main/assets/js/icons.js deleted file mode 100644 index 54ea56035..000000000 --- a/android/app/src/main/assets/js/icons.js +++ /dev/null @@ -1,6 +0,0 @@ -const volumnIcon = - ''; -const pauseIcon = - ''; -const resumeIcon = - ''; diff --git a/android/app/src/main/assets/js/index.js b/android/app/src/main/assets/js/index.js deleted file mode 100644 index f4175aa0a..000000000 --- a/android/app/src/main/assets/js/index.js +++ /dev/null @@ -1,351 +0,0 @@ -const { div, p, img, button } = van.tags; - -const ChapterEnding = () => { - return () => - reader.generalSettings.val.pageReader - ? div() - : div(div({ class: 'info-text' }, reader.strings.finished), () => - reader.nextChapter - ? button( - { - class: 'next-button', - onclick: e => { - e.stopPropagation(); - reader.post({ type: 'next' }); - }, - }, - reader.strings.nextChapter, - ) - : div({ class: 'info-text' }, reader.strings.noNextChapter), - ); -}; - -const Scrollbar = () => { - const horizontal = van.derive( - () => !reader.generalSettings.val.verticalSeekbar, - ); - let lock = false; - const percentage = van.state(0); - const update = ratio => { - if (ratio === undefined) { - ratio = (window.scrollY + reader.layoutHeight) / reader.chapterHeight; - } - if (ratio > 1) { - ratio = 1; - } - if (reader.generalSettings.val.pageReader) { - pageReader.movePage( - parseInt(pageReader.totalPages.val * Math.min(0.99, ratio)), - ); - return; - } - percentage.val = parseInt(ratio * 100); - if (lock) { - window.scrollTo({ - top: reader.chapterHeight * ratio - reader.layoutHeight, - behavior: 'instant', - }); - } - }; - window.addEventListener( - 'scroll', - () => !lock && !reader.generalSettings.val.pageReader && update(), - ); - return div( - { id: 'ScrollBar' }, - div( - { class: 'scrollbar-item scrollbar-text', id: 'scrollbar-percentage' }, - () => - reader.generalSettings.val.pageReader - ? pageReader.page.val + 1 - : percentage.val, - ), - div( - { class: 'scrollbar-item', id: 'scrollbar-slider' }, - div( - { id: 'scrollbar-track' }, - div( - { - id: 'scrollbar-progress', - style: () => { - const percentageValue = reader.generalSettings.val.pageReader - ? ((pageReader.page.val + 1) / pageReader.totalPages.val) * 100 - : percentage.val; - return horizontal.val - ? `width: ${percentageValue}%; height: 100%;` - : `height: ${percentageValue}%; width: 100%;`; - }, - }, - div( - { - id: 'scrollbar-thumb-wrapper', - ontouchstart: () => { - lock = true; - }, - ontouchend: () => { - lock = false; - }, - ontouchmove: function (e) { - const slider = this.parentElement.parentElement.parentElement; - const sliderHeight = horizontal.val - ? slider.clientWidth - : slider.clientHeight; - const sliderOffsetY = horizontal.val - ? slider.getBoundingClientRect().left - : slider.getBoundingClientRect().top; - const ratio = - ((horizontal.val - ? e.changedTouches[0].clientX - : e.changedTouches[0].clientY) - - sliderOffsetY) / - sliderHeight; - update(ratio < 0 ? 0 : ratio); - }, - }, - div({ id: 'scrollbar-thumb' }), - ), - ), - ), - ), - div( - { - class: 'scrollbar-item scrollbar-text', - id: 'scrollbar-percentage-max', - }, - () => - reader.generalSettings.val.pageReader ? pageReader.totalPages.val : 100, - ), - ); -}; - -const ToolWrapper = () => { - const horizontal = van.derive( - () => !reader.generalSettings.val.verticalSeekbar, - ); - return div( - { - id: 'ToolWrapper', - class: () => - `${reader.hidden.val ? 'hidden' : ''} ${ - horizontal.val ? 'horizontal' : '' - }`, - }, - Scrollbar(), - ); -}; - -const ImageModal = ({ src }) => { - return div( - { - id: 'Image-Modal', - class: () => (src.val ? 'show' : ''), - onclick: e => { - if (e.target.id !== 'Image-Modal-img') { - e.stopPropagation(); - src.val = ''; - } - }, - }, - img({ - id: 'Image-Modal-img', - src: src, - alt: () => (src.val ? `Cant not render image from ${src.val}` : ''), - }), - ); -}; - -const ModalWrapper = () => { - const imgSrc = van.state(''); - const showImage = src => { - imgSrc.val = src; - reader.viewport.setAttribute( - 'content', - 'width=device-width, initial-scale=1.0, maximum-scale=10', - ); - }; - const hideImage = () => { - imgSrc.val = ''; - reader.viewport.setAttribute( - 'content', - 'width=device-width, initial-scale=1.0, maximum-scale=1.0', - ); - }; - - document.addEventListener('contextmenu', e => { - if (e.target instanceof HTMLImageElement) { - if (!imgSrc.val) { - showImage(e.target.src); - } else { - hideImage(); - } - } - }); - return div(ImageModal({ src: imgSrc })); -}; - -const Footer = () => { - const percentage = van.state(0); - const time = van.state( - new Date().toLocaleTimeString(undefined, { - hour: '2-digit', - minute: '2-digit', - hour12: false, - }), - ); - window.addEventListener('scroll', () => { - let ratio = (window.scrollY + reader.layoutHeight) / reader.chapterHeight; - if (ratio > 1) { - ratio = 1; - } - percentage.val = parseInt(ratio * 100); - }); - setInterval(() => { - time.val = new Date().toLocaleTimeString(undefined, { - hour: '2-digit', - minute: '2-digit', - hour12: false, - }); - }, 10000); - return div( - { - id: 'reader-footer-wrapper', - class: () => - reader.generalSettings.val.showBatteryAndTime || - reader.generalSettings.val.showScrollPercentage - ? '' - : 'd-none', - }, - div( - { id: 'reader-footer' }, - - div( - { - id: 'reader-battery', - class: () => - `reader-footer-item ${ - reader.generalSettings.val.showBatteryAndTime ? '' : 'hidden' - }`, - }, - () => Math.ceil(reader.batteryLevel.val * 100) + '%', - ), - div( - { - id: 'reader-percentage', - class: () => - `reader-footer-item ${ - reader.generalSettings.val.showScrollPercentage ? '' : 'hidden' - }`, - }, - () => - reader.generalSettings.val.pageReader - ? `${pageReader.page.val + 1}/${pageReader.totalPages.val}` - : percentage.val + '%', - ), - div( - { - id: 'reader-time', - class: () => - `reader-footer-item ${ - reader.generalSettings.val.showBatteryAndTime ? '' : 'hidden' - }`, - }, - time, - ), - ), - ); -}; - -const TTSController = () => { - let controllerElement = null; - let hoverElement = null; - let clientX = null; - let clientY = null; - return div( - { - id: 'TTS-Controller', - class: () => `${reader.generalSettings.val.TTSEnable ? '' : 'hidden'}`, - style: () => - reader.generalSettings.val.TTSEnable - ? 'pointer-events: auto;' - : 'pointer-events: none; display: none !important; opacity: 0; transition: none;', - ontouchstart: () => { - if (!controllerElement) { - controllerElement = document.getElementById('TTS-Controller'); - } - controllerElement.classList.add('active'); - controllerElement.style.transition = ''; - }, - ontouchmove: e => { - e.preventDefault(); - e.stopPropagation(); - clientX = e.changedTouches[0].clientX; - clientY = e.changedTouches[0].clientY; - controllerElement.style.left = `${clientX}px`; - controllerElement.style.top = `${clientY}px`; - const hoverElements = document.elementsFromPoint(clientX, clientY); - const newHoverElement = hoverElements.reverse().find(e => { - if (e.id.includes('scrollbar')) { - return false; - } - return tts.readable(e); - }); - hoverElement?.classList.remove('highlight'); - if (newHoverElement) { - newHoverElement.classList.add('highlight'); - hoverElement = newHoverElement; - } else { - hoverElement = null; - } - }, - ontouchend: () => { - controllerElement.style.transition = '1s'; - controllerElement.classList.remove('active'); - controllerElement.style.left = '20px'; - if (clientX && clientY) { - let top = clientY < 120 ? 120 : clientY; - if (top + 120 > reader.layoutHeight) { - top = reader.layoutHeight - 120; - } - controllerElement.style.top = `${top}px`; - // Check if TTS is still enabled before starting - if (hoverElement && reader.generalSettings.val.TTSEnable) { - tts.start(hoverElement); - controllerElement.firstElementChild.innerHTML = pauseIcon; - } - } - clientX = null; - clientY = null; - }, - onclick: e => { - e.stopPropagation(); - // Don't allow interaction if TTS is disabled - if (!reader.generalSettings.val.TTSEnable) { - return; - } - if (tts.reading) { - tts.pause(); - controllerElement.firstElementChild.innerHTML = resumeIcon; - } else if (tts.started) { - tts.resume(); - controllerElement.firstElementChild.innerHTML = pauseIcon; - } else { - tts.start(); - controllerElement.firstElementChild.innerHTML = pauseIcon; - } - }, - }, - button({ innerHTML: volumnIcon }), - ); -}; - -const ReaderUI = () => { - return div( - ToolWrapper(), - TTSController(), - ModalWrapper(), - Footer(), - ChapterEnding(), - ); -}; - -van.add(document.getElementById('reader-ui'), ReaderUI()); diff --git a/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainActivity.kt b/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainActivity.kt deleted file mode 100644 index c23dfe6c9..000000000 --- a/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainActivity.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.rajarsheechatterjee.LNReader - -import android.graphics.Color -import android.os.Build -import android.os.Bundle -import android.view.KeyEvent -import android.view.View -import android.view.WindowManager -import com.facebook.react.ReactActivity -import com.facebook.react.ReactActivityDelegate -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled -import com.facebook.react.defaults.DefaultReactActivityDelegate -import com.rajarsheechatterjee.NativeVolumeButtonListener.NativeVolumeButtonListener -import expo.modules.ReactActivityDelegateWrapper -import org.devio.rn.splashscreen.SplashScreen - -class MainActivity : ReactActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - val layoutParams = WindowManager.LayoutParams() - layoutParams.layoutInDisplayCutoutMode = - WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES - window.attributes = layoutParams - } - window.statusBarColor = Color.TRANSPARENT - window.navigationBarColor = Color.TRANSPARENT - @Suppress("DEPRECATION") - window.decorView.systemUiVisibility = - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE - super.onCreate(null) - SplashScreen.show(this, R.style.SplashScreenTheme, R.id.lottie) - SplashScreen.setAnimationFinished(true) - } - - override fun dispatchKeyEvent(event: KeyEvent): Boolean { - if (NativeVolumeButtonListener.isActive) { - val action = event.action - return when (event.keyCode) { - KeyEvent.KEYCODE_VOLUME_UP -> { - if (action == KeyEvent.ACTION_DOWN) { - NativeVolumeButtonListener.sendEvent(true) - } - true - } - - KeyEvent.KEYCODE_VOLUME_DOWN -> { - if (action == KeyEvent.ACTION_DOWN) { - NativeVolumeButtonListener.sendEvent(false) - } - true - } - - else -> super.dispatchKeyEvent(event) - } - } - return super.dispatchKeyEvent(event) - } - - /** - * Returns the name of the main component registered from JavaScript. - * This is used to schedule rendering of the component. - */ - override fun getMainComponentName(): String = "main" - - override fun createReactActivityDelegate(): ReactActivityDelegate { - return ReactActivityDelegateWrapper( - this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, DefaultReactActivityDelegate( - this, - mainComponentName, // If you opted-in for the New Architecture, we enable the Fabric Renderer. - fabricEnabled - ) - ) - } -} diff --git a/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainApplication.kt b/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainApplication.kt deleted file mode 100644 index 856a31726..000000000 --- a/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainApplication.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.rajarsheechatterjee.LNReader -import expo.modules.ExpoReactHostFactory - -import android.app.Application -import android.content.res.Configuration -import com.facebook.react.PackageList -import com.facebook.react.ReactApplication -import com.facebook.react.ReactHost -import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load -import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost -import com.facebook.react.soloader.OpenSourceMergedSoMapping -import com.facebook.soloader.SoLoader -import com.rajarsheechatterjee.NativeFile.NativePackage -import com.rajarsheechatterjee.NativeVolumeButtonListener.NativeVolumeButtonListenerPackage -import com.rajarsheechatterjee.NativeTTSMediaControl.NativeTTSMediaControlPackage -import com.rajarsheechatterjee.NativeZipArchive.NativeZipArchivePackage -import expo.modules.ApplicationLifecycleDispatcher - -class MainApplication : Application(), ReactApplication { - override val reactHost: ReactHost by lazy { - ExpoReactHostFactory.getDefaultReactHost( - context = applicationContext, - packageList = - PackageList(this).packages.apply { - add(NativePackage()) - add(NativeTTSMediaControlPackage()) - add(NativeVolumeButtonListenerPackage()) - add(NativeZipArchivePackage()) - }, - ) - } - - override fun onCreate() { - super.onCreate() - loadReactNative(this) - ApplicationLifecycleDispatcher.onApplicationCreate(this) - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig) - } -} diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeFile/NativeFile.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeFile/NativeFile.kt deleted file mode 100644 index a65a56505..000000000 --- a/android/app/src/main/java/com/rajarsheechatterjee/NativeFile/NativeFile.kt +++ /dev/null @@ -1,248 +0,0 @@ -package com.rajarsheechatterjee.NativeFile - -import android.net.Uri -import android.os.Build -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReadableMap -import com.facebook.react.bridge.WritableArray -import com.facebook.react.bridge.WritableMap -import com.facebook.react.bridge.WritableNativeArray -import com.facebook.react.bridge.WritableNativeMap -import com.facebook.react.modules.network.CookieJarContainer -import com.facebook.react.modules.network.ForwardingCookieHandler -import com.facebook.react.modules.network.OkHttpClientProvider -import com.lnreader.spec.NativeFileSpec -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import okhttp3.Call -import okhttp3.Callback -import okhttp3.Headers -import okhttp3.JavaNetCookieJar -import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.Response -import java.io.File -import java.io.FileOutputStream -import java.io.FileWriter -import java.io.IOException -import java.io.InputStream -import java.io.OutputStream -import java.io.PushbackInputStream -import java.util.zip.GZIPInputStream - - -class NativeFile(context: ReactApplicationContext) : - NativeFileSpec(context) { - private val BUFFER_SIZE = 4096 - private val okHttpClient = OkHttpClientProvider.createClient() - private val coroutineScope = CoroutineScope(Dispatchers.IO) - - init { - val cookieContainer = okHttpClient.cookieJar as CookieJarContainer - val cookieHandler = ForwardingCookieHandler(reactApplicationContext) - cookieContainer.setCookieJar(JavaNetCookieJar(cookieHandler)) - } - - private fun getFileUri(filepath: String): Uri { - var uri = Uri.parse(filepath) - if (uri.scheme == null) { - // No prefix, assuming that provided path is absolute path to file - val file = File(filepath) - if (file.isDirectory) { - throw Exception("Invalid file, folder found!") - } - uri = Uri.parse("file://$filepath") - } - return uri - } - - private fun getInputStream(filepath: String): InputStream { - val uri = getFileUri(filepath) - return reactApplicationContext.contentResolver.openInputStream(uri) - ?: throw Exception("ENOENT: could not open an input stream for '$filepath'") - } - - private val writeAccessByAPILevel: String - get() = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) "w" else "rwt" - - private fun getOutputStream(filepath: String): OutputStream { - val uri = getFileUri(filepath) - return reactApplicationContext.contentResolver.openOutputStream(uri, writeAccessByAPILevel) - ?: throw Exception("ENOENT: could not open an output stream for '$filepath'") - } - - override fun writeFile(path: String, content: String) { - try { - val fw = FileWriter(path) - fw.write(content) - fw.close() - } catch (e: IOException) { - throw Exception("Failed to write file '$path': ${e.message}") - } - } - - override fun readFile(path: String): String { - val file = File(path) - if (!file.exists()) { - throw Exception("File not found: '$path'") - } - return file.bufferedReader().readText() - } - - override fun copyFile(filepath: String, destPath: String) { - copyFileContent(filepath, destPath) - } - - override fun moveFile(filepath: String, destPath: String) { - val inFile = File(filepath) - copyFileContent(filepath, destPath, { inFile.delete() }) - } - - private fun copyFileContent( - filepath: String, - destPath: String, - onDone: (() -> Unit)? = null, - ) { - try { - val inputStream = getInputStream(filepath) - try { - val outputStream = getOutputStream(destPath) - try { - val buffer = ByteArray(BUFFER_SIZE) - var length: Int - while (inputStream.read(buffer).also { length = it } > 0) { - outputStream.write(buffer, 0, length) - } - } finally { - outputStream.close() - } - } finally { - inputStream.close() - } - if (onDone != null) { - onDone() - } - } catch (e: IOException) { - throw Exception("Failed to copy file from '$filepath' to '$destPath': ${e.message}") - } - } - - override fun exists(filepath: String) = File(filepath).exists() - - override fun mkdir(filepath: String) { - val file = File(filepath) - if (!file.exists()) { - val created = file.mkdirs() - if (!created) throw Exception("Directory could not be created") - } - } - - private fun deleteRecursive(fileOrDirectory: File) { - if (fileOrDirectory.isDirectory) { - for (child in fileOrDirectory.listFiles()!!) { - deleteRecursive(child) - } - } - fileOrDirectory.delete() - } - - override fun unlink(filepath: String) { - val file = File(filepath) - if (!file.exists()) throw Exception("File does not exist") - deleteRecursive(file) - } - - override fun readDir(directory: String): WritableArray { - val file = File(directory) - if (!file.exists()) throw Exception("Folder does not exist") - val files = file.listFiles() - val fileMaps: WritableArray = WritableNativeArray() - for (childFile in files!!) { - val fileMap: WritableMap = WritableNativeMap() - fileMap.putString("name", childFile.name) - fileMap.putString("path", childFile.absolutePath) - fileMap.putBoolean("isDirectory", childFile.isDirectory) - fileMaps.pushMap(fileMap) - } - return fileMaps - } - - private fun decompressStream(input: InputStream?): InputStream { - val pb = PushbackInputStream(input, 2) - val signature = ByteArray(2) - val len = pb.read(signature) - if(len == -1) return pb; - pb.unread(signature, 0, len) - return if (signature[0] == 0x1f.toByte() && signature[1] == 0x8b.toByte()) - GZIPInputStream(pb) else pb - } - - override fun downloadFile( - url: String, - destPath: String, - method: String, - headers: ReadableMap, - body: String?, - promise: Promise - ) { - coroutineScope.launch { - try { - val headersBuilder = Headers.Builder() - headers.entryIterator.forEach { entry -> - headersBuilder.add(entry.key, entry.value.toString()) - } - val requestBuilder = Request.Builder() - .url(url) - .headers(headersBuilder.build()) - if (method.lowercase() == "get") { - requestBuilder.get() - } else if (body != null) { - requestBuilder.post(body.toRequestBody()) - } - - okHttpClient.newCall(requestBuilder.build()) - .enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - promise.reject(e) - } - - override fun onResponse(call: Call, response: Response) { - response.use { - if (!it.isSuccessful || it.body == null) { - promise.reject(Exception("Failed to download: ${it.code}")) - return - } - try { - decompressStream(it.body!!.byteStream()).use { inputStream -> - FileOutputStream(destPath).use { fos -> - inputStream.copyTo(fos, BUFFER_SIZE) - } - } - promise.resolve(null) - } catch (e: Exception) { - promise.reject(e) - } - } - } - }) - } catch (e: Exception) { - promise.reject(e) - } - } - } - - override fun getTypedExportedConstants(): MutableMap { - val constants: MutableMap = HashMap() - val externalDirectory = this.reactApplicationContext.getExternalFilesDir(null) - if (externalDirectory != null) { - constants["ExternalDirectoryPath"] = externalDirectory.absolutePath - } - val externalCachesDirectory = this.reactApplicationContext.externalCacheDir - if (externalCachesDirectory != null) { - constants["ExternalCachesDirectoryPath"] = externalCachesDirectory.absolutePath - } - return constants - } -} diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeFile/NativePackage.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeFile/NativePackage.kt deleted file mode 100644 index 62117a93b..000000000 --- a/android/app/src/main/java/com/rajarsheechatterjee/NativeFile/NativePackage.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.rajarsheechatterjee.NativeFile - -import com.facebook.react.BaseReactPackage -import com.facebook.react.bridge.NativeModule -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.module.model.ReactModuleInfo -import com.facebook.react.module.model.ReactModuleInfoProvider -import com.lnreader.spec.NativeFileSpec - -class NativePackage : BaseReactPackage() { - override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? = - if (name == NativeFileSpec.NAME) { - NativeFile(reactContext) - } else { - null - } - - override fun getReactModuleInfoProvider() = ReactModuleInfoProvider { - mapOf( - NativeFileSpec.NAME to ReactModuleInfo( - NativeFileSpec.NAME, - NativeFileSpec.NAME, - canOverrideExistingModule = false, - needsEagerInit = false, - isCxxModule = false, - isTurboModule = true - ) - ) - } -} diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeTTSMediaControl/NativeTTSMediaControl.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeTTSMediaControl/NativeTTSMediaControl.kt deleted file mode 100644 index b7adcc413..000000000 --- a/android/app/src/main/java/com/rajarsheechatterjee/NativeTTSMediaControl/NativeTTSMediaControl.kt +++ /dev/null @@ -1,355 +0,0 @@ -package com.rajarsheechatterjee.NativeTTSMediaControl - -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.PendingIntent -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.os.Build -import android.support.v4.media.MediaMetadataCompat -import android.support.v4.media.session.MediaSessionCompat -import android.support.v4.media.session.PlaybackStateCompat -import androidx.core.app.NotificationCompat -import androidx.media.app.NotificationCompat.MediaStyle -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.modules.core.DeviceEventManagerModule -import com.lnreader.spec.NativeTTSMediaControlSpec -import java.io.File -import java.net.URL - -class NativeTTSMediaControl(private val appContext: ReactApplicationContext) : - NativeTTSMediaControlSpec(appContext) { - - companion object { - private const val CHANNEL_ID = "tts-media-controls" - private const val NOTIFICATION_ID = 1001 - private const val ACTION_PLAY = "com.lnreader.TTS_PLAY" - private const val ACTION_PAUSE = "com.lnreader.TTS_PAUSE" - private const val ACTION_STOP = "com.lnreader.TTS_STOP" - private const val ACTION_PREV = "com.lnreader.TTS_PREV" - private const val ACTION_NEXT = "com.lnreader.TTS_NEXT" - private const val ACTION_REWIND = "com.lnreader.TTS_REWIND" - } - - private var mediaSession: MediaSessionCompat? = null - private var isPlaying = false - private var listenerCount = 0 - private var coverBitmap: Bitmap? = null - private var currentCoverUri: String? = null - private var currentTitle: String? = null - private var currentSubtitle: String? = null - private var receiverRegistered = false - private var currentPosition: Long = 0L - private var totalDuration: Long = 0L - - private val mediaReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - when (intent.action) { - ACTION_PLAY -> { - isPlaying = true - sendEvent("TTSPlay") - updateNotification() - } - ACTION_PAUSE -> { - isPlaying = false - sendEvent("TTSPause") - updateNotification() - } - ACTION_STOP -> sendEvent("TTSStop") - ACTION_PREV -> sendEvent("TTSPrev") - ACTION_NEXT -> sendEvent("TTSNext") - ACTION_REWIND -> sendEvent("TTSRewind") - } - } - } - - private fun sendEvent(eventName: String) { - if (listenerCount > 0) { - appContext.getJSModule( - DeviceEventManagerModule.RCTDeviceEventEmitter::class.java - ).emit(eventName, null) - } - } - - private fun sendSeekEvent(elementIndex: Long) { - if (listenerCount > 0) { - val params = com.facebook.react.bridge.Arguments.createMap().apply { - putInt("position", elementIndex.toInt()) - } - appContext.getJSModule( - DeviceEventManagerModule.RCTDeviceEventEmitter::class.java - ).emit("TTSSeekTo", params) - } - } - - private fun ensureChannel() { - val manager = appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val channel = NotificationChannel( - CHANNEL_ID, - "TTS Media Controls", - NotificationManager.IMPORTANCE_LOW - ).apply { - description = "Text-to-speech playback controls" - setShowBadge(false) - } - manager.createNotificationChannel(channel) - } - } - - private fun ensureMediaSession() { - if (mediaSession == null) { - mediaSession = MediaSessionCompat(appContext, "LNReaderTTS").apply { - setCallback(object : MediaSessionCompat.Callback() { - override fun onPlay() { - isPlaying = true - sendEvent("TTSPlay") - updateNotification() - } - - override fun onPause() { - isPlaying = false - sendEvent("TTSPause") - updateNotification() - } - - override fun onStop() { - sendEvent("TTSStop") - } - - override fun onSkipToPrevious() { - sendEvent("TTSPrev") - } - - override fun onSkipToNext() { - sendEvent("TTSNext") - } - - override fun onSeekTo(pos: Long) { - // pos is in our scaled ms domain: elementIndex * 1000 - val elementIndex = pos / 1000L - currentPosition = elementIndex - updateNotification() - sendSeekEvent(elementIndex) - } - }) - isActive = true - } - } - } - - private fun registerReceiver() { - if (!receiverRegistered) { - val filter = IntentFilter().apply { - addAction(ACTION_PLAY) - addAction(ACTION_PAUSE) - addAction(ACTION_STOP) - addAction(ACTION_PREV) - addAction(ACTION_NEXT) - addAction(ACTION_REWIND) - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - appContext.registerReceiver(mediaReceiver, filter, Context.RECEIVER_NOT_EXPORTED) - } else { - appContext.registerReceiver(mediaReceiver, filter) - } - receiverRegistered = true - } - } - - private fun buildPendingIntent(action: String): PendingIntent { - val intent = Intent(action).setPackage(appContext.packageName) - return PendingIntent.getBroadcast( - appContext, - action.hashCode(), - intent, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - ) - } - - private fun getContentIntent(): PendingIntent { - val intent = appContext.packageManager.getLaunchIntentForPackage(appContext.packageName) - ?: Intent() - return PendingIntent.getActivity( - appContext, - 0, - intent, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - ) - } - - private fun updateNotification() { - val session = mediaSession ?: return - - val stateBuilder = PlaybackStateCompat.Builder() - .setActions( - PlaybackStateCompat.ACTION_PLAY_PAUSE or - PlaybackStateCompat.ACTION_STOP or - PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or - PlaybackStateCompat.ACTION_SKIP_TO_NEXT or - PlaybackStateCompat.ACTION_SEEK_TO - ) - .setState( - if (isPlaying) PlaybackStateCompat.STATE_PLAYING else PlaybackStateCompat.STATE_PAUSED, - currentPosition * 1000L, - 0f - ) - session.setPlaybackState(stateBuilder.build()) - - val metadataBuilder = MediaMetadataCompat.Builder() - .putString(MediaMetadataCompat.METADATA_KEY_TITLE, currentSubtitle ?: "") - .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, currentTitle ?: "") - .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, totalDuration * 1000L) - coverBitmap?.let { - metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, it) - } - session.setMetadata(metadataBuilder.build()) - - val playPauseAction = if (isPlaying) { - NotificationCompat.Action.Builder( - android.R.drawable.ic_media_pause, - "Pause", - buildPendingIntent(ACTION_PAUSE) - ).build() - } else { - NotificationCompat.Action.Builder( - android.R.drawable.ic_media_play, - "Play", - buildPendingIntent(ACTION_PLAY) - ).build() - } - - val prevAction = NotificationCompat.Action.Builder( - android.R.drawable.ic_media_previous, - "Previous", - buildPendingIntent(ACTION_PREV) - ).build() - - val rewindAction = NotificationCompat.Action.Builder( - android.R.drawable.ic_media_rew, - "Replay", - buildPendingIntent(ACTION_REWIND) - ).build() - - val nextAction = NotificationCompat.Action.Builder( - android.R.drawable.ic_media_next, - "Next", - buildPendingIntent(ACTION_NEXT) - ).build() - - val notification = NotificationCompat.Builder(appContext, CHANNEL_ID) - .setContentTitle(currentSubtitle) - .setContentText(currentTitle) - .setSmallIcon(appContext.applicationInfo.icon) - .setLargeIcon(coverBitmap) - .setContentIntent(getContentIntent()) - .setDeleteIntent(buildPendingIntent(ACTION_STOP)) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setOngoing(isPlaying) - .addAction(prevAction) - .addAction(rewindAction) - .addAction(playPauseAction) - .addAction(nextAction) - .setStyle( - MediaStyle() - .setMediaSession(session.sessionToken) - .setShowActionsInCompactView(1, 2, 3) - ) - .build() - - val manager = appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - manager.notify(NOTIFICATION_ID, notification) - } - - private fun loadCoverBitmap(coverUri: String) { - if (coverUri == currentCoverUri && coverBitmap != null) return - currentCoverUri = coverUri - - if (coverUri.isBlank()) { - coverBitmap = null - return - } - - if (coverUri.startsWith("file://")) { - val path = coverUri.removePrefix("file://").split("?")[0] - val file = File(path) - if (file.exists()) { - coverBitmap = BitmapFactory.decodeFile(file.absolutePath) - } - } else if (coverUri.startsWith("http")) { - coverBitmap = null - Thread { - try { - val stream = URL(coverUri).openStream() - val bitmap = BitmapFactory.decodeStream(stream) - stream.close() - coverBitmap = bitmap - updateNotification() - } catch (_: Exception) { - // Silently fail — notification will show without cover - } - }.start() - } - } - - override fun showMediaNotification( - title: String, - subtitle: String, - coverUri: String, - isPlaying: Boolean - ) { - this.isPlaying = isPlaying - this.currentTitle = title - this.currentSubtitle = subtitle - - ensureChannel() - ensureMediaSession() - registerReceiver() - loadCoverBitmap(coverUri) - updateNotification() - } - - override fun updatePlaybackState(isPlaying: Boolean) { - this.isPlaying = isPlaying - updateNotification() - } - - override fun updateProgress(current: Double, total: Double) { - currentPosition = current.toLong() - totalDuration = total.toLong() - updateNotification() - } - - override fun dismiss() { - mediaSession?.isActive = false - mediaSession?.release() - mediaSession = null - - val manager = appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - manager.cancel(NOTIFICATION_ID) - - if (receiverRegistered) { - try { - appContext.unregisterReceiver(mediaReceiver) - } catch (_: Exception) {} - receiverRegistered = false - } - - coverBitmap = null - currentCoverUri = null - currentPosition = 0L - totalDuration = 0L - } - - override fun addListener(eventName: String?) { - listenerCount++ - } - - override fun removeListeners(count: Double) { - listenerCount = (listenerCount - count.toInt()).coerceAtLeast(0) - } -} diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeTTSMediaControl/NativeTTSMediaControlPackage.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeTTSMediaControl/NativeTTSMediaControlPackage.kt deleted file mode 100644 index 9b87ad07e..000000000 --- a/android/app/src/main/java/com/rajarsheechatterjee/NativeTTSMediaControl/NativeTTSMediaControlPackage.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.rajarsheechatterjee.NativeTTSMediaControl - -import com.facebook.react.BaseReactPackage -import com.facebook.react.bridge.NativeModule -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.module.model.ReactModuleInfo -import com.facebook.react.module.model.ReactModuleInfoProvider -import com.lnreader.spec.NativeTTSMediaControlSpec - -class NativeTTSMediaControlPackage : BaseReactPackage() { - override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? = - if (name == NativeTTSMediaControlSpec.NAME) { - NativeTTSMediaControl(reactContext) - } else { - null - } - - override fun getReactModuleInfoProvider() = ReactModuleInfoProvider { - mapOf( - NativeTTSMediaControlSpec.NAME to ReactModuleInfo( - NativeTTSMediaControlSpec.NAME, - NativeTTSMediaControlSpec.NAME, - canOverrideExistingModule = false, - needsEagerInit = false, - isCxxModule = false, - isTurboModule = true - ) - ) - } -} diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeVolumeButtonListener/NativeVolumeButtonListener.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeVolumeButtonListener/NativeVolumeButtonListener.kt deleted file mode 100644 index 601c19429..000000000 --- a/android/app/src/main/java/com/rajarsheechatterjee/NativeVolumeButtonListener/NativeVolumeButtonListener.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.rajarsheechatterjee.NativeVolumeButtonListener - -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.modules.core.DeviceEventManagerModule -import com.lnreader.spec.NativeVolumeButtonListenerSpec - -class NativeVolumeButtonListener(appContext: ReactApplicationContext) : - NativeVolumeButtonListenerSpec(appContext) { - init { - NativeVolumeButtonListener.appContext = appContext - } - - override fun addListener(eventName: String?) { - isActive = true - } - - override fun removeListeners(count: Double) { - isActive = false - } - - companion object { - lateinit var appContext: ReactApplicationContext - var isActive = false - - fun sendEvent(up: Boolean) { - if (isActive) { - appContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) - .emit(if (up) "VolumeUp" else "VolumeDown", null) - } - } - } -} diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeVolumeButtonListener/NativeVolumeButtonListenerPackage.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeVolumeButtonListener/NativeVolumeButtonListenerPackage.kt deleted file mode 100644 index c66df9baf..000000000 --- a/android/app/src/main/java/com/rajarsheechatterjee/NativeVolumeButtonListener/NativeVolumeButtonListenerPackage.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.rajarsheechatterjee.NativeVolumeButtonListener - -import com.facebook.react.BaseReactPackage -import com.facebook.react.bridge.NativeModule -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.module.model.ReactModuleInfo -import com.facebook.react.module.model.ReactModuleInfoProvider -import com.lnreader.spec.NativeVolumeButtonListenerSpec - -class NativeVolumeButtonListenerPackage : BaseReactPackage() { - override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? = - if (name == NativeVolumeButtonListenerSpec.NAME) { - NativeVolumeButtonListener(reactContext) - } else { - null - } - - override fun getReactModuleInfoProvider() = ReactModuleInfoProvider { - mapOf( - NativeVolumeButtonListenerSpec.NAME to ReactModuleInfo( - NativeVolumeButtonListenerSpec.NAME, - NativeVolumeButtonListenerSpec.NAME, - canOverrideExistingModule = false, - needsEagerInit = false, - isCxxModule = false, - isTurboModule = true - ) - ) - } -} diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeZipArchive/NativeZipArchive.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeZipArchive/NativeZipArchive.kt deleted file mode 100644 index 81b1276f4..000000000 --- a/android/app/src/main/java/com/rajarsheechatterjee/NativeZipArchive/NativeZipArchive.kt +++ /dev/null @@ -1,137 +0,0 @@ -package com.rajarsheechatterjee.NativeZipArchive - -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactMethod -import com.facebook.react.bridge.ReadableMap -import com.lnreader.spec.NativeZipArchiveSpec -import java.io.File -import java.io.FileOutputStream -import java.net.HttpURLConnection -import java.net.URL -import java.util.zip.ZipEntry -import java.util.zip.ZipFile -import java.util.zip.ZipInputStream -import java.util.zip.ZipOutputStream - -class NativeZipArchive(context: ReactApplicationContext) : NativeZipArchiveSpec(context) { - @ReactMethod - override fun unzip(sourceFilePath: String, distDirPath: String, promise: Promise) { - Thread { - try { - ZipFile(sourceFilePath).use { zis -> - zis.entries().asSequence().filterNot { it.isDirectory }.forEach { zipEntry -> - val newFile = File(distDirPath, zipEntry.name) - newFile.parentFile?.mkdirs() - zis.getInputStream(zipEntry).use { inputStream -> - FileOutputStream(newFile).use { fos -> inputStream.copyTo(fos, 4096) } - } - Thread.yield() - } - } - promise.resolve(null) - } catch (e: Exception) { - promise.reject(e) - } - }.start() - } - - @ReactMethod - override fun zip(sourceDirPath: String, zipFilePath: String, promise: Promise) { - Thread { - try { - FileOutputStream(zipFilePath).use { fos -> - ZipOutputStream(fos).use { zos -> zipProcess(sourceDirPath, zos) } - } - promise.resolve(null) - } catch (e: Exception) { - promise.reject(e) - } - }.start() - } - - @ReactMethod - override fun remoteUnzip( - distDirPath: String, - urlString: String, - headers: ReadableMap, - promise: Promise - ) { - val connection = URL(urlString).openConnection() as HttpURLConnection - Thread { - try { - connection.requestMethod = "GET" - val it = headers.entryIterator - while (it.hasNext()) { - val (key, value) = it.next() - connection.setRequestProperty(key, value.toString()) - } - ZipInputStream(connection.inputStream).use { zis -> - generateSequence { zis.nextEntry } - .filterNot { it.isDirectory } - .forEach { zipEntry -> - val newFile = File(distDirPath, zipEntry.name) - newFile.parentFile?.mkdirs() - FileOutputStream(newFile).use { fos -> zis.copyTo(fos, 4096) } - Thread.yield() - } - } - if (connection.responseCode == 200) { - promise.resolve(null) - } else { - throw Exception("Network request failed") - } - } catch (e: Exception) { - promise.reject(e) - } finally { - connection.disconnect() - } - }.start() - } - - private fun zipProcess(sourceDirPath: String, zos: ZipOutputStream) { - val sourceDir = File(sourceDirPath) - sourceDir.walkBottomUp().filter { it.isFile }.forEach { file -> - val zipFileName = - file.absolutePath.removePrefix(sourceDir.absolutePath).removePrefix("/") - val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}") - zos.putNextEntry(entry) - file.inputStream().use { fis -> - fis.copyTo(zos, 4096) - fis.close() - } - Thread.yield() - } - } - - @ReactMethod - override fun remoteZip( - sourceDirPath: String, - urlString: String, - headers: ReadableMap, - promise: Promise - ) { - Thread { - val connection = URL(urlString).openConnection() as HttpURLConnection - try { - connection.requestMethod = "POST" - val it = headers.entryIterator - while (it.hasNext()) { - val (key, value) = it.next() - connection.setRequestProperty(key, value.toString()) - } - ZipOutputStream(connection.outputStream).use { zipProcess(sourceDirPath, it) } - if (connection.responseCode == 200) { - promise.resolve( - connection.inputStream.bufferedReader().use { it.readText() }) - } else { - throw Exception("Network request failed") - } - } catch (e: Exception) { - promise.reject(e) - } finally { - connection.disconnect() - } - }.start() - } -} diff --git a/android/app/src/main/java/com/rajarsheechatterjee/NativeZipArchive/NativeZipArchivePackage.kt b/android/app/src/main/java/com/rajarsheechatterjee/NativeZipArchive/NativeZipArchivePackage.kt deleted file mode 100644 index 3f2d05dd1..000000000 --- a/android/app/src/main/java/com/rajarsheechatterjee/NativeZipArchive/NativeZipArchivePackage.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.rajarsheechatterjee.NativeZipArchive - -import com.facebook.react.BaseReactPackage -import com.facebook.react.bridge.NativeModule -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.module.model.ReactModuleInfo -import com.facebook.react.module.model.ReactModuleInfoProvider -import com.lnreader.spec.NativeZipArchiveSpec - -class NativeZipArchivePackage : BaseReactPackage() { - override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? = - if (name == NativeZipArchiveSpec.NAME) { - NativeZipArchive(reactContext) - } else { - null - } - - override fun getReactModuleInfoProvider() = ReactModuleInfoProvider { - mapOf( - NativeZipArchiveSpec.NAME to ReactModuleInfo( - NativeZipArchiveSpec.NAME, - NativeZipArchiveSpec.NAME, - canOverrideExistingModule = false, - needsEagerInit = false, - isCxxModule = false, - isTurboModule = true - ) - ) - } -} diff --git a/android/app/src/main/jni/CMakeLists.txt b/android/app/src/main/jni/CMakeLists.txt deleted file mode 100644 index 046708789..000000000 --- a/android/app/src/main/jni/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.13) - -# Define the library name here. -project(appmodules) - -# This file includes all the necessary to let you build your React Native application -include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake) - -# Define where the additional source code lives. We need to crawl back the jni, main, src, app, android folders -target_sources(${CMAKE_PROJECT_NAME} PRIVATE - ../../../../../shared/NativeEpub.cpp - ../../../../../shared/Epub.cpp - ../../../../../shared/pugixml.cpp -) - -# Define where CMake can find the additional header files. We need to crawl back the jni, main, src, app, android folders -target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ../../../../../shared) \ No newline at end of file diff --git a/android/app/src/main/jni/OnLoad.cpp b/android/app/src/main/jni/OnLoad.cpp deleted file mode 100644 index bf31f60b5..000000000 --- a/android/app/src/main/jni/OnLoad.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// This C++ file is part of the default configuration used by apps and is placed -// inside react-native to encapsulate it from user space (so you won't need to -// touch C++/Cmake code at all on Android). -// -// If you wish to customize it (because you want to manually link a C++ library -// or pass a custom compilation flag) you can: -// -// 1. Copy this CMake file inside the `android/app/src/main/jni` folder of your -// project -// 2. Copy the OnLoad.cpp (in this same folder) file inside the same folder as -// above. -// 3. Extend your `android/app/build.gradle` as follows -// -// android { -// // Other config here... -// externalNativeBuild { -// cmake { -// path "src/main/jni/CMakeLists.txt" -// } -// } -// } - -#include -#include -#include -#include -#include -#include -#include - -#ifdef REACT_NATIVE_APP_CODEGEN_HEADER -#include REACT_NATIVE_APP_CODEGEN_HEADER -#endif -#ifdef REACT_NATIVE_APP_COMPONENT_DESCRIPTORS_HEADER -#include REACT_NATIVE_APP_COMPONENT_DESCRIPTORS_HEADER -#endif - -namespace facebook::react -{ - - void registerComponents( - std::shared_ptr registry) - { - // Custom Fabric Components go here. You can register custom - // components coming from your App or from 3rd party libraries here. - // - // providerRegistry->add(concreteComponentDescriptorProvider< - // MyComponentDescriptor>()); - - // We link app local components if available -#ifdef REACT_NATIVE_APP_COMPONENT_REGISTRATION - REACT_NATIVE_APP_COMPONENT_REGISTRATION(registry); -#endif - - // And we fallback to the components autolinked - autolinking_registerProviders(registry); - } - - std::shared_ptr cxxModuleProvider( - const std::string &name, - const std::shared_ptr &jsInvoker) - { - // Here you can provide your CXX Turbo Modules coming from - // either your application or from external libraries. The approach to follow - // is similar to the following (for a module called `NativeCxxModuleExample`): - // - // if (name == NativeCxxModuleExample::kModuleName) { - // return std::make_shared(jsInvoker); - // } - - if (name == NativeEpub::kModuleName) - { - return std::make_shared(jsInvoker); - } - - // And we fallback to the CXX module providers autolinked - return autolinking_cxxModuleProvider(name, jsInvoker); - } - - std::shared_ptr javaModuleProvider( - const std::string &name, - const JavaTurboModule::InitParams ¶ms) - { - // Here you can provide your own module provider for TurboModules coming from - // either your application or from external libraries. The approach to follow - // is similar to the following (for a library called `samplelibrary`): - // - // auto module = samplelibrary_ModuleProvider(name, params); - // if (module != nullptr) { - // return module; - // } - // return FBReactNativeSpec_ModuleProvider(name, params); - - // We link app local modules if available -#ifdef REACT_NATIVE_APP_MODULE_PROVIDER - auto module = REACT_NATIVE_APP_MODULE_PROVIDER(name, params); - if (module != nullptr) - { - return module; - } -#endif - - // We first try to look up core modules - if (auto module = FBReactNativeSpec_ModuleProvider(name, params)) - { - return module; - } - - // And we fallback to the module providers autolinked - if (auto module = autolinking_ModuleProvider(name, params)) - { - return module; - } - - return nullptr; - } - -} // namespace facebook::react - -JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) -{ - return facebook::jni::initialize(vm, [] - { - facebook::react::DefaultTurboModuleManagerDelegate::cxxModuleProvider = - &facebook::react::cxxModuleProvider; - facebook::react::DefaultTurboModuleManagerDelegate::javaModuleProvider = - &facebook::react::javaModuleProvider; - facebook::react::DefaultComponentsRegistry:: - registerComponentDescriptorsFromEntryPoint = - &facebook::react::registerComponents; }); -} \ No newline at end of file diff --git a/android/app/src/main/res/drawable-hdpi/ic_action_name.png b/android/app/src/main/res/drawable-hdpi/ic_action_name.png deleted file mode 100644 index a757dda43..000000000 Binary files a/android/app/src/main/res/drawable-hdpi/ic_action_name.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-hdpi/notification_icon.png b/android/app/src/main/res/drawable-hdpi/notification_icon.png deleted file mode 100644 index 39c04b73d..000000000 Binary files a/android/app/src/main/res/drawable-hdpi/notification_icon.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-mdpi/ic_action_name.png b/android/app/src/main/res/drawable-mdpi/ic_action_name.png deleted file mode 100644 index b70c04c95..000000000 Binary files a/android/app/src/main/res/drawable-mdpi/ic_action_name.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-mdpi/notification_icon.png b/android/app/src/main/res/drawable-mdpi/notification_icon.png deleted file mode 100644 index 3d40c8038..000000000 Binary files a/android/app/src/main/res/drawable-mdpi/notification_icon.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xhdpi/ic_action_name.png b/android/app/src/main/res/drawable-xhdpi/ic_action_name.png deleted file mode 100644 index 854745935..000000000 Binary files a/android/app/src/main/res/drawable-xhdpi/ic_action_name.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xhdpi/notification_icon.png b/android/app/src/main/res/drawable-xhdpi/notification_icon.png deleted file mode 100644 index 3cc027388..000000000 Binary files a/android/app/src/main/res/drawable-xhdpi/notification_icon.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_action_name.png b/android/app/src/main/res/drawable-xxhdpi/ic_action_name.png deleted file mode 100644 index 54aed9a19..000000000 Binary files a/android/app/src/main/res/drawable-xxhdpi/ic_action_name.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xxhdpi/notification_icon.png b/android/app/src/main/res/drawable-xxhdpi/notification_icon.png deleted file mode 100644 index 4d5d81187..000000000 Binary files a/android/app/src/main/res/drawable-xxhdpi/notification_icon.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_action_name.png b/android/app/src/main/res/drawable-xxxhdpi/ic_action_name.png deleted file mode 100644 index eee1fd324..000000000 Binary files a/android/app/src/main/res/drawable-xxxhdpi/ic_action_name.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/notification_icon.png b/android/app/src/main/res/drawable-xxxhdpi/notification_icon.png deleted file mode 100644 index a5626d8f3..000000000 Binary files a/android/app/src/main/res/drawable-xxxhdpi/notification_icon.png and /dev/null differ diff --git a/android/app/src/main/res/drawable/invisible.xml b/android/app/src/main/res/drawable/invisible.xml deleted file mode 100644 index 91e9e2224..000000000 --- a/android/app/src/main/res/drawable/invisible.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/app/src/main/res/drawable/rn_edit_text_material.xml b/android/app/src/main/res/drawable/rn_edit_text_material.xml deleted file mode 100644 index bb6f578c3..000000000 --- a/android/app/src/main/res/drawable/rn_edit_text_material.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/android/app/src/main/res/drawable/splashscreen.xml b/android/app/src/main/res/drawable/splashscreen.xml deleted file mode 100644 index 9f19c56e5..000000000 --- a/android/app/src/main/res/drawable/splashscreen.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/android/app/src/main/res/layout/launch_screen.xml b/android/app/src/main/res/layout/launch_screen.xml deleted file mode 100644 index f3ce30a5f..000000000 --- a/android/app/src/main/res/layout/launch_screen.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index 6521c9a7b..000000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.webp deleted file mode 100644 index 53108616e..000000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp deleted file mode 100644 index bf5b28fef..000000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png deleted file mode 100644 index 377dc288f..000000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index 863294f0a..000000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4d20a97ef..000000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.webp deleted file mode 100644 index 0154c60bf..000000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp deleted file mode 100644 index 1db6cc7e5..000000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png deleted file mode 100644 index 8903564af..000000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 7971a36f6..000000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 7f81c2812..000000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp deleted file mode 100644 index 73b28cba0..000000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp deleted file mode 100644 index d57054667..000000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png deleted file mode 100644 index 4346ebe9c..000000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 53f189a30..000000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 4819c75d3..000000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp deleted file mode 100644 index 74a42e76a..000000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 3752fa4f0..000000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png deleted file mode 100644 index 508786cb4..000000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index eee3f1008..000000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index 2a0753a4a..000000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp deleted file mode 100644 index f9c00433b..000000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index f6f35e81f..000000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png deleted file mode 100644 index f194028f0..000000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 17cbf24de..000000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/app/src/main/res/raw/loading.json b/android/app/src/main/res/raw/loading.json deleted file mode 100644 index 906dbc98d..000000000 --- a/android/app/src/main/res/raw/loading.json +++ /dev/null @@ -1,2 +0,0 @@ -{"v":"5.9.0","fr":90,"ip":0,"op":100,"w":500,"h":500,"nm":"Untitled file","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Formebene 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":29,"s":[0]},{"t":30,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[262.75,248.531,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.281,0.906],[0,0],[-0.188,0.625],[0.188,0.281]],"o":[[0,0],[0,0],[0.156,-0.531],[0,0],[0.188,-0.625],[-0.531,-0.5]],"v":[[34.531,-109.562],[34.938,-103.844],[35.594,-105.625],[36.875,-106.875],[37.688,-108.219],[37.5,-109.344]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100.453,99.743],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42,"s":[0]},{"t":43,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Form 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[21,65],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[25,-109.5],"ix":2},"a":{"a":0,"k":[0,-32.5],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":42,"s":[100,0]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":47,"s":[100,100]},{"t":71,"s":[100,101.54]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 7","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[101,18.75],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[25.25,-35.625],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":47,"s":[0,100]},{"t":52,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 6","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[122.25,18.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[24.625,-75.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":45,"s":[0,100]},{"t":50,"s":[93.456,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[26.625,-68.125],"ix":2},"a":{"a":0,"k":[26.625,-68.125],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 3","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-122,-60],"ix":2},"a":{"a":0,"k":[-32,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":32,"s":[0,47.945]},{"t":38,"s":[119.685,47.945]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[63.5,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-109,-95],"ix":2},"a":{"a":0,"k":[-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[0,47]},{"t":35,"s":[87.222,47]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 2","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[63.5,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-109,9.5],"ix":2},"a":{"a":0,"k":[-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":36,"s":[0,47]},{"t":41,"s":[87.7,47]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 3","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[63.5,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-109,-26],"ix":2},"a":{"a":0,"k":[-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":34,"s":[0,47]},{"t":39,"s":[87.7,47]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 4","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,62],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-56.8,50.5],"ix":2},"a":{"a":0,"k":[0,-31],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":44,"s":[100,0]},{"t":49,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k" -:0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 8","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[50.8,15],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-98.2,44],"ix":2},"a":{"a":0,"k":[-25,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":40,"s":[0,100]},{"t":44,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 7","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[33.6,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-97.9,98],"ix":2},"a":{"a":0,"k":[-16,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":44,"s":[0,99.889]},{"t":49,"s":[100,99.889]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 6","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,80.75],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-106.75,36.525],"ix":2},"a":{"a":0,"k":[0,-40.375],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":39,"s":[100,0]},{"t":44,"s":[100,100]}],"ix":3},"r":{"a":0,"k":-0.026,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-81.5,76.875],"ix":2},"a":{"a":0,"k":[-81.5,76.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[19.5,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-30.125,3],"ix":2},"a":{"a":0,"k":[0,-13],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":56,"s":[100,0]},{"t":59,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 7","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[19,26.779],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[81.562,3],"ix":2},"a":{"a":0,"k":[0,-14],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":56,"s":[100,0]},{"t":59,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 6","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[131,18.25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[25.5,-5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":50,"s":[0,100]},{"t":56,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[25.562,8.062],"ix":2},"a":{"a":0,"k":[25.562,8.062],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":3,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[65.5,111],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847058883368,0.89019613827,0.90588241278,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":57,"s":[-13.5,66.75],"to":[0,0],"ti":[0,0]},{"t":63,"s":[-46,120]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":57,"s":[100,100]},{"t":63,"s":[100,0]}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":57,"s":[0]},{"t":63,"s":[90]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[9.438,0.188],[0,0],[0,0],[41.507,0],[0,0],[-6.5,54.25],[0,0],[0,0]],"o":[[-9.438,-0.188],[0,0],[0,0],[-0.25,0],[0,0],[0.328,-2.742],[0,0],[0,0]],"v":[[14,17.438],[-3.313,17.5],[-3.5,50.875],[-45,95.5],[-38.5,116.75],[14.25,61.25],[14.5,22.75],[14.656,21.688]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Form 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-13.318,67.059],"ix":2},"a":{"a":0,"k":[-13.318,67.059],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 4","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[75,104.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847058883368,0.89019613827,0.90588241278,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[62.75,62.5],"to":[0,0],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63,"s":[60,96],"to":[0,0],"ti":[0,0]},{"t":68,"s":[98,74]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[100,100]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":63,"s":[100,62.5]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":64,"s":[110,50]},{"t":68,"s":[100,0]}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":63,"s":[0]},{"t":68,"s":[-160]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-13,0],[0,0],[-3.938,11.938],[0,0],[0,0],[0,0],[6.5,0],[0,0],[0.31,6.262],[0,0],[9.062,0.125]],"o":[[0,0],[0,0],[13,0],[0,0],[4.009,-12.155],[0,0],[0,0],[0,0],[-6.5,0],[0,0],[-0.312,-6.312],[0,0],[-9.062,-0.125]],"v":[[31.062,16.938],[30.812,95.938],[44.062,109.938],[73.688,109.938],[90.312,97.188],[95.812,74.438],[79.062,69.688],[74.562,83.938],[66.75,90.625],[56.062,90.938],[50.562,84.938],[50.312,21.312],[50.125,17]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Form 4","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[63.312,63.43],"ix":2},"a":{"a":0,"k":[63.312,63.43],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 5","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Ellipse 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[0,0,100]},{"t":30,"s":[340,340,100]}],"ix":6,"l":2,"x":"var $bm_rt;\n$bm_rt = transform.scale;"}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 3","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847100019455,0.890200018883,0.905900001526,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":2,"bm":0,"nm":"Fill","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Group","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":5,"s":[0,0,100]},{"t":25,"s":[400,400,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.070600003004,0.431400001049,0.505900025368,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":2,"bm":0,"nm":"Fill","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Group","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Ellipse 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[0,0,100]},{"t":20,"s":[500,500,100]}],"ix":6,"l":2,"x":"var $bm_rt;\n$bm_rt = transform.scale;"}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 4","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074500001967,0.172499999404,0.20000000298,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":2,"bm":0,"nm":"Fill","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Group","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml deleted file mode 100644 index f0824a660..000000000 --- a/android/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - #FFFFFF - #1F2024 - #023c69 - #202125 - #202125 - #00adb5 - \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml deleted file mode 100644 index b897e3b63..000000000 --- a/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - LNReader - \ No newline at end of file diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 099ab50aa..000000000 --- a/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index d7e8f5a1c..000000000 --- a/android/build.gradle +++ /dev/null @@ -1,22 +0,0 @@ -buildscript { - ext { - buildToolsVersion = "36.0.0" - minSdkVersion = 24 - compileSdkVersion = 36 - targetSdkVersion = 36 - ndkVersion = "27.1.12297006" - kotlinVersion = "2.1.20" - } - repositories { - google() - mavenCentral() - } - dependencies { - classpath("com.android.tools.build:gradle") - classpath("com.facebook.react:react-native-gradle-plugin") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") - } -} - -apply plugin: "com.facebook.react.rootproject" -apply plugin: "expo-root-project" diff --git a/android/gradle.properties b/android/gradle.properties deleted file mode 100644 index 840d7b8aa..000000000 --- a/android/gradle.properties +++ /dev/null @@ -1,56 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. - -# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m - -org.gradle.jvmargs=-Xmx4608m -XX:MaxMetaspaceSize=512m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true - -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app's APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true - -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=true - -# Use this property to specify which architecture you want to build. -# You can also override it from the CLI using -# ./gradlew -PreactNativeArchitectures=x86_64 -reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 -# Use this property to enable support to the new architecture. -# This will allow you to use TurboModules and the Fabric render in -# your application. You should enable this flag either if you want -# to write custom TurboModules/Fabric components OR use libraries that -# are providing them. -newArchEnabled=true - -# Use this property to enable or disable the Hermes JS engine. -# If set to false, you will be using JSC instead. -hermesEnabled=true - -# Enable GIF support in React Native images (~200 B increase) -expo.gif.enabled=false -# Enable webp support in React Native images (~85 KB increase) -expo.webp.enabled=false -# Enable animated webp support (~3.4 MB increase) -# Disabled by default because iOS doesn't support animated webp -expo.webp.animated=false - -# Use this property to enable edge-to-edge display support. -# This allows your app to draw behind system bars for an immersive UI. -# Note: Only works with ReactActivity and should not be used with custom Activity. -edgeToEdgeEnabled=false \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 8bdaf60c7..000000000 Binary files a/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 2a84e188b..000000000 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew deleted file mode 100755 index bbb003279..000000000 --- a/android/gradlew +++ /dev/null @@ -1,251 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH="\\\"\\\"" - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat deleted file mode 100644 index 929f7b6ee..000000000 --- a/android/gradlew.bat +++ /dev/null @@ -1,99 +0,0 @@ -@REM Copyright (c) Meta Platforms, Inc. and affiliates. -@REM -@REM This source code is licensed under the MIT license found in the -@REM LICENSE file in the root directory of this source tree. - -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH= - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index 5bd85f93a..000000000 --- a/android/settings.gradle +++ /dev/null @@ -1,39 +0,0 @@ -pluginManagement { - def reactNativeGradlePlugin = new File( - providers.exec { - workingDir(rootDir) - commandLine("node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })") - }.standardOutput.asText.get().trim() - ).getParentFile().absolutePath - includeBuild(reactNativeGradlePlugin) - - def expoPluginsPath = new File( - providers.exec { - workingDir(rootDir) - commandLine("node", "--print", "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })") - }.standardOutput.asText.get().trim(), - "../android/expo-gradle-plugin" - ).absolutePath - includeBuild(expoPluginsPath) -} - -plugins { - id("com.facebook.react.settings") - id("expo-autolinking-settings") -} - -extensions.configure(com.facebook.react.ReactSettingsExtension) { ex -> - if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') { - ex.autolinkLibrariesFromCommand() - } else { - ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand) - } -} - -rootProject.name = "LNReader" - -expoAutolinking.useExpoModules() -expoAutolinking.useExpoVersionCatalog() - -include(":app") -includeBuild(expoAutolinking.reactNativeGradlePlugin) diff --git a/app.json b/app.json index 835ede139..e788d72de 100644 --- a/app.json +++ b/app.json @@ -1,12 +1,78 @@ { "expo": { + "version": "2.1.2", "name": "LNReader", "slug": "LNReader", "scheme": "lnreader", + "icon": "./assets/native/ic_launcher.png", "plugins": [ + "./plugins/withAndroidCustomizations", + [ + "expo-splash-screen", + { + "image": "./assets/native/ic_launcher_round.png", + "backgroundColor": "#1D1B20", + "resizeMode": "contain", + "dark": { + "image": "./assets/native/ic_launcher_round.png", + "backgroundColor": "#000000" + }, + "android": { + "image": "./assets/native/splash_icon.png", + "backgroundColor": "#132C33", + "imageWidth": 288, + "resizeMode": "native", + "dark": { + "image": "./assets/native/splash_icon.png", + "backgroundColor": "#132C33" + } + } + } + ], "expo-localization", "react-native-edge-to-edge", - "expo-web-browser" - ] + "expo-web-browser", + "./plugins/withReaderAssets", + [ + "expo-build-properties", + { + "android": { + "usesCleartextTraffic": true, + "manifestApplicationAttributes": { + "android:largeHeap": "true", + "android:allowBackup": "true" + } + } + } + ] + ], + "android": { + "versionCode": 4196354, + "package": "com.rajarsheechatterjee.LNReader", + "adaptiveIcon": { + "foregroundImage": "./assets/native/ic_launcher_foreground.png", + "backgroundImage": "./assets/native/ic_launcher_background.png", + "monochromeImage": "./assets/native/ic_launcher_monochrome.png" + }, + "permissions": [ + "android.permission.DOWNLOAD_WITHOUT_NOTIFICATION", + "android.permission.WAKE_LOCK", + "android.permission.FOREGROUND_SERVICE", + "android.permission.FOREGROUND_SERVICE_DATA_SYNC", + "android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK", + "android.permission.POST_NOTIFICATIONS" + ] + }, + "extra": { + "eas": { + "projectId": "3a63596f-e80c-4342-96b7-a8ac48910489" + } + }, + "ios": { + "bundleIdentifier": "com.rajarsheechatterjee.LNReader", + "infoPlist": { + "UIBackgroundModes": ["audio"] + } + } } } diff --git a/assets/android-icons/debug/res/drawable-hdpi/notification_icon.png b/assets/android-icons/debug/res/drawable-hdpi/notification_icon.png new file mode 100644 index 000000000..267e5123b Binary files /dev/null and b/assets/android-icons/debug/res/drawable-hdpi/notification_icon.png differ diff --git a/assets/android-icons/debug/res/drawable-hdpi/splash_icon.png b/assets/android-icons/debug/res/drawable-hdpi/splash_icon.png new file mode 100644 index 000000000..9f5506379 Binary files /dev/null and b/assets/android-icons/debug/res/drawable-hdpi/splash_icon.png differ diff --git a/assets/android-icons/debug/res/drawable-mdpi/notification_icon.png b/assets/android-icons/debug/res/drawable-mdpi/notification_icon.png new file mode 100644 index 000000000..dccaf1004 Binary files /dev/null and b/assets/android-icons/debug/res/drawable-mdpi/notification_icon.png differ diff --git a/assets/android-icons/debug/res/drawable-mdpi/splash_icon.png b/assets/android-icons/debug/res/drawable-mdpi/splash_icon.png new file mode 100644 index 000000000..116d94ad4 Binary files /dev/null and b/assets/android-icons/debug/res/drawable-mdpi/splash_icon.png differ diff --git a/assets/android-icons/debug/res/drawable-xhdpi/notification_icon.png b/assets/android-icons/debug/res/drawable-xhdpi/notification_icon.png new file mode 100644 index 000000000..f4cfc55c0 Binary files /dev/null and b/assets/android-icons/debug/res/drawable-xhdpi/notification_icon.png differ diff --git a/assets/android-icons/debug/res/drawable-xhdpi/splash_icon.png b/assets/android-icons/debug/res/drawable-xhdpi/splash_icon.png new file mode 100644 index 000000000..d0bf1a199 Binary files /dev/null and b/assets/android-icons/debug/res/drawable-xhdpi/splash_icon.png differ diff --git a/assets/android-icons/debug/res/drawable-xxhdpi/notification_icon.png b/assets/android-icons/debug/res/drawable-xxhdpi/notification_icon.png new file mode 100644 index 000000000..e89da831d Binary files /dev/null and b/assets/android-icons/debug/res/drawable-xxhdpi/notification_icon.png differ diff --git a/assets/android-icons/debug/res/drawable-xxhdpi/splash_icon.png b/assets/android-icons/debug/res/drawable-xxhdpi/splash_icon.png new file mode 100644 index 000000000..213bd6e3c Binary files /dev/null and b/assets/android-icons/debug/res/drawable-xxhdpi/splash_icon.png differ diff --git a/assets/android-icons/debug/res/drawable-xxxhdpi/notification_icon.png b/assets/android-icons/debug/res/drawable-xxxhdpi/notification_icon.png new file mode 100644 index 000000000..ff903841a Binary files /dev/null and b/assets/android-icons/debug/res/drawable-xxxhdpi/notification_icon.png differ diff --git a/assets/android-icons/debug/res/drawable-xxxhdpi/splash_icon.png b/assets/android-icons/debug/res/drawable-xxxhdpi/splash_icon.png new file mode 100644 index 000000000..a30f6f1a2 Binary files /dev/null and b/assets/android-icons/debug/res/drawable-xxxhdpi/splash_icon.png differ diff --git a/assets/android-icons/debug/res/drawable/ic_launcher_background.xml b/assets/android-icons/debug/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..e12438038 --- /dev/null +++ b/assets/android-icons/debug/res/drawable/ic_launcher_background.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/assets/android-icons/debug/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 75% rename from android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to assets/android-icons/debug/res/mipmap-anydpi-v26/ic_launcher.xml index 1a4bfc938..3585e9988 100644 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/assets/android-icons/debug/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,5 +2,5 @@ - - \ No newline at end of file + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/assets/android-icons/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 74% rename from android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to assets/android-icons/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml index 4ae7d1237..3585e9988 100644 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/assets/android-icons/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -2,4 +2,5 @@ - \ No newline at end of file + + diff --git a/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher.png b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..a238c5cc0 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher.png differ diff --git a/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_background.png b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_background.png new file mode 100644 index 000000000..cf3a2ae6f Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_foreground.png b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..eedda3bc7 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_monochrome.png b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..f3308306a Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_round.png b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..625247d7f Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher.png b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..09e868b4d Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher.png differ diff --git a/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_background.png b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_background.png new file mode 100644 index 000000000..75c4da67d Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_foreground.png b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..e929231c5 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_monochrome.png b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..4d25c1d77 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_round.png b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..034f72740 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher.png b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..1e90a0684 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_background.png b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_background.png new file mode 100644 index 000000000..2f7e2eb6f Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_foreground.png b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..7a38c3f6b Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_monochrome.png b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..ae317e5f2 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_round.png b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..3d36a85a5 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher.png b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..67547fde5 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_background.png b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..c06374c97 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..db6e9dfc9 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..7b6795cfc Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_round.png b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..76ecc7164 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher.png b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..94203400b Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_background.png b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..74c0bdbe7 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..91e68e11b Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..e048505fe Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_round.png b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..caded9323 Binary files /dev/null and b/assets/android-icons/debug/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/debug/res/values-night/splashscreen_background.xml b/assets/android-icons/debug/res/values-night/splashscreen_background.xml new file mode 100644 index 000000000..60ac481de --- /dev/null +++ b/assets/android-icons/debug/res/values-night/splashscreen_background.xml @@ -0,0 +1,4 @@ + + + #3A2A0C + diff --git a/assets/android-icons/debug/res/values/ic_launcher_background.xml b/assets/android-icons/debug/res/values/ic_launcher_background.xml new file mode 100644 index 000000000..57925e2c0 --- /dev/null +++ b/assets/android-icons/debug/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #4A3208 + diff --git a/assets/android-icons/debug/res/values/splashscreen_background.xml b/assets/android-icons/debug/res/values/splashscreen_background.xml new file mode 100644 index 000000000..60ac481de --- /dev/null +++ b/assets/android-icons/debug/res/values/splashscreen_background.xml @@ -0,0 +1,4 @@ + + + #3A2A0C + diff --git a/assets/android-icons/debug/web/ic_launcher-1024.png b/assets/android-icons/debug/web/ic_launcher-1024.png new file mode 100644 index 000000000..20b5f99d4 Binary files /dev/null and b/assets/android-icons/debug/web/ic_launcher-1024.png differ diff --git a/assets/android-icons/debug/web/ic_launcher-playstore.png b/assets/android-icons/debug/web/ic_launcher-playstore.png new file mode 100644 index 000000000..cb9ed3e3a Binary files /dev/null and b/assets/android-icons/debug/web/ic_launcher-playstore.png differ diff --git a/assets/android-icons/debug/web/splash_icon.png b/assets/android-icons/debug/web/splash_icon.png new file mode 100644 index 000000000..a30f6f1a2 Binary files /dev/null and b/assets/android-icons/debug/web/splash_icon.png differ diff --git a/assets/android-icons/preview/res/drawable-hdpi/notification_icon.png b/assets/android-icons/preview/res/drawable-hdpi/notification_icon.png new file mode 100644 index 000000000..267e5123b Binary files /dev/null and b/assets/android-icons/preview/res/drawable-hdpi/notification_icon.png differ diff --git a/assets/android-icons/preview/res/drawable-hdpi/splash_icon.png b/assets/android-icons/preview/res/drawable-hdpi/splash_icon.png new file mode 100644 index 000000000..4f5555979 Binary files /dev/null and b/assets/android-icons/preview/res/drawable-hdpi/splash_icon.png differ diff --git a/assets/android-icons/preview/res/drawable-mdpi/notification_icon.png b/assets/android-icons/preview/res/drawable-mdpi/notification_icon.png new file mode 100644 index 000000000..dccaf1004 Binary files /dev/null and b/assets/android-icons/preview/res/drawable-mdpi/notification_icon.png differ diff --git a/assets/android-icons/preview/res/drawable-mdpi/splash_icon.png b/assets/android-icons/preview/res/drawable-mdpi/splash_icon.png new file mode 100644 index 000000000..4f6b628f8 Binary files /dev/null and b/assets/android-icons/preview/res/drawable-mdpi/splash_icon.png differ diff --git a/assets/android-icons/preview/res/drawable-xhdpi/notification_icon.png b/assets/android-icons/preview/res/drawable-xhdpi/notification_icon.png new file mode 100644 index 000000000..f4cfc55c0 Binary files /dev/null and b/assets/android-icons/preview/res/drawable-xhdpi/notification_icon.png differ diff --git a/assets/android-icons/preview/res/drawable-xhdpi/splash_icon.png b/assets/android-icons/preview/res/drawable-xhdpi/splash_icon.png new file mode 100644 index 000000000..32eff329e Binary files /dev/null and b/assets/android-icons/preview/res/drawable-xhdpi/splash_icon.png differ diff --git a/assets/android-icons/preview/res/drawable-xxhdpi/notification_icon.png b/assets/android-icons/preview/res/drawable-xxhdpi/notification_icon.png new file mode 100644 index 000000000..e89da831d Binary files /dev/null and b/assets/android-icons/preview/res/drawable-xxhdpi/notification_icon.png differ diff --git a/assets/android-icons/preview/res/drawable-xxhdpi/splash_icon.png b/assets/android-icons/preview/res/drawable-xxhdpi/splash_icon.png new file mode 100644 index 000000000..8c05eafc5 Binary files /dev/null and b/assets/android-icons/preview/res/drawable-xxhdpi/splash_icon.png differ diff --git a/assets/android-icons/preview/res/drawable-xxxhdpi/notification_icon.png b/assets/android-icons/preview/res/drawable-xxxhdpi/notification_icon.png new file mode 100644 index 000000000..ff903841a Binary files /dev/null and b/assets/android-icons/preview/res/drawable-xxxhdpi/notification_icon.png differ diff --git a/assets/android-icons/preview/res/drawable-xxxhdpi/splash_icon.png b/assets/android-icons/preview/res/drawable-xxxhdpi/splash_icon.png new file mode 100644 index 000000000..815db018e Binary files /dev/null and b/assets/android-icons/preview/res/drawable-xxxhdpi/splash_icon.png differ diff --git a/assets/android-icons/preview/res/drawable/ic_launcher_background.xml b/assets/android-icons/preview/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..e12438038 --- /dev/null +++ b/assets/android-icons/preview/res/drawable/ic_launcher_background.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/android-icons/preview/res/mipmap-anydpi-v26/ic_launcher.xml b/assets/android-icons/preview/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..3585e9988 --- /dev/null +++ b/assets/android-icons/preview/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/android-icons/preview/res/mipmap-anydpi-v26/ic_launcher_round.xml b/assets/android-icons/preview/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..3585e9988 --- /dev/null +++ b/assets/android-icons/preview/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher.png b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..ffcb0885c Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher.png differ diff --git a/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_background.png b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_background.png new file mode 100644 index 000000000..74f035be5 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_foreground.png b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..899e13c14 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_monochrome.png b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..f3308306a Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_round.png b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..bcf5964b9 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher.png b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..220a75e19 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher.png differ diff --git a/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_background.png b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_background.png new file mode 100644 index 000000000..c3b63019e Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_foreground.png b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..589944ea1 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_monochrome.png b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..4d25c1d77 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_round.png b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..9e1536f42 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher.png b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..e6a3fad40 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_background.png b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_background.png new file mode 100644 index 000000000..48fd6a276 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_foreground.png b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..a31413d03 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_monochrome.png b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..ae317e5f2 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_round.png b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..bc3f1cd4c Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher.png b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..ff5d99924 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_background.png b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..653617e87 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_foreground.png b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..aaa122181 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..7b6795cfc Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_round.png b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..c078cb948 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher.png b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..1cc2ff5a1 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_background.png b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..9973963e1 Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..85cbc8e3b Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..e048505fe Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_round.png b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..fe09e252a Binary files /dev/null and b/assets/android-icons/preview/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/preview/res/values-night/splashscreen_background.xml b/assets/android-icons/preview/res/values-night/splashscreen_background.xml new file mode 100644 index 000000000..455c360b5 --- /dev/null +++ b/assets/android-icons/preview/res/values-night/splashscreen_background.xml @@ -0,0 +1,4 @@ + + + #DFE2E3 + diff --git a/assets/android-icons/preview/res/values/ic_launcher_background.xml b/assets/android-icons/preview/res/values/ic_launcher_background.xml new file mode 100644 index 000000000..535864714 --- /dev/null +++ b/assets/android-icons/preview/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #23292B + diff --git a/assets/android-icons/preview/res/values/splashscreen_background.xml b/assets/android-icons/preview/res/values/splashscreen_background.xml new file mode 100644 index 000000000..455c360b5 --- /dev/null +++ b/assets/android-icons/preview/res/values/splashscreen_background.xml @@ -0,0 +1,4 @@ + + + #DFE2E3 + diff --git a/assets/android-icons/preview/web/ic_launcher-1024.png b/assets/android-icons/preview/web/ic_launcher-1024.png new file mode 100644 index 000000000..122e030fa Binary files /dev/null and b/assets/android-icons/preview/web/ic_launcher-1024.png differ diff --git a/assets/android-icons/preview/web/ic_launcher-playstore.png b/assets/android-icons/preview/web/ic_launcher-playstore.png new file mode 100644 index 000000000..13f5ad77c Binary files /dev/null and b/assets/android-icons/preview/web/ic_launcher-playstore.png differ diff --git a/assets/android-icons/preview/web/splash_icon.png b/assets/android-icons/preview/web/splash_icon.png new file mode 100644 index 000000000..815db018e Binary files /dev/null and b/assets/android-icons/preview/web/splash_icon.png differ diff --git a/assets/android-icons/release/res/drawable-hdpi/notification_icon.png b/assets/android-icons/release/res/drawable-hdpi/notification_icon.png new file mode 100644 index 000000000..267e5123b Binary files /dev/null and b/assets/android-icons/release/res/drawable-hdpi/notification_icon.png differ diff --git a/assets/android-icons/release/res/drawable-hdpi/splash_icon.png b/assets/android-icons/release/res/drawable-hdpi/splash_icon.png new file mode 100644 index 000000000..6e753f5ab Binary files /dev/null and b/assets/android-icons/release/res/drawable-hdpi/splash_icon.png differ diff --git a/assets/android-icons/release/res/drawable-mdpi/notification_icon.png b/assets/android-icons/release/res/drawable-mdpi/notification_icon.png new file mode 100644 index 000000000..dccaf1004 Binary files /dev/null and b/assets/android-icons/release/res/drawable-mdpi/notification_icon.png differ diff --git a/assets/android-icons/release/res/drawable-mdpi/splash_icon.png b/assets/android-icons/release/res/drawable-mdpi/splash_icon.png new file mode 100644 index 000000000..58e3c61e9 Binary files /dev/null and b/assets/android-icons/release/res/drawable-mdpi/splash_icon.png differ diff --git a/assets/android-icons/release/res/drawable-xhdpi/notification_icon.png b/assets/android-icons/release/res/drawable-xhdpi/notification_icon.png new file mode 100644 index 000000000..f4cfc55c0 Binary files /dev/null and b/assets/android-icons/release/res/drawable-xhdpi/notification_icon.png differ diff --git a/assets/android-icons/release/res/drawable-xhdpi/splash_icon.png b/assets/android-icons/release/res/drawable-xhdpi/splash_icon.png new file mode 100644 index 000000000..0abb4e6b7 Binary files /dev/null and b/assets/android-icons/release/res/drawable-xhdpi/splash_icon.png differ diff --git a/assets/android-icons/release/res/drawable-xxhdpi/notification_icon.png b/assets/android-icons/release/res/drawable-xxhdpi/notification_icon.png new file mode 100644 index 000000000..e89da831d Binary files /dev/null and b/assets/android-icons/release/res/drawable-xxhdpi/notification_icon.png differ diff --git a/assets/android-icons/release/res/drawable-xxhdpi/splash_icon.png b/assets/android-icons/release/res/drawable-xxhdpi/splash_icon.png new file mode 100644 index 000000000..d33552676 Binary files /dev/null and b/assets/android-icons/release/res/drawable-xxhdpi/splash_icon.png differ diff --git a/assets/android-icons/release/res/drawable-xxxhdpi/notification_icon.png b/assets/android-icons/release/res/drawable-xxxhdpi/notification_icon.png new file mode 100644 index 000000000..ff903841a Binary files /dev/null and b/assets/android-icons/release/res/drawable-xxxhdpi/notification_icon.png differ diff --git a/assets/android-icons/release/res/drawable-xxxhdpi/splash_icon.png b/assets/android-icons/release/res/drawable-xxxhdpi/splash_icon.png new file mode 100644 index 000000000..6550deaed Binary files /dev/null and b/assets/android-icons/release/res/drawable-xxxhdpi/splash_icon.png differ diff --git a/assets/android-icons/release/res/drawable/ic_launcher_background.xml b/assets/android-icons/release/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..e12438038 --- /dev/null +++ b/assets/android-icons/release/res/drawable/ic_launcher_background.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/android-icons/release/res/mipmap-anydpi-v26/ic_launcher.xml b/assets/android-icons/release/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..3585e9988 --- /dev/null +++ b/assets/android-icons/release/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/android-icons/release/res/mipmap-anydpi-v26/ic_launcher_round.xml b/assets/android-icons/release/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..3585e9988 --- /dev/null +++ b/assets/android-icons/release/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/android-icons/release/res/mipmap-hdpi/ic_launcher.png b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..4879e95f1 Binary files /dev/null and b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher.png differ diff --git a/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_background.png b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_background.png new file mode 100644 index 000000000..1c7b89ebc Binary files /dev/null and b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_foreground.png b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..30c481424 Binary files /dev/null and b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_monochrome.png b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..f3308306a Binary files /dev/null and b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_round.png b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..90072a139 Binary files /dev/null and b/assets/android-icons/release/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/release/res/mipmap-mdpi/ic_launcher.png b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..5c9bd8e8b Binary files /dev/null and b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher.png differ diff --git a/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_background.png b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_background.png new file mode 100644 index 000000000..57ed6eb2e Binary files /dev/null and b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_foreground.png b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..5ebd85e52 Binary files /dev/null and b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_monochrome.png b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..4d25c1d77 Binary files /dev/null and b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_round.png b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..53f40befb Binary files /dev/null and b/assets/android-icons/release/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher.png b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..f324c66bf Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_background.png b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_background.png new file mode 100644 index 000000000..526015d0c Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_foreground.png b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..c99f4d2d8 Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_monochrome.png b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..ae317e5f2 Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_round.png b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..14558c96c Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher.png b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..5893bc65d Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_background.png b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..cfcb4afbd Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_foreground.png b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..48220d198 Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..7b6795cfc Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_round.png b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..607bc40a3 Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher.png b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..8d70e77ea Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_background.png b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..c63c00dd8 Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_background.png differ diff --git a/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..e7c468306 Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..e048505fe Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_monochrome.png differ diff --git a/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_round.png b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..d0033a9c0 Binary files /dev/null and b/assets/android-icons/release/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/values/ic_launcher_background.xml b/assets/android-icons/release/res/values/ic_launcher_background.xml similarity index 89% rename from android/app/src/main/res/values/ic_launcher_background.xml rename to assets/android-icons/release/res/values/ic_launcher_background.xml index 11e7f5609..f6457e3b8 100644 --- a/android/app/src/main/res/values/ic_launcher_background.xml +++ b/assets/android-icons/release/res/values/ic_launcher_background.xml @@ -1,4 +1,4 @@ #132C33 - \ No newline at end of file + diff --git a/assets/android-icons/release/web/ic_launcher-1024.png b/assets/android-icons/release/web/ic_launcher-1024.png new file mode 100644 index 000000000..a2685efcf Binary files /dev/null and b/assets/android-icons/release/web/ic_launcher-1024.png differ diff --git a/assets/android-icons/release/web/ic_launcher-playstore.png b/assets/android-icons/release/web/ic_launcher-playstore.png new file mode 100644 index 000000000..4ff8f23bc Binary files /dev/null and b/assets/android-icons/release/web/ic_launcher-playstore.png differ diff --git a/assets/android-icons/release/web/splash_icon.png b/assets/android-icons/release/web/splash_icon.png new file mode 100644 index 000000000..6550deaed Binary files /dev/null and b/assets/android-icons/release/web/splash_icon.png differ diff --git a/assets/native/ic_launcher.png b/assets/native/ic_launcher.png new file mode 100644 index 000000000..8d70e77ea Binary files /dev/null and b/assets/native/ic_launcher.png differ diff --git a/assets/native/ic_launcher_background.png b/assets/native/ic_launcher_background.png new file mode 100644 index 000000000..c63c00dd8 Binary files /dev/null and b/assets/native/ic_launcher_background.png differ diff --git a/assets/native/ic_launcher_foreground.png b/assets/native/ic_launcher_foreground.png new file mode 100644 index 000000000..e7c468306 Binary files /dev/null and b/assets/native/ic_launcher_foreground.png differ diff --git a/assets/native/ic_launcher_monochrome.png b/assets/native/ic_launcher_monochrome.png new file mode 100644 index 000000000..e048505fe Binary files /dev/null and b/assets/native/ic_launcher_monochrome.png differ diff --git a/assets/native/ic_launcher_round.png b/assets/native/ic_launcher_round.png new file mode 100644 index 000000000..d0033a9c0 Binary files /dev/null and b/assets/native/ic_launcher_round.png differ diff --git a/assets/native/notification_icon.png b/assets/native/notification_icon.png new file mode 100644 index 000000000..dccaf1004 Binary files /dev/null and b/assets/native/notification_icon.png differ diff --git a/assets/native/splash_icon.png b/assets/native/splash_icon.png new file mode 100644 index 000000000..6550deaed Binary files /dev/null and b/assets/native/splash_icon.png differ diff --git a/android/app/src/main/assets/css/index.css b/assets/reader/css/index.css similarity index 89% rename from android/app/src/main/assets/css/index.css rename to assets/reader/css/index.css index 50a31c03a..b599d4a59 100644 --- a/android/app/src/main/assets/css/index.css +++ b/assets/reader/css/index.css @@ -53,6 +53,10 @@ img { max-width: 100%; } +div:has(> table):not(#LNReader-chapter) { + overflow: auto; +} + table { background-color: var(--theme-onPrimary); border-collapse: collapse; @@ -83,8 +87,6 @@ td { margin-left: var(--readerSettings-padding); margin-right: var(--readerSettings-padding); border-radius: 50px; - border-width: 1; - color: var(--theme-onPrimary); background-color: var(--theme-primary); font-family: var(--readerSettings-fontFamily); font-size: 16px; @@ -93,6 +95,7 @@ td { } .next-button { + color: var(--theme-onPrimary); min-height: 40px; text-overflow: ellipsis; overflow: hidden; @@ -160,6 +163,21 @@ td { background-color: transparent; } +mark.lnreader-search-match { + border-radius: 2px; + color: inherit; + background-color: color-mix( + in srgb, + var(--theme-tertiary) 55%, + transparent + ); +} + +mark.lnreader-search-match-active { + color: var(--theme-onPrimary); + background-color: var(--theme-primary); +} + #Image-Modal { position: fixed; left: 0; diff --git a/android/app/src/main/assets/css/pageReader.css b/assets/reader/css/pageReader.css similarity index 69% rename from android/app/src/main/assets/css/pageReader.css rename to assets/reader/css/pageReader.css index 8efdd26d1..df5cf8c98 100644 --- a/android/app/src/main/assets/css/pageReader.css +++ b/assets/reader/css/pageReader.css @@ -1,13 +1,19 @@ body.page-reader { overflow: hidden; - padding-bottom: unset; + box-sizing: border-box; + height: 100vh; + height: 100dvh; + margin-top: 0; + margin-bottom: 0; + padding-bottom: 0; } body.page-reader > #LNReader-chapter { - height: calc(90vh); + height: calc(100% - var(--pageReader-footerHeight, 0px)); column-width: calc(100vw - var(--readerSettings-padding) * 2); column-gap: calc(var(--readerSettings-padding) * 2); - transition: 200ms; + column-fill: auto; + transition: transform 200ms; } .transition-chapter { height: 100vh; diff --git a/android/app/src/main/assets/css/toolWrapper.css b/assets/reader/css/toolWrapper.css similarity index 100% rename from android/app/src/main/assets/css/toolWrapper.css rename to assets/reader/css/toolWrapper.css diff --git a/assets/reader/css/tts.css b/assets/reader/css/tts.css new file mode 100644 index 000000000..801304fe8 --- /dev/null +++ b/assets/reader/css/tts.css @@ -0,0 +1,84 @@ +#TTS-Controller { + align-items: center; + background: color-mix( + in srgb, + var(--readerSettings-theme) 90%, + var(--readerSettings-textColor) 10% + ); + border: 1px solid + color-mix( + in srgb, + var(--readerSettings-textColor) 30%, + var(--readerSettings-theme) 70% + ); + border-radius: 28px; + box-shadow: 0 3px 12px rgba(0, 0, 0, 0.28); + display: flex; + gap: 2px; + padding: 4px; + position: fixed; + top: 50%; + left: 20px; + opacity: 0.78; + z-index: 2147483647; +} + +#TTS-Controller button { + align-items: center; + background: transparent; + border: 0; + border-radius: 50%; + display: flex; + height: 40px; + justify-content: center; + outline: none; + padding: 8px; + width: 40px; +} + +#TTS-Controller button:active { + background: var(--theme-rippleColor); +} + +#TTS-Controller .tts-drag-handle { + cursor: move; + touch-action: none; + width: 32px; +} + +#TTS-Controller .tts-collapse-toggle { + width: 32px; +} + +#TTS-Controller.collapsed { + border-radius: 50%; + gap: 0; +} + +#TTS-Controller.collapsed > :not(.tts-collapse-toggle) { + display: none; +} + +#TTS-Controller.collapsed .tts-collapse-toggle { + cursor: move; + touch-action: none; + width: 40px; +} + +#TTS-Controller.active { + opacity: 1; +} + +#TTS-Controller svg { + fill: var(--readerSettings-textColor); + width: 20px; + height: 20px; +} + +#TTS-Progress { + color: var(--readerSettings-textColor); + font-size: 11px; + min-width: 34px; + padding-right: 6px; + text-align: center; +} diff --git a/android/app/src/main/assets/fonts/OpenDyslexic3-Regular.ttf b/assets/reader/fonts/OpenDyslexic3-Regular.ttf similarity index 100% rename from android/app/src/main/assets/fonts/OpenDyslexic3-Regular.ttf rename to assets/reader/fonts/OpenDyslexic3-Regular.ttf diff --git a/android/app/src/main/assets/fonts/arbutus-slab.ttf b/assets/reader/fonts/arbutus-slab.ttf similarity index 100% rename from android/app/src/main/assets/fonts/arbutus-slab.ttf rename to assets/reader/fonts/arbutus-slab.ttf diff --git a/android/app/src/main/assets/fonts/domine.ttf b/assets/reader/fonts/domine.ttf similarity index 100% rename from android/app/src/main/assets/fonts/domine.ttf rename to assets/reader/fonts/domine.ttf diff --git a/android/app/src/main/assets/fonts/lato.ttf b/assets/reader/fonts/lato.ttf similarity index 100% rename from android/app/src/main/assets/fonts/lato.ttf rename to assets/reader/fonts/lato.ttf diff --git a/android/app/src/main/assets/fonts/lora.ttf b/assets/reader/fonts/lora.ttf similarity index 100% rename from android/app/src/main/assets/fonts/lora.ttf rename to assets/reader/fonts/lora.ttf diff --git a/android/app/src/main/assets/fonts/noto-sans.ttf b/assets/reader/fonts/noto-sans.ttf similarity index 100% rename from android/app/src/main/assets/fonts/noto-sans.ttf rename to assets/reader/fonts/noto-sans.ttf diff --git a/android/app/src/main/assets/fonts/nunito.ttf b/assets/reader/fonts/nunito.ttf similarity index 100% rename from android/app/src/main/assets/fonts/nunito.ttf rename to assets/reader/fonts/nunito.ttf diff --git a/android/app/src/main/assets/fonts/open-sans.ttf b/assets/reader/fonts/open-sans.ttf similarity index 100% rename from android/app/src/main/assets/fonts/open-sans.ttf rename to assets/reader/fonts/open-sans.ttf diff --git a/android/app/src/main/assets/fonts/pt-sans-bold.ttf b/assets/reader/fonts/pt-sans-bold.ttf similarity index 100% rename from android/app/src/main/assets/fonts/pt-sans-bold.ttf rename to assets/reader/fonts/pt-sans-bold.ttf diff --git a/android/app/src/main/assets/fonts/pt-serif.ttf b/assets/reader/fonts/pt-serif.ttf similarity index 100% rename from android/app/src/main/assets/fonts/pt-serif.ttf rename to assets/reader/fonts/pt-serif.ttf diff --git a/android/app/src/main/assets/js/core.js b/assets/reader/js/core.js similarity index 60% rename from android/app/src/main/assets/js/core.js rename to assets/reader/js/core.js index a218595dd..463a9a4cd 100644 --- a/android/app/src/main/assets/js/core.js +++ b/assets/reader/js/core.js @@ -1,3 +1,19 @@ +window.onUserInteraction = (() => { + let groupStart = 0; + // Group multiple interactions within a short time frame to avoid excessive calls + const groupTime = 3000; + return () => { + let now = Date.now(); + if (now - groupStart < groupTime) { + // We're inside the group + } else { + // We're the first in a while + groupStart = now; + reader.post({ type: 'interaction' }); + } + }; +})(); + /* eslint-disable no-console */ window.reader = new (function () { const { @@ -5,8 +21,6 @@ window.reader = new (function () { chapterGeneralSettings, novel, chapter, - nextChapter, - prevChapter, batteryLevel, autoSaveInterval, DEBUG, @@ -18,6 +32,12 @@ window.reader = new (function () { this.batteryLevel = van.state(batteryLevel); this.readerSettings = van.state(readerSettings); this.generalSettings = van.state(chapterGeneralSettings); + /** + * Bumped whenever the app pushes new adjacent chapters. The chapter is + * rendered before its neighbours are known, so any UI that depends on them + * has to read this state to re-render when they arrive. + */ + this.adjacentVersion = van.state(0); this.chapterElement = document.querySelector('#LNReader-chapter'); this.selection = window.getSelection(); @@ -25,9 +45,17 @@ window.reader = new (function () { this.novel = novel; this.chapter = chapter; - this.nextChapter = nextChapter; - this.prevChapter = prevChapter; + this.nextChapter = undefined; + this.prevChapter = undefined; this.strings = strings; + + /** Called by the app once the neighbouring chapters have been resolved. */ + this.setAdjacentChapters = ({ nextChapter, prevChapter, strings: texts }) => { + this.nextChapter = nextChapter ?? undefined; + this.prevChapter = prevChapter ?? undefined; + Object.assign(this.strings, texts); + this.adjacentVersion.val++; + }; this.autoSaveInterval = autoSaveInterval; this.rawHTML = this.chapterElement.innerHTML; @@ -39,14 +67,20 @@ window.reader = new (function () { 10, ); this.chapterHeight = this.chapterElement.scrollHeight + this.paddingTop; - this.layoutHeight = window.screen.height; - this.layoutWidth = window.screen.width; + this.layoutHeight = window.innerHeight; + this.layoutWidth = window.innerWidth; this.layoutEvent = undefined; this.chapterEndingVisible = van.state(false); this.post = obj => window.ReactNativeWebView.postMessage(JSON.stringify(obj)); this.refresh = () => { + this.layoutHeight = window.innerHeight; + this.layoutWidth = window.innerWidth; + this.paddingTop = + parseFloat( + getComputedStyle(document.body).getPropertyValue('padding-top'), + ) || 0; if (this.generalSettings.val.pageReader) { this.chapterWidth = this.chapterElement.scrollWidth; } else { @@ -54,6 +88,7 @@ window.reader = new (function () { } }; + let loadedFontFamily = readerSettings.fontFamily || ''; van.derive(() => { const settings = this.readerSettings.val; document.documentElement.style.setProperty( @@ -84,7 +119,8 @@ window.reader = new (function () { '--readerSettings-fontFamily', settings.fontFamily, ); - if (settings.fontFamily) { + if (settings.fontFamily && settings.fontFamily !== loadedFontFamily) { + loadedFontFamily = settings.fontFamily; new FontFace( settings.fontFamily, 'url("file:///android_asset/fonts/' + settings.fontFamily + '.ttf")', @@ -92,14 +128,18 @@ window.reader = new (function () { .load() .then(function (loadedFont) { document.fonts.add(loadedFont); + schedulePageCalculation(); }); - } else { + } else if (!settings.fontFamily && loadedFontFamily) { + loadedFontFamily = ''; // have no affect with a font declared in head document.fonts.forEach(fontFace => document.fonts.delete(fontFace)); } + schedulePageCalculation(); }); document.onscrollend = () => { + onUserInteraction(); if (!this.generalSettings.val.pageReader) { this.post({ type: 'save', @@ -111,6 +151,10 @@ window.reader = new (function () { } }; + document.onpointerdown = () => onUserInteraction(); + document.onpointermove = () => onUserInteraction(); + document.onpointerup = () => onUserInteraction(); + if (DEBUG) { // eslint-disable-next-line no-global-assign, no-new-object console = new Object(); @@ -135,6 +179,7 @@ window.tts = new (function () { 'BR', 'STRONG', 'A', + 'MARK', ]; this.prevElement = null; this.currentElement = reader.chapterElement; @@ -240,80 +285,44 @@ window.tts = new (function () { }; this.next = () => { - try { - this.currentElement?.classList?.remove('highlight'); - - // Use array-based approach instead of DOM traversal (no recursion!) - while (this.elementsRead < this.totalElements) { - const nextElement = this.allReadableElements[this.elementsRead]; - if (!nextElement) break; - - const text = this.normalizeText(nextElement.innerText); - if (text) { - // Found valid text - speak it - this.currentElement = nextElement; - this.reading = true; - this.elementsRead++; - this.speak(); - return; - } else { - // Empty text, skip to next in array (no recursion!) - this.elementsRead++; - } - } - - // Reached the end (elementsRead >= totalElements or no more valid elements) - this.reading = false; - const autoPageAdvance = - reader.readerSettings.val.tts?.autoPageAdvance === true; - const hasNextChapter = !!reader.nextChapter; + if (!this.started) return; + reader.post({ type: 'tts-command', data: { command: 'next' } }); + }; - if (autoPageAdvance && hasNextChapter) { - reader.post({ type: 'next', autoStartTTS: true }); - } else { - this.stop(); - const controller = document.getElementById('TTS-Controller'); - if (controller?.firstElementChild) { - controller.firstElementChild.innerHTML = volumnIcon; - } - } - } catch (e) { - this.stop(); - alert('TTS Error: ' + e.message); - } + this.previous = () => { + if (!this.started) return; + reader.post({ type: 'tts-command', data: { command: 'previous' } }); }; this.start = element => { - this.stop(); - this.started = true; const startElement = element ?? reader.chapterElement; - this.currentElement = startElement; - // Get all readable elements from the chapter - this.allReadableElements = this.getAllReadableElements( - reader.chapterElement, - ); + const readableEntries = this.getAllReadableElements(reader.chapterElement) + .map(readableElement => ({ + element: readableElement, + text: this.normalizeText(readableElement.innerText), + })) + .filter(entry => !!entry.text); + this.allReadableElements = readableEntries.map(entry => entry.element); this.totalElements = this.allReadableElements.length; - this.textQueue = this.allReadableElements - .map(el => this.normalizeText(el.innerText)) - .filter(text => !!text); + this.textQueue = readableEntries.map(entry => entry.text); + + const requestedIndex = + element && element !== reader.chapterElement + ? this.allReadableElements.indexOf(startElement) + : 0; + const startIndex = requestedIndex >= 0 ? requestedIndex : 0; + + this.started = this.totalElements > 0; + this.reading = this.started; + this.setActiveIndex(startIndex); reader.post({ type: 'tts-queue', data: { queue: this.textQueue, - startIndex: this.elementsRead, + startIndex, }, }); - - // If starting from a specific element, count how many are before it - if (element && element !== reader.chapterElement) { - const startIndex = this.allReadableElements.indexOf(element); - this.elementsRead = startIndex >= 0 ? startIndex : 0; - } else { - this.elementsRead = 0; - } - - this.next(); }; // Get all readable elements in order @@ -333,46 +342,35 @@ window.tts = new (function () { }; this.resume = () => { - if (!this.reading) { - if ( - this.currentElement && - this.currentElement.id !== 'LNReader-chapter' - ) { - this.speak(); - this.reading = true; - } else { - this.next(); - } - } + if (!this.started) return; + reader.post({ type: 'tts-command', data: { command: 'play' } }); }; this.pause = () => { - this.reading = false; - reader.post({ type: 'pause-speak' }); - reader.post({ type: 'tts-state', data: { isReading: false } }); + if (!this.started) return; + reader.post({ type: 'tts-command', data: { command: 'pause' } }); }; this.rewind = () => { - if (!this.started || !this.currentElement) return; - reader.post({ type: 'pause-speak' }); - this.reading = true; - this.speak(); + if (!this.started) return; + reader.post({ type: 'tts-command', data: { command: 'replay' } }); }; this.seekTo = index => { if (!this.started || !this.allReadableElements.length) return; const targetIndex = Math.max(0, Math.min(index, this.totalElements - 1)); - reader.post({ type: 'pause-speak' }); - this.currentElement?.classList?.remove('highlight'); - this.elementsRead = targetIndex; - this.currentElement = this.allReadableElements[targetIndex]; - this.reading = true; - this.elementsRead++; - this.speak(); + reader.post({ + type: 'tts-command', + data: { command: 'seekTo', index: targetIndex }, + }); }; this.stop = () => { - reader.post({ type: 'stop-speak' }); + reader.post({ type: 'tts-command', data: { command: 'stop' } }); + this.reset(); + }; + + this.reset = () => { this.currentElement?.classList?.remove('highlight'); this.prevElement = null; this.currentElement = reader.chapterElement; @@ -382,12 +380,49 @@ window.tts = new (function () { this.totalElements = 0; this.allReadableElements = []; this.textQueue = []; - reader.post({ type: 'tts-state', data: { isReading: false } }); - // Ensure icon updates to stopped state - const controller = document.getElementById('TTS-Controller'); - if (controller?.firstElementChild) { - controller.firstElementChild.innerHTML = volumnIcon; + const playPauseButton = document.getElementById('TTS-PlayPause'); + if (playPauseButton) playPauseButton.innerHTML = resumeIcon; + const progress = document.getElementById('TTS-Progress'); + if (progress) progress.textContent = ''; + }; + + this.setActiveIndex = index => { + if (!this.allReadableElements.length) return; + const targetIndex = Math.max(0, Math.min(index, this.totalElements - 1)); + this.currentElement?.classList?.remove('highlight'); + this.currentElement = this.allReadableElements[targetIndex]; + this.elementsRead = targetIndex + 1; + this.started = true; + this.scrollToElement(this.currentElement); + this.currentElement.classList.add('highlight'); + const progress = document.getElementById('TTS-Progress'); + if (progress) { + progress.textContent = `${targetIndex + 1}/${this.totalElements}`; + } + }; + + this.setPlaybackState = state => { + this.reading = state === 'playing'; + if (state === 'error') { + this.reset(); + return; + } + const playPauseButton = document.getElementById('TTS-PlayPause'); + if (playPauseButton) { + playPauseButton.innerHTML = this.reading ? pauseIcon : resumeIcon; + } + }; + + this.complete = () => { + this.reading = false; + if ( + reader.readerSettings.val.tts?.autoPageAdvance === true && + reader.nextChapter + ) { + reader.post({ type: 'next', autoStartTTS: true }); + return; } + this.reset(); }; this.isElementInViewport = element => { @@ -411,6 +446,21 @@ window.tts = new (function () { if (!element) return; // Check if element is partially visible (at least some part is in viewport) const rect = element.getBoundingClientRect(); + if (reader.generalSettings.val.pageReader) { + const relativePage = Math.floor( + (rect.left + rect.width / 2) / reader.layoutWidth, + ); + pageReader.movePage( + Math.max( + 0, + Math.min( + pageReader.totalPages.val - 1, + pageReader.page.val + relativePage, + ), + ), + ); + return; + } const windowHeight = window.innerHeight || document.documentElement.clientHeight; const isPartiallyVisible = @@ -446,22 +496,7 @@ window.tts = new (function () { }; this.speak = () => { - if (!this.currentElement) return; - this.prevElement = this.currentElement; - this.scrollToElement(this.currentElement); - this.currentElement.classList.add('highlight'); - const text = this.normalizeText(this.currentElement.innerText); - if (text) { - reader.post({ - type: 'speak', - data: text, - index: this.elementsRead - 1, - total: this.totalElements, - }); - reader.post({ type: 'tts-state', data: { isReading: true } }); - } else { - this.next(); - } + this.rewind(); }; })(); @@ -475,10 +510,16 @@ van.derive(() => { }); window.pageReader = new (function () { + const config = + typeof initialPageReaderConfig === 'undefined' + ? {} + : initialPageReaderConfig; this.page = van.state(0); this.totalPages = van.state(0); + this.ignoreClickUntil = 0; + this.chapterNavigationPending = false; this.chapterEndingVisible = van.state( - initialPageReaderConfig.nextChapterScreenVisible, + config.nextChapterScreenVisible === true, ); this.chapterEnding = document.getElementsByClassName('transition-chapter')[0]; @@ -492,45 +533,54 @@ window.pageReader = new (function () { if (bool) { this.chapterEnding.style.transform = `translateX(${left ? -200 : 0}vw)`; requestAnimationFrame(() => { - if (!instant) this.chapterEnding.style.transition = '200ms'; + if (!instant) { + this.chapterEnding.style.transition = 'transform 200ms'; + } this.chapterEnding.style.transform = 'translateX(-100vw)'; }); this.chapterEndingVisible.val = true; } else { - if (!instant) this.chapterEnding.style.transition = '200ms'; + if (!instant) { + this.chapterEnding.style.transition = 'transform 200ms'; + } this.chapterEnding.style.transform = `translateX(${left ? -200 : 0}vw)`; this.chapterEndingVisible.val = false; } }; - this.movePage = destPage => { + this.movePage = (destPage, { interaction = true, save = true } = {}) => { + if (interaction) { + onUserInteraction(); + } if (this.chapterEndingVisible.val) { - if (destPage < 0) { - this.showChapterEnding(false); + if (this.chapterNavigationPending) { return; } - if (destPage < this.totalPages.val) { - this.showChapterEnding(false, false, true); + if (destPage < 0) { + this.showChapterEnding(false); return; } - if (destPage >= this.totalPages.val) { - return reader.post({ type: 'next' }); - } + this.showChapterEnding(false, false, true); + return; } destPage = parseInt(destPage, 10); if (destPage < 0) { + if (!reader.prevChapter) return; document.getElementsByClassName('transition-chapter')[0].innerText = reader.prevChapter.name; this.showChapterEnding(true, false, true); + this.chapterNavigationPending = true; setTimeout(() => { reader.post({ type: 'prev' }); }, 200); return; } if (destPage >= this.totalPages.val) { + if (!reader.nextChapter) return; document.getElementsByClassName('transition-chapter')[0].innerText = reader.nextChapter.name; this.showChapterEnding(true); + this.chapterNavigationPending = true; setTimeout(() => { reader.post({ type: 'next' }); }, 200); @@ -545,17 +595,53 @@ window.pageReader = new (function () { 10, ); - if (newProgress > reader.chapter.progress) { + if (save && newProgress > reader.chapter.progress) { + reader.chapter.progress = newProgress; reader.post({ type: 'save', - data: parseInt( - ((pageReader.page.val + 1) / pageReader.totalPages.val) * 100, - 10, - ), + data: newProgress, }); } }; + this.repaginate = ratio => { + const previousTotal = this.totalPages.val; + const currentRatio = + previousTotal > 0 ? (this.page.val + 1) / previousTotal : 0; + const positionRatio = Number.isFinite(ratio) + ? ratio + : currentRatio || reader.chapter.progress / 100; + + reader.refresh(); + const chapterStyle = getComputedStyle(reader.chapterElement); + const horizontalPadding = + (parseFloat(chapterStyle.paddingLeft) || 0) + + (parseFloat(chapterStyle.paddingRight) || 0); + this.totalPages.val = Math.max( + 1, + Math.floor( + (reader.chapterWidth + horizontalPadding) / + (reader.chapterElement.clientWidth || reader.layoutWidth) + + 0.001, + ), + ); + + if (this.chapterEndingVisible.val) { + return; + } + + const destination = Math.min( + this.totalPages.val - 1, + Math.max( + 0, + Math.round( + this.totalPages.val * Math.min(1, Math.max(0, positionRatio)), + ) - 1, + ), + ); + this.movePage(destination, { interaction: false, save: false }); + }; + van.derive(() => { // ignore if initial or other states change if ( @@ -570,27 +656,20 @@ window.pageReader = new (function () { (window.scrollY + reader.layoutHeight) / reader.chapterHeight, ); document.body.classList.add('page-reader'); - setTimeout(() => { - reader.refresh(); - this.totalPages.val = parseInt( - (reader.chapterWidth + reader.readerSettings.val.padding * 2) / - reader.layoutWidth, - 10, - ); - this.movePage(this.totalPages.val * ratio); - }, 100); + requestAnimationFrame(() => this.repaginate(ratio)); } else { - reader.chapterElement.style = ''; + const ratio = + this.totalPages.val > 0 ? (this.page.val + 1) / this.totalPages.val : 0; + reader.chapterElement.style.removeProperty('transform'); + reader.chapterElement.style.removeProperty('transition'); document.body.classList.remove('page-reader'); - setTimeout(() => { + requestAnimationFrame(() => { reader.refresh(); window.scrollTo({ - top: - (reader.chapterHeight * (this.page.val + 1)) / this.totalPages.val - - reader.layoutHeight, + top: reader.chapterHeight * ratio - reader.layoutHeight, behavior: 'smooth', }); - }, 100); + }); } }); })(); @@ -601,48 +680,124 @@ document.addEventListener('DOMContentLoaded', () => { } }); -function calculatePages() { +/** Scroll offset the reading position was restored to, in scroll mode. */ +let restoredScrollTop = null; +let positionRestored = false; + +function calculatePages(behavior = 'instant') { reader.refresh(); if (reader.generalSettings.val.pageReader) { - pageReader.totalPages.val = parseInt( - (reader.chapterWidth + reader.readerSettings.val.padding * 2) / - reader.layoutWidth, - 10, - ); - - if (initialPageReaderConfig.nextChapterScreenVisible) return; - - pageReader.movePage( - Math.max( - 0, - Math.round( - (pageReader.totalPages.val * reader.chapter.progress) / 100, - ) - 1, - ), - ); + pageReader.repaginate(reader.chapter.progress / 100); } else { - window.scrollTo({ - top: - (reader.chapterHeight * reader.chapter.progress) / 100 - - reader.layoutHeight, - behavior: 'smooth', - }); + restoredScrollTop = + (reader.chapterHeight * reader.chapter.progress) / 100 - + reader.layoutHeight; + window.scrollTo({ top: restoredScrollTop, behavior }); + } +} + +let pageCalculationFrame; +let pendingPageRatio; +function schedulePageCalculation(ratio) { + if (Number.isFinite(ratio)) { + pendingPageRatio = ratio; } + if ( + pageCalculationFrame || + !window.pageReader || + !reader.generalSettings.val.pageReader + ) { + return; + } + pageCalculationFrame = requestAnimationFrame(() => { + pageCalculationFrame = undefined; + const nextRatio = pendingPageRatio; + pendingPageRatio = undefined; + pageReader.repaginate(nextRatio); + }); } const ro = new ResizeObserver(() => { - if (pageReader.totalPages.val) { - calculatePages(); + if (pageReader.totalPages.val && reader.generalSettings.val.pageReader) { + schedulePageCalculation(); } }); ro.observe(reader.chapterElement); +reader.chapterElement.addEventListener( + 'load', + () => schedulePageCalculation(), + true, +); + +let viewportResizeTimer; +window.addEventListener('resize', () => { + clearTimeout(viewportResizeTimer); + viewportResizeTimer = setTimeout(() => { + if (reader.generalSettings.val.pageReader) { + schedulePageCalculation(); + } else { + reader.refresh(); + } + }, 100); +}); -// Also call once on load -window.addEventListener('load', () => { - document.fonts.ready.then(() => { - requestAnimationFrame(() => setTimeout(calculatePages, 0)); +/** + * Fonts and images can still change the chapter height after the position was + * restored. Correct it when that happens - but only while the reader is sitting + * exactly where it was put, otherwise this would yank the page from under + * someone who has already started reading. + */ +const correctReadingPosition = () => { + if (reader.generalSettings.val.pageReader || restoredScrollTop === null) { + return; + } + + const previousHeight = reader.chapterHeight; + reader.refresh(); + if ( + reader.chapterHeight !== previousHeight && + Math.abs(window.scrollY - Math.max(0, restoredScrollTop)) < 4 + ) { + calculatePages(); + } +}; + +const restoreReadingPosition = () => { + requestAnimationFrame(() => + setTimeout(() => { + positionRestored = true; + calculatePages(); + // Deliberately not awaited before restoring: `document.fonts.ready` does + // not resolve until the document has finished loading, which is what this + // is trying to avoid waiting for. + document.fonts.ready.then(correctReadingPosition); + }, 0), + ); +}; + +// Restore as soon as the chapter itself is parsed and styled. Waiting for +// `load` means waiting for every image in the chapter, which can take seconds - +// the reader would sit at the top of the chapter until then. +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', restoreReadingPosition, { + once: true, }); +} else { + restoreReadingPosition(); +} + +window.addEventListener('load', () => { + if (!positionRestored) { + restoreReadingPosition(); + return; + } + + if (reader.generalSettings.val.pageReader) { + schedulePageCalculation(); + } else { + correctReadingPosition(); + } }); // click handler @@ -666,6 +821,9 @@ window.addEventListener('load', () => { return 'center'; }; document.onclick = e => { + if (Date.now() < pageReader.ignoreClickUntil) { + return; + } const { clientX, clientY } = e; const { x, y } = { x: clientX / reader.layoutWidth, @@ -729,7 +887,10 @@ window.addEventListener('load', () => { const diffX = e.changedTouches[0].screenX - this.initialX; const diffY = e.changedTouches[0].screenY - this.initialY; if (reader.generalSettings.val.pageReader) { - reader.chapterElement.style.transition = '200ms'; + if (Math.abs(diffX) > 8 || Math.abs(diffY) > 8) { + pageReader.ignoreClickUntil = Date.now() + 400; + } + reader.chapterElement.style.transition = 'transform 200ms'; const diffXPercentage = diffX / reader.layoutWidth; if (diffXPercentage < -0.3) { pageReader.movePage(pageReader.page.val + 1); @@ -764,6 +925,12 @@ window.addEventListener('load', () => { // text options (function () { + // What the chapter element currently holds. The document is delivered with + // the untransformed chapter already parsed, so writing the same markup back + // would re-parse and re-layout the whole chapter (and restart image loads) + // for nothing - which is exactly what happens when neither transform is on. + let appliedHTML = reader.rawHTML; + van.derive(() => { let html = reader.rawHTML; if (reader.generalSettings.val.bionicReading) { @@ -780,14 +947,31 @@ window.addEventListener('load', () => { `${ /\/p>/.test(_) ? _.replace( - /
\s*
(?:(?=\s*<\/?p[> ])|(?<=<\/?p\b[^>]*>
\s*
))\s*/g, + /
\s*
(?:(?=\s*<\/?p[> ])|(?<=<\/?p(?:>| [^>]+>)
\s*
))\s*/g, '', ) : _ }`, ) //if p found, delete all double br near p - .replace(/
(?:(?=\s*<\/?p[> ])|(?<=<\/?p>\s*
))\s*/g, ''); + .replace( + /
(?:(?=\s*<\/?p[> ])|(?<=<\/?p(?:>| [^>]+>)(?:<[^>]+>)*\s*
))\s*/g, + '', + ); } + if (html === appliedHTML) { + return; + } + reader.chapterElement.innerHTML = html; + appliedHTML = html; + reader.refresh(); + schedulePageCalculation(); + + // Replacing the markup dropped the highlights, so restore the search. + const searchQuery = window.readerSearch?.query; + const searchIndex = window.readerSearch?.index; + if (searchQuery) { + window.readerSearch.search(searchQuery, searchIndex); + } }); })(); diff --git a/assets/reader/js/icons.js b/assets/reader/js/icons.js new file mode 100644 index 000000000..bde94e854 --- /dev/null +++ b/assets/reader/js/icons.js @@ -0,0 +1,14 @@ +const pauseIcon = + ''; +const resumeIcon = + ''; +const previousParagraphIcon = + ''; +const nextParagraphIcon = + ''; +const dragHandleIcon = + ''; +const minimizeIcon = + ''; +const textToSpeechIcon = + ''; diff --git a/android/app/src/main/assets/js/index.d.ts b/assets/reader/js/index.d.ts similarity index 81% rename from android/app/src/main/assets/js/index.d.ts rename to assets/reader/js/index.d.ts index 84190e09f..cfdca69bb 100644 --- a/android/app/src/main/assets/js/index.d.ts +++ b/assets/reader/js/index.d.ts @@ -16,10 +16,13 @@ export interface Reader { generalSettings: State; readerSettings: State; batteryLevel: State; + /** Bumped whenever the app pushes newly resolved adjacent chapters. */ + adjacentVersion: State; novel: NovelInfo; chapter: ChapterInfo; nextChapter?: ChapterInfo; + prevChapter?: ChapterInfo; autoSaveInterval: number; rawHTML: string; strings: { @@ -28,6 +31,12 @@ export interface Reader { noNextChapter: string; }; + setAdjacentChapters: (adjacent: { + nextChapter?: ChapterInfo; + prevChapter?: ChapterInfo; + strings?: Partial; + }) => void; + //layout props paddingTop: number; layoutHeight: number; diff --git a/assets/reader/js/index.js b/assets/reader/js/index.js new file mode 100644 index 000000000..3cd62d3ce --- /dev/null +++ b/assets/reader/js/index.js @@ -0,0 +1,581 @@ +const { div, p, img, button, span } = van.tags; + +/** + * Registers a callback for the scrolled-through ratio, coalesced to one call + * per frame. Every scroll-driven indicator shares this: separate `scroll` + * listeners all re-run the same work on the same frames, and that shows up + * directly as scrolling smoothness. + */ +const onScrollRatio = (() => { + const callbacks = []; + let queued = false; + + window.addEventListener( + 'scroll', + () => { + if (queued) { + return; + } + queued = true; + requestAnimationFrame(() => { + queued = false; + const ratio = Math.min( + 1, + (window.scrollY + reader.layoutHeight) / reader.chapterHeight, + ); + for (const callback of callbacks) { + callback(ratio); + } + }); + }, + { passive: true }, + ); + + return callback => callbacks.push(callback); +})(); + +const ChapterEnding = () => { + return () => + reader.generalSettings.val.pageReader + ? div() + : div(div({ class: 'info-text' }, reader.strings.finished), () => + // Reading `adjacentVersion` subscribes this binding to the adjacent + // chapters being pushed in after the chapter itself was rendered. + reader.adjacentVersion.val >= 0 && reader.nextChapter + ? button( + { + class: 'next-button', + onclick: e => { + e.stopPropagation(); + reader.post({ type: 'next' }); + }, + }, + reader.strings.nextChapter, + ) + : div({ class: 'info-text' }, reader.strings.noNextChapter), + ); +}; + +const Scrollbar = () => { + const horizontal = van.derive( + () => !reader.generalSettings.val.verticalSeekbar, + ); + let lock = false; + const percentage = van.state(0); + const update = ratio => { + if (ratio === undefined) { + ratio = (window.scrollY + reader.layoutHeight) / reader.chapterHeight; + } + if (ratio > 1) { + ratio = 1; + } + if (reader.generalSettings.val.pageReader) { + pageReader.movePage( + parseInt(pageReader.totalPages.val * Math.min(0.99, ratio)), + ); + return; + } + percentage.val = parseInt(ratio * 100); + if (lock) { + window.scrollTo({ + top: reader.chapterHeight * ratio - reader.layoutHeight, + behavior: 'instant', + }); + } + }; + onScrollRatio( + ratio => !lock && !reader.generalSettings.val.pageReader && update(ratio), + ); + return div( + { id: 'ScrollBar' }, + div( + { class: 'scrollbar-item scrollbar-text', id: 'scrollbar-percentage' }, + () => + reader.generalSettings.val.pageReader + ? pageReader.page.val + 1 + : percentage.val, + ), + div( + { class: 'scrollbar-item', id: 'scrollbar-slider' }, + div( + { id: 'scrollbar-track' }, + div( + { + id: 'scrollbar-progress', + style: () => { + const percentageValue = reader.generalSettings.val.pageReader + ? ((pageReader.page.val + 1) / pageReader.totalPages.val) * 100 + : percentage.val; + return horizontal.val + ? `width: ${percentageValue}%; height: 100%;` + : `height: ${percentageValue}%; width: 100%;`; + }, + }, + div( + { + id: 'scrollbar-thumb-wrapper', + ontouchstart: () => { + lock = true; + }, + ontouchend: () => { + lock = false; + }, + ontouchmove: function (e) { + const slider = this.parentElement.parentElement.parentElement; + const sliderHeight = horizontal.val + ? slider.clientWidth + : slider.clientHeight; + const sliderOffsetY = horizontal.val + ? slider.getBoundingClientRect().left + : slider.getBoundingClientRect().top; + const ratio = + ((horizontal.val + ? e.changedTouches[0].clientX + : e.changedTouches[0].clientY) - + sliderOffsetY) / + sliderHeight; + update(ratio < 0 ? 0 : ratio); + }, + }, + div({ id: 'scrollbar-thumb' }), + ), + ), + ), + ), + div( + { + class: 'scrollbar-item scrollbar-text', + id: 'scrollbar-percentage-max', + }, + () => + reader.generalSettings.val.pageReader ? pageReader.totalPages.val : 100, + ), + ); +}; + +const ToolWrapper = () => { + const horizontal = van.derive( + () => !reader.generalSettings.val.verticalSeekbar, + ); + return div( + { + id: 'ToolWrapper', + class: () => + `${reader.hidden.val ? 'hidden' : ''} ${ + horizontal.val ? 'horizontal' : '' + }`, + }, + Scrollbar(), + ); +}; + +const ImageModal = ({ src }) => { + return div( + { + id: 'Image-Modal', + class: () => (src.val ? 'show' : ''), + onclick: e => { + if (e.target.id !== 'Image-Modal-img') { + e.stopPropagation(); + src.val = ''; + } + }, + }, + img({ + id: 'Image-Modal-img', + src: src, + alt: () => (src.val ? `Cant not render image from ${src.val}` : ''), + }), + ); +}; + +const ModalWrapper = () => { + const imgSrc = van.state(''); + const showImage = src => { + imgSrc.val = src; + reader.viewport.setAttribute( + 'content', + 'width=device-width, initial-scale=1.0, maximum-scale=10', + ); + }; + const hideImage = () => { + imgSrc.val = ''; + reader.viewport.setAttribute( + 'content', + 'width=device-width, initial-scale=1.0, maximum-scale=1.0', + ); + }; + + document.addEventListener('contextmenu', e => { + if (e.target instanceof HTMLImageElement) { + if (!imgSrc.val) { + showImage(e.target.src); + } else { + hideImage(); + } + } + }); + return div(ImageModal({ src: imgSrc })); +}; + +const Footer = () => { + const percentage = van.state(0); + const time = van.state( + new Date().toLocaleTimeString(undefined, { + hour: '2-digit', + minute: '2-digit', + hour12: false, + }), + ); + onScrollRatio(ratio => { + percentage.val = parseInt(ratio * 100); + }); + setInterval(() => { + if (!reader.generalSettings.val.showBatteryAndTime) { + return; + } + time.val = new Date().toLocaleTimeString(undefined, { + hour: '2-digit', + minute: '2-digit', + hour12: false, + }); + }, 10000); + const wrapper = div( + { + id: 'reader-footer-wrapper', + class: () => + reader.generalSettings.val.showBatteryAndTime || + reader.generalSettings.val.showScrollPercentage + ? '' + : 'd-none', + }, + div( + { id: 'reader-footer' }, + + div( + { + id: 'reader-battery', + class: () => + `reader-footer-item ${ + reader.generalSettings.val.showBatteryAndTime ? '' : 'hidden' + }`, + }, + () => Math.ceil(reader.batteryLevel.val * 100) + '%', + ), + div( + { + id: 'reader-percentage', + class: () => + `reader-footer-item ${ + reader.generalSettings.val.showScrollPercentage ? '' : 'hidden' + }`, + }, + () => + reader.generalSettings.val.pageReader + ? `${pageReader.page.val + 1}/${pageReader.totalPages.val}` + : percentage.val + '%', + ), + div( + { + id: 'reader-time', + class: () => + `reader-footer-item ${ + reader.generalSettings.val.showBatteryAndTime ? '' : 'hidden' + }`, + }, + time, + ), + ), + ); + const footerObserver = new ResizeObserver(() => { + document.documentElement.style.setProperty( + '--pageReader-footerHeight', + `${wrapper.offsetHeight}px`, + ); + }); + footerObserver.observe(wrapper); + requestAnimationFrame(() => { + document.documentElement.style.setProperty( + '--pageReader-footerHeight', + `${wrapper.offsetHeight}px`, + ); + }); + return wrapper; +}; + +const TTSController = () => { + let controllerElement = null; + let hoverElement = null; + let dragOffsetX = 0; + let dragOffsetY = 0; + let dragStartX = 0; + let dragStartY = 0; + let moved = false; + const collapsed = van.state(true); + let collapseButtonElement = null; + let lastBubbleTouchEnd = 0; + + const stopEvent = e => { + e.preventDefault(); + e.stopPropagation(); + }; + + const setControllerPosition = touch => { + const maxLeft = Math.max( + 8, + window.innerWidth - controllerElement.offsetWidth - 8, + ); + const maxTop = Math.max( + 8, + window.innerHeight - controllerElement.offsetHeight - 8, + ); + const left = Math.min(maxLeft, Math.max(8, touch.clientX - dragOffsetX)); + const top = Math.min(maxTop, Math.max(8, touch.clientY - dragOffsetY)); + + controllerElement.style.left = `${left}px`; + controllerElement.style.top = `${top}px`; + controllerElement.style.right = 'auto'; + controllerElement.style.bottom = 'auto'; + }; + + const clampControllerToViewport = () => { + const bounds = controllerElement.getBoundingClientRect(); + const maxLeft = Math.max(8, window.innerWidth - bounds.width - 8); + const maxTop = Math.max(8, window.innerHeight - bounds.height - 8); + + controllerElement.style.left = `${Math.min( + maxLeft, + Math.max(8, bounds.left), + )}px`; + controllerElement.style.top = `${Math.min( + maxTop, + Math.max(8, bounds.top), + )}px`; + controllerElement.style.right = 'auto'; + controllerElement.style.bottom = 'auto'; + }; + + const setCollapsed = value => { + collapsed.val = value; + controllerElement ??= document.getElementById('TTS-Controller'); + collapseButtonElement ??= controllerElement.querySelector( + '.tts-collapse-toggle', + ); + collapseButtonElement.setAttribute( + 'aria-label', + collapsed.val + ? 'Expand text-to-speech controls' + : 'Minimize text-to-speech controls', + ); + collapseButtonElement.innerHTML = collapsed.val + ? textToSpeechIcon + : minimizeIcon; + requestAnimationFrame(clampControllerToViewport); + }; + + const startDrag = e => { + stopEvent(e); + controllerElement ??= document.getElementById('TTS-Controller'); + const touch = e.changedTouches[0]; + const bounds = controllerElement.getBoundingClientRect(); + dragOffsetX = touch.clientX - bounds.left; + dragOffsetY = touch.clientY - bounds.top; + moved = false; + controllerElement.classList.add('active'); + controllerElement.style.transition = 'none'; + }; + + const moveDrag = e => { + stopEvent(e); + const touch = e.changedTouches[0]; + + moved = true; + setControllerPosition(touch); + + const newHoverElement = document + .elementsFromPoint(touch.clientX, touch.clientY) + .find( + element => + !element.closest('#TTS-Controller') && + !element.id.includes('scrollbar') && + tts.readable(element), + ); + hoverElement?.classList.remove('highlight'); + hoverElement = newHoverElement ?? null; + hoverElement?.classList.add('highlight'); + }; + + const endDrag = e => { + stopEvent(e); + controllerElement.classList.remove('active'); + controllerElement.style.transition = ''; + + if (moved && hoverElement && reader.generalSettings.val.TTSEnable) { + tts.start(hoverElement); + } + hoverElement?.classList.remove('highlight'); + hoverElement = null; + moved = false; + }; + + const startBubbleDrag = e => { + if (!collapsed.val) { + return; + } + stopEvent(e); + controllerElement ??= document.getElementById('TTS-Controller'); + const touch = e.changedTouches[0]; + const bounds = controllerElement.getBoundingClientRect(); + dragOffsetX = touch.clientX - bounds.left; + dragOffsetY = touch.clientY - bounds.top; + dragStartX = touch.clientX; + dragStartY = touch.clientY; + moved = false; + controllerElement.classList.add('active'); + controllerElement.style.transition = 'none'; + }; + + const moveBubbleDrag = e => { + if (!collapsed.val) { + return; + } + stopEvent(e); + const touch = e.changedTouches[0]; + if ( + !moved && + Math.hypot(touch.clientX - dragStartX, touch.clientY - dragStartY) < 6 + ) { + return; + } + moved = true; + setControllerPosition(touch); + }; + + const finishBubbleDrag = () => { + controllerElement.classList.remove('active'); + controllerElement.style.transition = ''; + moved = false; + }; + + const endBubbleDrag = e => { + if (!collapsed.val) { + return; + } + stopEvent(e); + const shouldExpand = !moved; + lastBubbleTouchEnd = Date.now(); + finishBubbleDrag(); + if (shouldExpand) { + setCollapsed(false); + } + }; + + const cancelBubbleDrag = e => { + if (!collapsed.val) { + return; + } + stopEvent(e); + lastBubbleTouchEnd = Date.now(); + finishBubbleDrag(); + }; + + const toggleCollapsed = e => { + e.stopPropagation(); + if (Date.now() - lastBubbleTouchEnd < 500) { + return; + } + setCollapsed(!collapsed.val); + }; + + const runCommand = command => e => { + e.stopPropagation(); + if (reader.generalSettings.val.TTSEnable) { + command(); + } + }; + + collapseButtonElement = button({ + type: 'button', + class: 'tts-collapse-toggle', + 'aria-label': 'Expand text-to-speech controls', + innerHTML: textToSpeechIcon, + ontouchstart: startBubbleDrag, + ontouchmove: moveBubbleDrag, + ontouchend: endBubbleDrag, + ontouchcancel: cancelBubbleDrag, + onclick: toggleCollapsed, + }); + + return div( + { + id: 'TTS-Controller', + class: () => + [ + reader.generalSettings.val.TTSEnable ? '' : 'hidden', + collapsed.val ? 'collapsed' : '', + ] + .filter(Boolean) + .join(' '), + style: () => + reader.generalSettings.val.TTSEnable + ? 'pointer-events: auto;' + : 'pointer-events: none; display: none !important; opacity: 0; transition: none;', + onclick: e => e.stopPropagation(), + }, + button({ + type: 'button', + class: 'tts-drag-handle', + 'aria-label': 'Move text-to-speech controls', + innerHTML: dragHandleIcon, + ontouchstart: startDrag, + ontouchmove: moveDrag, + ontouchend: endDrag, + ontouchcancel: endDrag, + onclick: stopEvent, + }), + collapseButtonElement, + button({ + type: 'button', + class: 'tts-control-button', + 'aria-label': 'Previous paragraph', + innerHTML: previousParagraphIcon, + onclick: runCommand(() => tts.previous()), + }), + button({ + id: 'TTS-PlayPause', + type: 'button', + class: 'tts-control-button tts-play-pause', + 'aria-label': 'Play text-to-speech', + innerHTML: resumeIcon, + onclick: runCommand(() => { + if (tts.reading) { + tts.pause(); + } else if (tts.started) { + tts.resume(); + } else { + tts.start(); + } + }), + }), + button({ + type: 'button', + class: 'tts-control-button', + 'aria-label': 'Next paragraph', + innerHTML: nextParagraphIcon, + onclick: runCommand(() => tts.next()), + }), + span({ id: 'TTS-Progress', 'aria-hidden': 'true' }), + ); +}; + +const ReaderUI = () => { + return div( + ToolWrapper(), + TTSController(), + ModalWrapper(), + Footer(), + ChapterEnding(), + ); +}; + +van.add(document.getElementById('reader-ui'), ReaderUI()); diff --git a/android/app/src/main/assets/js/polyfill-onscrollend.js b/assets/reader/js/polyfill-onscrollend.js similarity index 100% rename from android/app/src/main/assets/js/polyfill-onscrollend.js rename to assets/reader/js/polyfill-onscrollend.js diff --git a/assets/reader/js/search.js b/assets/reader/js/search.js new file mode 100644 index 000000000..a0376f6a9 --- /dev/null +++ b/assets/reader/js/search.js @@ -0,0 +1,460 @@ +window.readerSearch = new (function () { + const MIN_QUERY_LENGTH = 3; + const SEGMENT_BATCH_SIZE = 40; + const MAX_RENDERED_MATCHES = 1500; + const SPECIAL_CHARACTER_REGEX = /[^\p{L}\p{N}\s]/u; + const INLINE_TEXT_ELEMENTS = new Set([ + 'A', + 'ABBR', + 'B', + 'BDI', + 'BDO', + 'CITE', + 'CODE', + 'DATA', + 'DFN', + 'EM', + 'I', + 'KBD', + 'MARK', + 'Q', + 'RP', + 'RT', + 'RUBY', + 'S', + 'SAMP', + 'SMALL', + 'SPAN', + 'STRONG', + 'SUB', + 'SUP', + 'TIME', + 'U', + 'VAR', + 'WBR', + ]); + + this.query = ''; + this.index = -1; + this.matches = []; + this.total = 0; + this.isTruncated = false; + this.searchToken = 0; + this.pendingSearchTimer = null; + this.cachedSegments = null; + this.lastSegmentedChapterId = null; + this.isValidState = true; + + this.emit = (query = this.query) => { + reader.post({ + type: 'search-result', + data: { + query, + current: this.index >= 0 ? this.index + 1 : 0, + total: this.total, + renderedTotal: this.matches.length, + isTruncated: this.isTruncated, + }, + }); + }; + + this.cancelPendingSearch = () => { + this.searchToken += 1; + + if (this.pendingSearchTimer !== null) { + clearTimeout(this.pendingSearchTimer); + if (typeof cancelIdleCallback !== 'undefined') { + cancelIdleCallback(this.pendingSearchTimer); + } + this.pendingSearchTimer = null; + } + }; + + this.refreshLayout = () => { + if (!reader.generalSettings.val.pageReader || !window.pageReader) { + reader.refresh(); + return; + } + + pageReader.repaginate(); + }; + + this.resetMatches = () => { + const touchedParents = new Set(); + const marks = document.querySelectorAll('mark.lnreader-search-match'); + const fragment = document.createDocumentFragment(); + + marks.forEach(mark => { + const parent = mark.parentNode; + if (!parent) { + return; + } + + while (mark.firstChild) { + fragment.appendChild(mark.firstChild); + } + parent.replaceChild(fragment, mark); + touchedParents.add(parent); + }); + + touchedParents.forEach(parent => { + parent.normalize(); + }); + + this.matches = []; + this.index = -1; + this.total = 0; + this.isTruncated = false; + this.isValidState = true; + this.cachedSegments = null; + this.refreshLayout(); + }; + + this.clear = (emit = true, resetQuery = true) => { + this.cancelPendingSearch(); + + if (resetQuery) { + this.query = ''; + } + + this.resetMatches(); + this.invalidateCache(); + + if (emit) { + this.emit(); + } + }; + + this.getTextBlock = node => { + let element = node.parentElement; + + while ( + element && + element !== reader.chapterElement && + INLINE_TEXT_ELEMENTS.has(element.nodeName) + ) { + element = element.parentElement; + } + + return element || reader.chapterElement; + }; + + this.hasElementBetween = (previousNode, nextNode, selector) => { + let current = previousNode.nextSibling; + while (current && current !== nextNode) { + if (current.nodeType === Node.ELEMENT_NODE && current.matches(selector)) { + return true; + } + if (current.querySelector?.(selector)) { + return true; + } + current = current.nextSibling; + } + return false; + }; + + this.buildTextSegments = () => { + const segments = []; + const textNodes = []; + const walker = document.createTreeWalker( + reader.chapterElement, + NodeFilter.SHOW_TEXT, + { + acceptNode: node => { + if (!node.nodeValue) { + return NodeFilter.FILTER_REJECT; + } + if (node.parentElement?.closest('script, style')) { + return NodeFilter.FILTER_REJECT; + } + return NodeFilter.FILTER_ACCEPT; + }, + }, + ); + let node = walker.nextNode(); + + while (node) { + textNodes.push(node); + node = walker.nextNode(); + } + + textNodes.forEach(textNode => { + const block = this.getTextBlock(textNode); + const previousSegment = segments[segments.length - 1]; + const previousEntry = + previousSegment?.entries[previousSegment.entries.length - 1]; + const startsNewSegment = + !previousSegment || + previousSegment.block !== block || + this.hasElementBetween( + previousEntry.node, + textNode, + 'br, hr, img, table, ul, ol', + ); + + if (startsNewSegment) { + segments.push({ + block, + entries: [], + text: '', + }); + } + + const segment = segments[segments.length - 1]; + const start = segment.text.length; + const text = textNode.nodeValue || ''; + + segment.entries.push({ + end: start + text.length, + node: textNode, + start, + }); + segment.text += text; + }); + + return segments.filter(segment => segment.text.trim()); + }; + + this.getTextSegments = () => { + if (this.cachedSegments) { + return this.cachedSegments; + } + this.cachedSegments = this.buildTextSegments(); + return this.cachedSegments; + }; + + this.findSegmentMatches = (segment, normalizedTerm) => { + const matches = []; + const normalizedText = segment.text.toLowerCase(); + let matchIndex = normalizedText.indexOf(normalizedTerm); + + while (matchIndex !== -1) { + matches.push(matchIndex); + matchIndex = normalizedText.indexOf( + normalizedTerm, + matchIndex + normalizedTerm.length, + ); + } + + return matches; + }; + + this.getTextPosition = (segment, offset, preferPrevious = false) => { + for (const entry of segment.entries) { + if (offset >= entry.start && offset < entry.end) { + return { + node: entry.node, + offset: offset - entry.start, + }; + } + + if (preferPrevious && offset === entry.end) { + return { + node: entry.node, + offset: entry.node.nodeValue?.length || 0, + }; + } + } + + const entry = segment.entries[segment.entries.length - 1]; + return { + node: entry.node, + offset: entry.node.nodeValue?.length || 0, + }; + }; + + this.removeEmptyInlineTextElement = node => { + if ( + !node || + node.nodeType !== Node.ELEMENT_NODE || + !INLINE_TEXT_ELEMENTS.has(node.nodeName) || + node.textContent || + node.querySelector('img, svg, canvas, video, audio, iframe') + ) { + return; + } + + const parent = node.parentNode; + parent?.removeChild(node); + this.removeEmptyInlineTextElement(parent); + }; + + this.wrapSegmentMatch = (segment, start, length) => { + const end = start + length; + const range = document.createRange(); + const mark = document.createElement('mark'); + const startPosition = this.getTextPosition(segment, start); + const endPosition = this.getTextPosition(segment, end, true); + + mark.className = 'lnreader-search-match'; + range.setStart(startPosition.node, startPosition.offset); + range.setEnd(endPosition.node, endPosition.offset); + mark.appendChild(range.extractContents()); + range.insertNode(mark); + this.removeEmptyInlineTextElement(mark.previousSibling); + this.removeEmptyInlineTextElement(mark.nextSibling); + range.detach?.(); + }; + + this.ensureSearch = query => { + const term = String(query ?? this.query ?? '').trim(); + if (!term) { + this.clear(); + return false; + } + + if (term !== this.query || !this.isValidState) { + this.search(term, Math.max(0, this.index)); + } + + return this.matches.length > 0; + }; + + this.scrollToMatch = match => { + if (reader.generalSettings.val.pageReader && window.pageReader) { + const rect = match.getBoundingClientRect(); + const relativePage = Math.floor( + (rect.left + rect.width / 2) / reader.layoutWidth, + ); + const page = Math.max( + 0, + Math.min( + pageReader.totalPages.val - 1, + pageReader.page.val + relativePage, + ), + ); + pageReader.movePage(page); + return; + } + + requestAnimationFrame(() => { + match.scrollIntoView({ block: 'center', behavior: 'smooth' }); + }); + }; + + this.focus = index => { + if (!this.matches.length) { + this.index = -1; + this.emit(); + return; + } + + const oldIndex = this.index; + this.index = + ((index % this.matches.length) + this.matches.length) % + this.matches.length; + + if (oldIndex !== this.index) { + if (oldIndex >= 0) { + this.matches[oldIndex]?.classList.remove( + 'lnreader-search-match-active', + ); + } + this.matches[this.index].classList.add('lnreader-search-match-active'); + this.scrollToMatch(this.matches[this.index]); + } + + this.emit(); + }; + + this.finishSearch = (query, preferredIndex, total) => { + this.pendingSearchTimer = null; + this.matches = Array.from( + reader.chapterElement.querySelectorAll('mark.lnreader-search-match'), + ); + this.total = total; + this.isTruncated = this.matches.length < this.total; + this.isValidState = true; + this.refreshLayout(); + + if (!this.matches.length) { + this.emit(query); + return; + } + + this.focus(Math.max(0, Math.min(preferredIndex, this.matches.length - 1))); + }; + + this.invalidateCache = () => { + this.cachedSegments = null; + this.isValidState = false; + }; + + this.search = (query, preferredIndex = 0) => { + const term = String(query ?? '').trim(); + this.cancelPendingSearch(); + this.resetMatches(); + this.query = term; + + if ( + !term || + (term.length < MIN_QUERY_LENGTH && !SPECIAL_CHARACTER_REGEX.test(term)) + ) { + this.emit(term); + return; + } + + const searchToken = this.searchToken; + const normalizedTerm = term.toLowerCase(); + const textSegments = this.getTextSegments(); + let textSegmentIndex = 0; + let totalMatchCount = 0; + let renderedMatchCount = 0; + + const processBatch = () => { + if (searchToken !== this.searchToken || term !== this.query) { + this.pendingSearchTimer = null; + return; + } + + const batchEnd = Math.min( + textSegmentIndex + SEGMENT_BATCH_SIZE, + textSegments.length, + ); + + while (textSegmentIndex < batchEnd) { + const segment = textSegments[textSegmentIndex]; + const matches = this.findSegmentMatches(segment, normalizedTerm); + const renderableMatches = matches.slice( + 0, + Math.max(0, MAX_RENDERED_MATCHES - renderedMatchCount), + ); + + renderableMatches.reverse().forEach(matchIndex => { + this.wrapSegmentMatch(segment, matchIndex, normalizedTerm.length); + }); + + renderedMatchCount += renderableMatches.length; + totalMatchCount += matches.length; + textSegmentIndex += 1; + } + + if (textSegmentIndex < textSegments.length) { + if (typeof requestIdleCallback !== 'undefined') { + this.pendingSearchTimer = requestIdleCallback(processBatch, { + timeout: 50, + }); + } else { + this.pendingSearchTimer = setTimeout(processBatch, 0); + } + return; + } + + this.finishSearch(term, preferredIndex, totalMatchCount); + }; + + processBatch(); + }; + + this.next = query => { + if (this.ensureSearch(query)) { + this.focus(this.index + 1); + } + }; + + this.previous = query => { + if (this.ensureSearch(query)) { + this.focus(this.index - 1); + } + }; +})(); diff --git a/android/app/src/main/assets/js/text-vibe.js b/assets/reader/js/text-vibe.js similarity index 100% rename from android/app/src/main/assets/js/text-vibe.js rename to assets/reader/js/text-vibe.js diff --git a/android/app/src/main/assets/js/van.d.ts b/assets/reader/js/van.d.ts similarity index 100% rename from android/app/src/main/assets/js/van.d.ts rename to assets/reader/js/van.d.ts diff --git a/android/app/src/main/assets/js/van.js b/assets/reader/js/van.js similarity index 100% rename from android/app/src/main/assets/js/van.js rename to assets/reader/js/van.js diff --git a/babel.config.js b/babel.config.js index d5de9ae37..44242b422 100644 --- a/babel.config.js +++ b/babel.config.js @@ -5,7 +5,7 @@ const ReactCompilerConfig = { export default function (api) { api.cache(true); return { - presets: ['module:@react-native/babel-preset'], + presets: ['babel-preset-expo'], plugins: [ 'module:@babel/plugin-transform-export-namespace-from', ['babel-plugin-react-compiler', ReactCompilerConfig], @@ -17,7 +17,7 @@ export default function (api) { '@database': './src/database', '@hooks': './src/hooks', '@screens': './src/screens', - '@strings': './strings', + '@i18n': './src/i18n', '@services': './src/services', '@plugins': './src/plugins', '@utils': './src/utils', @@ -26,7 +26,11 @@ export default function (api) { '@api': './src/api', '@type': './src/type', '@specs': './specs', - '@test-utils': './__tests-modules__/test-utils', + '@test-utils': './test/test-utils', + '@env': './src/generated/build-info', + '@modules/nitro-epub': './modules/nitro-epub/src/index', + '@modules/nitro-tts': './modules/nitro-tts/src/index', + '@modules': './modules', 'react-native-vector-icons/MaterialCommunityIcons': '@react-native-vector-icons/material-design-icons', }, diff --git a/crowdin.yml b/crowdin.yml index df73f2f91..d791cbcdf 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,5 +1,5 @@ pull_request_title: 'chore: Update Translations' commit_message: '[ci skip]' files: - - source: /strings/languages/en/strings.json - translation: /strings/languages/%locale_with_underscore%/strings.json + - source: /src/i18n/languages/en/strings.json + translation: /src/i18n/languages/%locale_with_underscore%/strings.json diff --git a/drizzle/20260612232322_normal_saracen/migration.sql b/drizzle/20260612232322_normal_saracen/migration.sql new file mode 100644 index 000000000..9977fd82b --- /dev/null +++ b/drizzle/20260612232322_normal_saracen/migration.sql @@ -0,0 +1 @@ +ALTER TABLE `Chapter` ADD `scanlator` text; \ No newline at end of file diff --git a/drizzle/20260612232322_normal_saracen/snapshot.json b/drizzle/20260612232322_normal_saracen/snapshot.json new file mode 100644 index 000000000..6d211c12d --- /dev/null +++ b/drizzle/20260612232322_normal_saracen/snapshot.json @@ -0,0 +1,634 @@ +{ + "version": "7", + "dialect": "sqlite", + "id": "51b6018f-2f9b-49d9-9762-37522abbdbf1", + "prevIds": [ + "cf9a3ebf-235d-4a1d-8b65-297a5d2847e8" + ], + "ddl": [ + { + "name": "Category", + "entityType": "tables" + }, + { + "name": "Chapter", + "entityType": "tables" + }, + { + "name": "NovelCategory", + "entityType": "tables" + }, + { + "name": "Novel", + "entityType": "tables" + }, + { + "name": "Repository", + "entityType": "tables" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "Category" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "name", + "entityType": "columns", + "table": "Category" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "sort", + "entityType": "columns", + "table": "Category" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "novelId", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "path", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "name", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "releaseTime", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "false", + "generated": null, + "name": "bookmark", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "true", + "generated": null, + "name": "unread", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "readTime", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "false", + "generated": null, + "name": "isDownloaded", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "updatedTime", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "real", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "chapterNumber", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": "'1'", + "generated": null, + "name": "page", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "position", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "progress", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "scanlator", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "NovelCategory" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "novelId", + "entityType": "columns", + "table": "NovelCategory" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "categoryId", + "entityType": "columns", + "table": "NovelCategory" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "path", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "pluginId", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "name", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "cover", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "author", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "artist", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": "'Unknown'", + "generated": null, + "name": "status", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "genres", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "false", + "generated": null, + "name": "inLibrary", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "false", + "generated": null, + "name": "isLocal", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "totalPages", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "chaptersDownloaded", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "chaptersUnread", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "totalChapters", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "lastReadAt", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "lastUpdatedAt", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "Repository" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "url", + "entityType": "columns", + "table": "Repository" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "Category_pk", + "table": "Category", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "Chapter_pk", + "table": "Chapter", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "NovelCategory_pk", + "table": "NovelCategory", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "Novel_pk", + "table": "Novel", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "Repository_pk", + "table": "Repository", + "entityType": "pks" + }, + { + "columns": [ + { + "value": "name", + "isExpression": false + } + ], + "isUnique": true, + "where": null, + "origin": "manual", + "name": "category_name_unique", + "entityType": "indexes", + "table": "Category" + }, + { + "columns": [ + { + "value": "sort", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "category_sort_idx", + "entityType": "indexes", + "table": "Category" + }, + { + "columns": [ + { + "value": "novelId", + "isExpression": false + }, + { + "value": "path", + "isExpression": false + } + ], + "isUnique": true, + "where": null, + "origin": "manual", + "name": "chapter_novel_path_unique", + "entityType": "indexes", + "table": "Chapter" + }, + { + "columns": [ + { + "value": "novelId", + "isExpression": false + }, + { + "value": "position", + "isExpression": false + }, + { + "value": "page", + "isExpression": false + }, + { + "value": "id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "chapterNovelIdIndex", + "entityType": "indexes", + "table": "Chapter" + }, + { + "columns": [ + { + "value": "novelId", + "isExpression": false + }, + { + "value": "categoryId", + "isExpression": false + } + ], + "isUnique": true, + "where": null, + "origin": "manual", + "name": "novel_category_unique", + "entityType": "indexes", + "table": "NovelCategory" + }, + { + "columns": [ + { + "value": "path", + "isExpression": false + }, + { + "value": "pluginId", + "isExpression": false + } + ], + "isUnique": true, + "where": null, + "origin": "manual", + "name": "novel_path_plugin_unique", + "entityType": "indexes", + "table": "Novel" + }, + { + "columns": [ + { + "value": "pluginId", + "isExpression": false + }, + { + "value": "path", + "isExpression": false + }, + { + "value": "id", + "isExpression": false + }, + { + "value": "inLibrary", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "NovelIndex", + "entityType": "indexes", + "table": "Novel" + }, + { + "columns": [ + { + "value": "url", + "isExpression": false + } + ], + "isUnique": true, + "where": null, + "origin": "manual", + "name": "repository_url_unique", + "entityType": "indexes", + "table": "Repository" + } + ], + "renames": [] +} \ No newline at end of file diff --git a/drizzle/20260719143427_long_moondragon/migration.sql b/drizzle/20260719143427_long_moondragon/migration.sql new file mode 100644 index 000000000..b0f9ccbe6 --- /dev/null +++ b/drizzle/20260719143427_long_moondragon/migration.sql @@ -0,0 +1 @@ +ALTER TABLE `Chapter` ADD `timeSpent` integer DEFAULT 0; \ No newline at end of file diff --git a/drizzle/20260719143427_long_moondragon/snapshot.json b/drizzle/20260719143427_long_moondragon/snapshot.json new file mode 100644 index 000000000..27cabe777 --- /dev/null +++ b/drizzle/20260719143427_long_moondragon/snapshot.json @@ -0,0 +1,644 @@ +{ + "version": "7", + "dialect": "sqlite", + "id": "498c0b85-a3f4-413f-8fed-7914c3ba15d9", + "prevIds": [ + "51b6018f-2f9b-49d9-9762-37522abbdbf1" + ], + "ddl": [ + { + "name": "Category", + "entityType": "tables" + }, + { + "name": "Chapter", + "entityType": "tables" + }, + { + "name": "NovelCategory", + "entityType": "tables" + }, + { + "name": "Novel", + "entityType": "tables" + }, + { + "name": "Repository", + "entityType": "tables" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "Category" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "name", + "entityType": "columns", + "table": "Category" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "sort", + "entityType": "columns", + "table": "Category" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "novelId", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "path", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "name", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "releaseTime", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "false", + "generated": null, + "name": "bookmark", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "true", + "generated": null, + "name": "unread", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "readTime", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "false", + "generated": null, + "name": "isDownloaded", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "updatedTime", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "real", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "chapterNumber", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": "'1'", + "generated": null, + "name": "page", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "position", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "progress", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "scanlator", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "timeSpent", + "entityType": "columns", + "table": "Chapter" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "NovelCategory" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "novelId", + "entityType": "columns", + "table": "NovelCategory" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "categoryId", + "entityType": "columns", + "table": "NovelCategory" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "path", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "pluginId", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "name", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "cover", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "author", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "artist", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": "'Unknown'", + "generated": null, + "name": "status", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "genres", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "false", + "generated": null, + "name": "inLibrary", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "false", + "generated": null, + "name": "isLocal", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "totalPages", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "chaptersDownloaded", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "chaptersUnread", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": "0", + "generated": null, + "name": "totalChapters", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "lastReadAt", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "lastUpdatedAt", + "entityType": "columns", + "table": "Novel" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "Repository" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "url", + "entityType": "columns", + "table": "Repository" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "Category_pk", + "table": "Category", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "Chapter_pk", + "table": "Chapter", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "NovelCategory_pk", + "table": "NovelCategory", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "Novel_pk", + "table": "Novel", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "Repository_pk", + "table": "Repository", + "entityType": "pks" + }, + { + "columns": [ + { + "value": "name", + "isExpression": false + } + ], + "isUnique": true, + "where": null, + "origin": "manual", + "name": "category_name_unique", + "entityType": "indexes", + "table": "Category" + }, + { + "columns": [ + { + "value": "sort", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "category_sort_idx", + "entityType": "indexes", + "table": "Category" + }, + { + "columns": [ + { + "value": "novelId", + "isExpression": false + }, + { + "value": "path", + "isExpression": false + } + ], + "isUnique": true, + "where": null, + "origin": "manual", + "name": "chapter_novel_path_unique", + "entityType": "indexes", + "table": "Chapter" + }, + { + "columns": [ + { + "value": "novelId", + "isExpression": false + }, + { + "value": "position", + "isExpression": false + }, + { + "value": "page", + "isExpression": false + }, + { + "value": "id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "chapterNovelIdIndex", + "entityType": "indexes", + "table": "Chapter" + }, + { + "columns": [ + { + "value": "novelId", + "isExpression": false + }, + { + "value": "categoryId", + "isExpression": false + } + ], + "isUnique": true, + "where": null, + "origin": "manual", + "name": "novel_category_unique", + "entityType": "indexes", + "table": "NovelCategory" + }, + { + "columns": [ + { + "value": "path", + "isExpression": false + }, + { + "value": "pluginId", + "isExpression": false + } + ], + "isUnique": true, + "where": null, + "origin": "manual", + "name": "novel_path_plugin_unique", + "entityType": "indexes", + "table": "Novel" + }, + { + "columns": [ + { + "value": "pluginId", + "isExpression": false + }, + { + "value": "path", + "isExpression": false + }, + { + "value": "id", + "isExpression": false + }, + { + "value": "inLibrary", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "NovelIndex", + "entityType": "indexes", + "table": "Novel" + }, + { + "columns": [ + { + "value": "url", + "isExpression": false + } + ], + "isUnique": true, + "where": null, + "origin": "manual", + "name": "repository_url_unique", + "entityType": "indexes", + "table": "Repository" + } + ], + "renames": [] +} \ No newline at end of file diff --git a/drizzle/20260727081855_calm_chimera/migration.sql b/drizzle/20260727081855_calm_chimera/migration.sql new file mode 100644 index 000000000..1f709f3a5 --- /dev/null +++ b/drizzle/20260727081855_calm_chimera/migration.sql @@ -0,0 +1,126 @@ +DROP TRIGGER IF EXISTS `update_novel_stats`;--> statement-breakpoint +DROP TRIGGER IF EXISTS `update_novel_stats_on_update`;--> statement-breakpoint +DROP TRIGGER IF EXISTS `update_novel_stats_on_delete`;--> statement-breakpoint +CREATE TABLE IF NOT EXISTS `__migration_Novel` AS SELECT * FROM `Novel`;--> statement-breakpoint +CREATE TABLE IF NOT EXISTS `__migration_Chapter` AS SELECT * FROM `Chapter`;--> statement-breakpoint +CREATE TABLE IF NOT EXISTS `__migration_NovelCategory` AS SELECT * FROM `NovelCategory`;--> statement-breakpoint +DROP TABLE IF EXISTS `__new_Novel`;--> statement-breakpoint +CREATE TABLE `__new_Novel` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `path` text NOT NULL, + `pluginId` text NOT NULL, + `name` text NOT NULL, + `cover` text, + `summary` text, + `author` text, + `artist` text, + `status` text DEFAULT 'Unknown', + `genres` text, + `inLibrary` integer DEFAULT false, + `isLocal` integer DEFAULT false, + `totalPages` integer DEFAULT 0, + `chaptersDownloaded` integer DEFAULT 0, + `chaptersUnread` integer DEFAULT 0, + `totalChapters` integer DEFAULT 0, + `lastReadAt` text, + `lastUpdatedAt` text +); +--> statement-breakpoint +INSERT INTO `__new_Novel` ( + `id`, + `path`, + `pluginId`, + `name`, + `cover`, + `summary`, + `author`, + `artist`, + `status`, + `genres`, + `inLibrary`, + `isLocal`, + `totalPages`, + `chaptersDownloaded`, + `chaptersUnread`, + `totalChapters`, + `lastReadAt`, + `lastUpdatedAt` +) +SELECT + `__migration_Novel`.`id`, + `__migration_Novel`.`path`, + `__migration_Novel`.`pluginId`, + `__migration_Novel`.`name`, + `__migration_Novel`.`cover`, + `__migration_Novel`.`summary`, + `__migration_Novel`.`author`, + `__migration_Novel`.`artist`, + `__migration_Novel`.`status`, + `__migration_Novel`.`genres`, + `__migration_Novel`.`inLibrary`, + `__migration_Novel`.`isLocal`, + `__migration_Novel`.`totalPages`, + COALESCE(`chapter_stats`.`chaptersDownloaded`, 0), + COALESCE(`chapter_stats`.`chaptersUnread`, 0), + COALESCE(`chapter_stats`.`totalChapters`, 0), + `chapter_stats`.`lastReadAt`, + `chapter_stats`.`lastUpdatedAt` +FROM `__migration_Novel` +LEFT JOIN ( + SELECT + `novelId`, + SUM(CASE WHEN `isDownloaded` = 1 THEN 1 ELSE 0 END) AS `chaptersDownloaded`, + SUM(CASE WHEN `unread` = 1 THEN 1 ELSE 0 END) AS `chaptersUnread`, + COUNT(*) AS `totalChapters`, + MAX(`readTime`) AS `lastReadAt`, + MAX(`updatedTime`) AS `lastUpdatedAt` + FROM `__migration_Chapter` + GROUP BY `novelId` +) AS `chapter_stats` + ON `chapter_stats`.`novelId` = `__migration_Novel`.`id`;--> statement-breakpoint +DELETE FROM `NovelCategory`;--> statement-breakpoint +DELETE FROM `Chapter`;--> statement-breakpoint +DROP TABLE IF EXISTS `Novel`;--> statement-breakpoint +ALTER TABLE `__new_Novel` RENAME TO `Novel`;--> statement-breakpoint +INSERT OR REPLACE INTO `Chapter` ( + `id`, + `novelId`, + `path`, + `name`, + `releaseTime`, + `bookmark`, + `unread`, + `readTime`, + `isDownloaded`, + `updatedTime`, + `chapterNumber`, + `page`, + `position`, + `progress`, + `scanlator`, + `timeSpent` +) +SELECT + `id`, + `novelId`, + `path`, + `name`, + `releaseTime`, + `bookmark`, + `unread`, + `readTime`, + `isDownloaded`, + `updatedTime`, + `chapterNumber`, + `page`, + `position`, + `progress`, + `scanlator`, + `timeSpent` +FROM `__migration_Chapter`;--> statement-breakpoint +INSERT OR REPLACE INTO `NovelCategory` SELECT * FROM `__migration_NovelCategory`;--> statement-breakpoint +DROP TABLE `__migration_Novel`;--> statement-breakpoint +DROP TABLE `__migration_Chapter`;--> statement-breakpoint +DROP TABLE `__migration_NovelCategory`;--> statement-breakpoint +CREATE UNIQUE INDEX `novel_path_plugin_unique` ON `Novel` (`path`,`pluginId`);--> statement-breakpoint +CREATE INDEX `NovelIndex` ON `Novel` (`pluginId`,`path`,`id`,`inLibrary`); diff --git a/drizzle/migrations.js b/drizzle/migrations.js index 42832eb58..6a8167e3f 100644 --- a/drizzle/migrations.js +++ b/drizzle/migrations.js @@ -1,7 +1,15 @@ +// This file is required for Expo/React Native SQLite migrations - https://orm.drizzle.team/quick-sqlite/expo + import m0000 from './20251222152612_past_mandrill/migration.sql'; +import m0001 from './20260612232322_normal_saracen/migration.sql'; +import m0002 from './20260719143427_long_moondragon/migration.sql'; +import m0003 from './20260727081855_calm_chimera/migration.sql'; export default { migrations: { '20251222152612_past_mandrill': m0000, + '20260612232322_normal_saracen': m0001, + '20260719143427_long_moondragon': m0002, + '20260727081855_calm_chimera': m0003, }, }; diff --git a/eas.json b/eas.json new file mode 100644 index 000000000..62049ab70 --- /dev/null +++ b/eas.json @@ -0,0 +1,21 @@ +{ + "cli": { + "version": ">= 20.5.1", + "appVersionSource": "local" + }, + "build": { + "development": { + "developmentClient": true, + "distribution": "internal" + }, + "preview": { + "distribution": "internal" + }, + "production": { + "autoIncrement": true + } + }, + "submit": { + "production": {} + } +} diff --git a/env.d.ts b/env.d.ts deleted file mode 100644 index a62595a63..000000000 --- a/env.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -declare module 'react-native-config' { - export interface NativeConfig { - MYANIMELIST_CLIENT_ID: string; - ANILIST_CLIENT_ID: string; - GIT_HASH: string; - RELEASE_DATE: string; - BUILD_TYPE: 'Debug' | 'Release' | 'Beta' | 'Github Action'; - } - - export const Config: NativeConfig; - export default Config; -} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..0f106f207 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,62 @@ +const { defineConfig, globalIgnores } = require('eslint/config'); +const expoConfig = require('eslint-config-expo/flat'); +const { FlatCompat } = require('@eslint/eslintrc'); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const testFiles = [ + '**/__tests__/**/*.[jt]s?(x)', + '**/?(*.)+(spec|test).[jt]s?(x)', +]; + +module.exports = defineConfig([ + globalIgnores([ + 'android/**', + 'ios/**', + 'node_modules/**', + '.expo/**', + 'dist/**', + 'coverage/**', + ]), + expoConfig, + ...compat + .extends('plugin:testing-library/react', 'plugin:jest/recommended') + .map(config => ({ + ...config, + files: testFiles, + })), + + { + files: ['**/*.{js,jsx,ts,tsx}'], + rules: { + 'no-shadow': 'off', + 'no-undef': 'off', + 'no-console': 'error', + + '@typescript-eslint/no-shadow': 'warn', + + 'react-hooks/exhaustive-deps': 'warn', + 'react-hooks/static-components': 'warn', + 'react-hooks/purity': 'warn', + 'react-hooks/set-state-in-effect': 'warn', + 'react-hooks/refs': 'warn', + 'react-hooks/immutability': 'warn', + 'react/no-children-prop': 'warn', + + 'react/display-name': 'off', + + curly: ['error', 'multi-line', 'consistent'], + 'no-useless-return': 'error', + 'block-scoped-var': 'error', + 'no-var': 'error', + 'prefer-const': 'error', + 'no-dupe-else-if': 'error', + 'no-duplicate-imports': 'error', + + '@react-native/no-deep-imports': 'off', + '@typescript-eslint/no-require-imports': 'off', + }, + }, +]); diff --git a/index.js b/index.js index 6d720428b..8f42a73fd 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,13 @@ import 'react-native-gesture-handler'; import { registerRootComponent } from 'expo'; -import { I18nManager } from 'react-native'; -import { i18n } from './strings/translations'; +import { AppRegistry, I18nManager } from 'react-native'; +import { i18n } from './src/i18n/translations'; +import { runHeadlessBackgroundTask } from './src/services/backgroundTasks'; + +AppRegistry.registerHeadlessTask( + 'LNReaderBackgroundTask', + () => runHeadlessBackgroundTask, +); const isRTL = i18n.locale.startsWith('ar') || i18n.locale.startsWith('he'); I18nManager.allowRTL(isRTL); diff --git a/ios/.xcode.env b/ios/.xcode.env deleted file mode 100644 index 772b339b4..000000000 --- a/ios/.xcode.env +++ /dev/null @@ -1 +0,0 @@ -export NODE_BINARY=$(command -v node) diff --git a/ios/Dynamic.swift b/ios/Dynamic.swift deleted file mode 100644 index cdfde8eac..000000000 --- a/ios/Dynamic.swift +++ /dev/null @@ -1,22 +0,0 @@ -import UIKit -import Foundation -import Lottie - -@objc class Dynamic: NSObject { - - @objc func createAnimationView(rootView: UIView, lottieName: String) -> AnimationView { - let animationView = AnimationView(name: lottieName) - animationView.frame = rootView.frame - animationView.center = rootView.center - animationView.backgroundColor = UIColor.white; - return animationView; - } - - @objc func play(animationView: AnimationView) { - animationView.play( - completion: { (success) in - RNSplashScreen.setAnimationFinished(true) - } - ); - } -} diff --git a/ios/LNReader.xcodeproj/project.pbxproj b/ios/LNReader.xcodeproj/project.pbxproj deleted file mode 100644 index ad17a7e0c..000000000 --- a/ios/LNReader.xcodeproj/project.pbxproj +++ /dev/null @@ -1,598 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 0C80B921A6F3F58F76C31292 /* libPods-LNReader.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-LNReader.a */; }; - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 147488B02DD8E9BD00C6D0A2 /* Dynamic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 147488AF2DD8E9BD00C6D0A2 /* Dynamic.swift */; }; - 147488B22DD8EA0700C6D0A2 /* loading.json in Resources */ = {isa = PBXBuildFile; fileRef = 147488B12DD8EA0700C6D0A2 /* loading.json */; }; - 147488B72DD97E8A00C6D0A2 /* RCTNativeFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 147488B62DD97E8A00C6D0A2 /* RCTNativeFile.swift */; }; - 147488BC2DD9882000C6D0A2 /* RCTNativeFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 147488BB2DD9882000C6D0A2 /* RCTNativeFile.mm */; }; - 147488C02DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.mm in Sources */ = {isa = PBXBuildFile; fileRef = 147488BF2DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.mm */; }; - 147488C42DD9A27400C6D0A2 /* RCTNativeEpubUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 147488C32DD9A27400C6D0A2 /* RCTNativeEpubUtil.mm */; }; - 147488C82DD9A68600C6D0A2 /* RCTNativeZipArchive.mm in Sources */ = {isa = PBXBuildFile; fileRef = 147488C72DD9A68600C6D0A2 /* RCTNativeZipArchive.mm */; }; - 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; - 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; - A1FA4E89FDA7CFAD14C43693 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F74819B237E0768BB72DED8 /* ExpoModulesProvider.swift */; }; - CF1A2676AC104DBC83DBFFF9 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 13B07F961A680F5B00A75B9A /* LNReader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LNReader.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = LNReader/Images.xcassets; sourceTree = ""; }; - 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = LNReader/Info.plist; sourceTree = ""; }; - 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = LNReader/PrivacyInfo.xcprivacy; sourceTree = ""; }; - 147488AA2DD8E7DE00C6D0A2 /* LNReader-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "LNReader-Bridging-Header.h"; path = "LNReader/LNReader-Bridging-Header.h"; sourceTree = ""; }; - 147488AF2DD8E9BD00C6D0A2 /* Dynamic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dynamic.swift; sourceTree = ""; }; - 147488B12DD8EA0700C6D0A2 /* loading.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = loading.json; sourceTree = ""; }; - 147488B62DD97E8A00C6D0A2 /* RCTNativeFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RCTNativeFile.swift; sourceTree = ""; }; - 147488BA2DD9882000C6D0A2 /* RCTNativeFile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTNativeFile.h; sourceTree = ""; }; - 147488BB2DD9882000C6D0A2 /* RCTNativeFile.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNativeFile.mm; sourceTree = ""; }; - 147488BE2DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTNativeVolumeButtonListener.h; sourceTree = ""; }; - 147488BF2DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNativeVolumeButtonListener.mm; sourceTree = ""; }; - 147488C22DD9A27400C6D0A2 /* RCTNativeEpubUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTNativeEpubUtil.h; sourceTree = ""; }; - 147488C32DD9A27400C6D0A2 /* RCTNativeEpubUtil.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNativeEpubUtil.mm; sourceTree = ""; }; - 147488C62DD9A68600C6D0A2 /* RCTNativeZipArchive.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTNativeZipArchive.h; sourceTree = ""; }; - 147488C72DD9A68600C6D0A2 /* RCTNativeZipArchive.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNativeZipArchive.mm; sourceTree = ""; }; - 1F74819B237E0768BB72DED8 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-LNReader/ExpoModulesProvider.swift"; sourceTree = ""; }; - 3B4392A12AC88292D35C810B /* Pods-LNReader.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LNReader.debug.xcconfig"; path = "Target Support Files/Pods-LNReader/Pods-LNReader.debug.xcconfig"; sourceTree = ""; }; - 5709B34CF0A7D63546082F79 /* Pods-LNReader.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LNReader.release.xcconfig"; path = "Target Support Files/Pods-LNReader/Pods-LNReader.release.xcconfig"; sourceTree = ""; }; - 5DCACB8F33CDC322A6C60F78 /* libPods-LNReader.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LNReader.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = LNReader/AppDelegate.swift; sourceTree = ""; }; - 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = LNReader/LaunchScreen.storyboard; sourceTree = ""; }; - ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 0C80B921A6F3F58F76C31292 /* libPods-LNReader.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 13B07FAE1A68108700A75B9A /* LNReader */ = { - isa = PBXGroup; - children = ( - 13B07FB51A68108700A75B9A /* Images.xcassets */, - 761780EC2CA45674006654EE /* AppDelegate.swift */, - 147488B12DD8EA0700C6D0A2 /* loading.json */, - 13B07FB61A68108700A75B9A /* Info.plist */, - 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, - 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, - 147488AA2DD8E7DE00C6D0A2 /* LNReader-Bridging-Header.h */, - 147488AF2DD8E9BD00C6D0A2 /* Dynamic.swift */, - 147488B62DD97E8A00C6D0A2 /* RCTNativeFile.swift */, - ); - name = LNReader; - sourceTree = ""; - }; - 147488B92DD987DF00C6D0A2 /* NativeFile */ = { - isa = PBXGroup; - children = ( - 147488BA2DD9882000C6D0A2 /* RCTNativeFile.h */, - 147488BB2DD9882000C6D0A2 /* RCTNativeFile.mm */, - ); - path = NativeFile; - sourceTree = ""; - }; - 147488BD2DD9A0E900C6D0A2 /* NativeVolumeButtonListener */ = { - isa = PBXGroup; - children = ( - 147488BE2DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.h */, - 147488BF2DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.mm */, - ); - path = NativeVolumeButtonListener; - sourceTree = ""; - }; - 147488C12DD9A25E00C6D0A2 /* NativeEpubUtil */ = { - isa = PBXGroup; - children = ( - 147488C22DD9A27400C6D0A2 /* RCTNativeEpubUtil.h */, - 147488C32DD9A27400C6D0A2 /* RCTNativeEpubUtil.mm */, - ); - path = NativeEpubUtil; - sourceTree = ""; - }; - 147488C52DD9A66F00C6D0A2 /* NativeZipArchive */ = { - isa = PBXGroup; - children = ( - 147488C62DD9A68600C6D0A2 /* RCTNativeZipArchive.h */, - 147488C72DD9A68600C6D0A2 /* RCTNativeZipArchive.mm */, - ); - path = NativeZipArchive; - sourceTree = ""; - }; - 24615CBE8A657680F1260CBB /* ExpoModulesProviders */ = { - isa = PBXGroup; - children = ( - 28BBFC4322E6EFCA529574FE /* LNReader */, - ); - name = ExpoModulesProviders; - sourceTree = ""; - }; - 28BBFC4322E6EFCA529574FE /* LNReader */ = { - isa = PBXGroup; - children = ( - 1F74819B237E0768BB72DED8 /* ExpoModulesProvider.swift */, - ); - name = LNReader; - sourceTree = ""; - }; - 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { - isa = PBXGroup; - children = ( - ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 5DCACB8F33CDC322A6C60F78 /* libPods-LNReader.a */, - ); - name = Frameworks; - sourceTree = ""; - }; - 832341AE1AAA6A7D00B99B32 /* Libraries */ = { - isa = PBXGroup; - children = ( - ); - name = Libraries; - sourceTree = ""; - }; - 83CBB9F61A601CBA00E9B192 /* PBXGroup */ = { - isa = PBXGroup; - children = ( - 147488C52DD9A66F00C6D0A2 /* NativeZipArchive */, - 147488C12DD9A25E00C6D0A2 /* NativeEpubUtil */, - 147488BD2DD9A0E900C6D0A2 /* NativeVolumeButtonListener */, - 147488B92DD987DF00C6D0A2 /* NativeFile */, - 13B07FAE1A68108700A75B9A /* LNReader */, - 832341AE1AAA6A7D00B99B32 /* Libraries */, - 83CBBA001A601CBA00E9B192 /* Products */, - 2D16E6871FA4F8E400B85C8A /* Frameworks */, - BBD78D7AC51CEA395F1C20DB /* Pods */, - 24615CBE8A657680F1260CBB /* ExpoModulesProviders */, - ); - indentWidth = 2; - sourceTree = ""; - tabWidth = 2; - usesTabs = 0; - }; - 83CBBA001A601CBA00E9B192 /* Products */ = { - isa = PBXGroup; - children = ( - 13B07F961A680F5B00A75B9A /* LNReader.app */, - ); - name = Products; - sourceTree = ""; - }; - BBD78D7AC51CEA395F1C20DB /* Pods */ = { - isa = PBXGroup; - children = ( - 3B4392A12AC88292D35C810B /* Pods-LNReader.debug.xcconfig */, - 5709B34CF0A7D63546082F79 /* Pods-LNReader.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 13B07F861A680F5B00A75B9A /* LNReader */ = { - isa = PBXNativeTarget; - buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "LNReader" */; - buildPhases = ( - C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */, - E2CB0E1FD58C9A385B2CE9DA /* [Expo] Configure project */, - 13B07F871A680F5B00A75B9A /* Sources */, - 13B07F8C1A680F5B00A75B9A /* Frameworks */, - 13B07F8E1A680F5B00A75B9A /* Resources */, - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */, - E235C05ADACE081382539298 /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = LNReader; - productName = LNReader; - productReference = 13B07F961A680F5B00A75B9A /* LNReader.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 83CBB9F71A601CBA00E9B192 = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1210; - TargetAttributes = { - 13B07F861A680F5B00A75B9A = { - LastSwiftMigration = 1120; - }; - }; - }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "LNReader" */; - compatibilityVersion = "Xcode 12.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 83CBB9F61A601CBA00E9B192 /* PBXGroup */; - productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 13B07F861A680F5B00A75B9A /* LNReader */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 13B07F8E1A680F5B00A75B9A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - 147488B22DD8EA0700C6D0A2 /* loading.json in Resources */, - CF1A2676AC104DBC83DBFFF9 /* PrivacyInfo.xcprivacy in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "$(SRCROOT)/.xcode.env.local", - "$(SRCROOT)/.xcode.env", - ); - name = "Bundle React Native code and images"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; - }; - 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-LNReader/Pods-LNReader-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-LNReader/Pods-LNReader-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LNReader/Pods-LNReader-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-LNReader-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-LNReader/Pods-LNReader-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-LNReader/Pods-LNReader-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LNReader/Pods-LNReader-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - E2CB0E1FD58C9A385B2CE9DA /* [Expo] Configure project */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "[Expo] Configure project"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-LNReader/expo-configure-project.sh\"\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 13B07F871A680F5B00A75B9A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 147488C02DD9A14100C6D0A2 /* RCTNativeVolumeButtonListener.mm in Sources */, - 147488B02DD8E9BD00C6D0A2 /* Dynamic.swift in Sources */, - 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */, - 147488BC2DD9882000C6D0A2 /* RCTNativeFile.mm in Sources */, - 147488B72DD97E8A00C6D0A2 /* RCTNativeFile.swift in Sources */, - 147488C82DD9A68600C6D0A2 /* RCTNativeZipArchive.mm in Sources */, - A1FA4E89FDA7CFAD14C43693 /* ExpoModulesProvider.swift in Sources */, - 147488C42DD9A27400C6D0A2 /* RCTNativeEpubUtil.mm in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 13B07F941A680F5B00A75B9A /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-LNReader.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = LNReader/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = LNReader; - SWIFT_OBJC_BRIDGING_HEADER = "LNReader/LNReader-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 13B07F951A680F5B00A75B9A /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-LNReader.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; - INFOPLIST_FILE = LNReader/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = LNReader; - SWIFT_OBJC_BRIDGING_HEADER = "LNReader/LNReader-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; - 83CBBA201A601CBA00E9B192 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++20"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = ( - /usr/lib/swift, - "$(inherited)", - ); - LIBRARY_SEARCH_PATHS = ( - "\"$(SDKROOT)/usr/lib/swift\"", - "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", - "\"$(inherited)\"", - ); - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - OTHER_CPLUSPLUSFLAGS = ( - "$(OTHER_CFLAGS)", - "-DFOLLY_NO_CONFIG", - "-DFOLLY_MOBILE=1", - "-DFOLLY_USE_LIBCPP=1", - "-DFOLLY_CFG_NO_COROUTINES=1", - "-DFOLLY_HAVE_CLOCK_GETTIME=1", - ); - OTHER_LDFLAGS = "$(inherited) "; - REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; - USE_HERMES = true; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 83CBBA211A601CBA00E9B192 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++20"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = ( - /usr/lib/swift, - "$(inherited)", - ); - LIBRARY_SEARCH_PATHS = ( - "\"$(SDKROOT)/usr/lib/swift\"", - "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", - "\"$(inherited)\"", - ); - MTL_ENABLE_DEBUG_INFO = NO; - OTHER_CPLUSPLUSFLAGS = ( - "$(OTHER_CFLAGS)", - "-DFOLLY_NO_CONFIG", - "-DFOLLY_MOBILE=1", - "-DFOLLY_USE_LIBCPP=1", - "-DFOLLY_CFG_NO_COROUTINES=1", - "-DFOLLY_HAVE_CLOCK_GETTIME=1", - ); - OTHER_LDFLAGS = "$(inherited) "; - REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; - SDKROOT = iphoneos; - USE_HERMES = true; - VALIDATE_PRODUCT = YES; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "LNReader" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 13B07F941A680F5B00A75B9A /* Debug */, - 13B07F951A680F5B00A75B9A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "LNReader" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83CBBA201A601CBA00E9B192 /* Debug */, - 83CBBA211A601CBA00E9B192 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 83CBB9F71A601CBA00E9B192; -} diff --git a/ios/LNReader.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/LNReader.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a62..000000000 --- a/ios/LNReader.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/ios/LNReader.xcodeproj/xcshareddata/xcschemes/LNReader.xcscheme b/ios/LNReader.xcodeproj/xcshareddata/xcschemes/LNReader.xcscheme deleted file mode 100644 index 55cde05a9..000000000 --- a/ios/LNReader.xcodeproj/xcshareddata/xcschemes/LNReader.xcscheme +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/LNReader.xcworkspace/contents.xcworkspacedata b/ios/LNReader.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index f7ebad31d..000000000 --- a/ios/LNReader.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/ios/LNReader/AppDelegate.swift b/ios/LNReader/AppDelegate.swift deleted file mode 100644 index 61dae719a..000000000 --- a/ios/LNReader/AppDelegate.swift +++ /dev/null @@ -1,71 +0,0 @@ -import UIKit -import Expo -import React -import React_RCTAppDelegate -import ReactAppDependencyProvider -import Lottie - -@main -class AppDelegate: ExpoAppDelegate { - var window: UIWindow? - - var reactNativeDelegate: ReactNativeDelegate? - var reactNativeFactory: RCTReactNativeFactory? - - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil - ) -> Bool { - let delegate = ReactNativeDelegate() - let factory = ExpoReactNativeFactory(delegate: delegate) - delegate.dependencyProvider = RCTAppDependencyProvider() - - reactNativeDelegate = delegate - reactNativeFactory = factory - bindReactNativeFactory(factory) - window = UIWindow(frame: UIScreen.main.bounds) - - factory.startReactNative( - withModuleName: "main", - in: window, - launchOptions: launchOptions - ) - - if let w = window { - let splashScreenBackground = UIColor(red: 31/255, green: 32/255, blue: 36/255, alpha: 1) - w.backgroundColor = splashScreenBackground - w.rootViewController?.view.backgroundColor = splashScreenBackground - let t = Dynamic() - let animationSize = CGSize(width: 200, height: 200) - let screenBounds = UIScreen.main.bounds - let animationX = (screenBounds.width - animationSize.width) / 2 - let animationY = (screenBounds.height - animationSize.height) / 2 - let animationUIView: UIView = t.createAnimationView(rootView: UIView(frame:CGRect(x: animationX, y: animationY, width: animationSize.width, height: animationSize.height)), lottieName:"loading") - RNSplashScreen.showLottieSplash(animationUIView, inRootView: w) - animationUIView.backgroundColor = UIColor(white: 1, alpha:0) - t.play(animationView: animationUIView as! AnimationView) - RNSplashScreen.setAnimationFinished(true) - } - - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } - - override func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { - return RCTLinkingManager.application(application, open: url, options: options); - } -} - -class ReactNativeDelegate: ExpoReactNativeFactoryDelegate { - override func sourceURL(for bridge: RCTBridge) -> URL? { - // needed to return the correct URL for expo-dev-client. - bridge.bundleURL ?? bundleURL() - } - - override func bundleURL() -> URL? { -#if DEBUG - RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") -#else - Bundle.main.url(forResource: "main", withExtension: "jsbundle") -#endif - } -} diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/1024.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/1024.png deleted file mode 100644 index 67c91d55b..000000000 Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/1024.png and /dev/null differ diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/114.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/114.png deleted file mode 100644 index 65003476e..000000000 Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/114.png and /dev/null differ diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/120.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/120.png deleted file mode 100644 index 9ebd23d84..000000000 Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/120.png and /dev/null differ diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/180.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/180.png deleted file mode 100644 index 814e97e13..000000000 Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/180.png and /dev/null differ diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/29.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/29.png deleted file mode 100644 index 06e5d710a..000000000 Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/29.png and /dev/null differ diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/40.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/40.png deleted file mode 100644 index 6d40b8d99..000000000 Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/40.png and /dev/null differ diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/57.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/57.png deleted file mode 100644 index 09a575473..000000000 Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/57.png and /dev/null differ diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/58.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/58.png deleted file mode 100644 index fe907c053..000000000 Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/58.png and /dev/null differ diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/60.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/60.png deleted file mode 100644 index 16771fd77..000000000 Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/60.png and /dev/null differ diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/80.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/80.png deleted file mode 100644 index 600dfe8e3..000000000 Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/80.png and /dev/null differ diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/87.png b/ios/LNReader/Images.xcassets/AppIcon.appiconset/87.png deleted file mode 100644 index 2feb37b7c..000000000 Binary files a/ios/LNReader/Images.xcassets/AppIcon.appiconset/87.png and /dev/null differ diff --git a/ios/LNReader/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/LNReader/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 73d3b7f6d..000000000 --- a/ios/LNReader/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1 +0,0 @@ -{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"}]} \ No newline at end of file diff --git a/ios/LNReader/Info.plist b/ios/LNReader/Info.plist deleted file mode 100644 index 968307d57..000000000 --- a/ios/LNReader/Info.plist +++ /dev/null @@ -1,66 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - LNReader - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLIconFile - - CFBundleURLName - LNReader - CFBundleURLSchemes - - lnreader - - - - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSAllowsLocalNetworking - - - NSLocationWhenInUseUsageDescription - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - arm64 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/ios/LNReader/LNReader-Bridging-Header.h b/ios/LNReader/LNReader-Bridging-Header.h deleted file mode 100644 index b577135ed..000000000 --- a/ios/LNReader/LNReader-Bridging-Header.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// LNReader-Bridging-Header.h -// LNReader -// -// Created by QUAN on 17/5/25. -// - -#ifndef LNReader_Bridging_Header_h -#define LNReader_Bridging_Header_h - -#import "RNSplashScreen.h" -#import -#import - -#endif /* LNReader_Bridging_Header_h */ diff --git a/ios/LNReader/LaunchScreen.storyboard b/ios/LNReader/LaunchScreen.storyboard deleted file mode 100644 index 1c1f4d25f..000000000 --- a/ios/LNReader/LaunchScreen.storyboard +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/LNReader/PrivacyInfo.xcprivacy b/ios/LNReader/PrivacyInfo.xcprivacy deleted file mode 100644 index 49b8ded26..000000000 --- a/ios/LNReader/PrivacyInfo.xcprivacy +++ /dev/null @@ -1,48 +0,0 @@ - - - - - NSPrivacyAccessedAPITypes - - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryFileTimestamp - NSPrivacyAccessedAPITypeReasons - - C617.1 - 0A2A.1 - 3B52.1 - - - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryUserDefaults - NSPrivacyAccessedAPITypeReasons - - CA92.1 - - - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryDiskSpace - NSPrivacyAccessedAPITypeReasons - - E174.1 - 85F4.1 - - - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategorySystemBootTime - NSPrivacyAccessedAPITypeReasons - - 35F9.1 - - - - NSPrivacyCollectedDataTypes - - NSPrivacyTracking - - - diff --git a/ios/NativeEpubUtil/RCTNativeEpubUtil.h b/ios/NativeEpubUtil/RCTNativeEpubUtil.h deleted file mode 100644 index 3d0bf5b69..000000000 --- a/ios/NativeEpubUtil/RCTNativeEpubUtil.h +++ /dev/null @@ -1,10 +0,0 @@ -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface RCTNativeEpubUtil : NSObject - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/NativeEpubUtil/RCTNativeEpubUtil.mm b/ios/NativeEpubUtil/RCTNativeEpubUtil.mm deleted file mode 100644 index 2197e3500..000000000 --- a/ios/NativeEpubUtil/RCTNativeEpubUtil.mm +++ /dev/null @@ -1,22 +0,0 @@ -// - -#import "RCTNativeEpubUtil.h" - -@implementation RCTNativeEpubUtil - - -+ (NSString *)moduleName { - return @"NativeEpubUtil"; -} - -- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { - return std::make_shared(params); -} - -- (NSDictionary * _Nullable)parseNovelAndChapters:(nonnull NSString *)epubDirPath { - NSMutableDictionary * res = [NSMutableDictionary dictionary]; - // TODO: implement parse epub - return res; -} - -@end diff --git a/ios/NativeFile/RCTNativeFile.h b/ios/NativeFile/RCTNativeFile.h deleted file mode 100644 index 4533b71e0..000000000 --- a/ios/NativeFile/RCTNativeFile.h +++ /dev/null @@ -1,10 +0,0 @@ -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface RCTNativeFile : NSObject - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/NativeFile/RCTNativeFile.mm b/ios/NativeFile/RCTNativeFile.mm deleted file mode 100644 index 9b5723ce6..000000000 --- a/ios/NativeFile/RCTNativeFile.mm +++ /dev/null @@ -1,92 +0,0 @@ -#import "RCTNativeFile.h" - -@implementation RCTNativeFile - -+ (NSString *)moduleName { - return @"NativeFile"; -} - -- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { - return std::make_shared(params); -} - -- (void)copyFile:(nonnull NSString *)sourcePath destPath:(nonnull NSString *)destPath { - [[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:destPath error:nil]; -} - -- (void)downloadFile:(nonnull NSString *)url destPath:(nonnull NSString *)destPath method:(nonnull NSString *)method headers:(nonnull NSDictionary *)headers body:(nonnull NSString *)body resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject { -} - -- (nonnull NSNumber *)exists:(nonnull NSString *)filePath { - - BOOL isExisted = [[NSFileManager defaultManager] fileExistsAtPath:filePath]; - - return [NSNumber numberWithBool:isExisted]; -} - -- (NSDictionary *)getConstants { - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths firstObject]; - - NSArray *cachePaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - NSString *cachesDirectory = [cachePaths firstObject]; - - return @{ - @"ExternalDirectoryPath": documentsDirectory, - @"ExternalCachesDirectoryPath": cachesDirectory - }; -} - -- (void)mkdir:(nonnull NSString *)filePath { - [ - [NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil - ]; -} - -- (void)moveFile:(nonnull NSString *)sourcePath destPath:(nonnull NSString *)destPath { - [ - [NSFileManager defaultManager] moveItemAtPath:sourcePath toPath:destPath error:nil - ]; -} - -- (nonnull NSArray *)readDir:(nonnull NSString *)dirPath { - NSMutableArray* res = [NSMutableArray array]; - NSArray *content = [ - [NSFileManager defaultManager] - contentsOfDirectoryAtPath:dirPath error:nil - ]; - for(int i=0; i)constantsToExport { - return [self getConstants]; -} - - - -@end diff --git a/ios/NativeVolumeButtonListener/RCTNativeVolumeButtonListener.h b/ios/NativeVolumeButtonListener/RCTNativeVolumeButtonListener.h deleted file mode 100644 index 185d9b86f..000000000 --- a/ios/NativeVolumeButtonListener/RCTNativeVolumeButtonListener.h +++ /dev/null @@ -1,10 +0,0 @@ -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface RCTNativeVolumeButtonListener : NSObject - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/NativeVolumeButtonListener/RCTNativeVolumeButtonListener.mm b/ios/NativeVolumeButtonListener/RCTNativeVolumeButtonListener.mm deleted file mode 100644 index 06b6cd6c9..000000000 --- a/ios/NativeVolumeButtonListener/RCTNativeVolumeButtonListener.mm +++ /dev/null @@ -1,21 +0,0 @@ -#import "RCTNativeVolumeButtonListener.h" - -@implementation RCTNativeVolumeButtonListener - -+ (NSString *)moduleName { - return @"NativeVolumeButtonListener"; -} - -- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { - return std::make_shared(params); -} - -- (void)addListener:(nonnull NSString *)eventName { - // TODO: implement addlistener -} - -- (void)removeListeners:(double)count { - // TODO: implement count listeners -} - -@end diff --git a/ios/NativeZipArchive/RCTNativeZipArchive.h b/ios/NativeZipArchive/RCTNativeZipArchive.h deleted file mode 100644 index 1475d365b..000000000 --- a/ios/NativeZipArchive/RCTNativeZipArchive.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// RCTNativeZipArchive.h -// LNReader -// -// Created by QUAN on 18/5/25. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface RCTNativeZipArchive : NSObject - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/NativeZipArchive/RCTNativeZipArchive.mm b/ios/NativeZipArchive/RCTNativeZipArchive.mm deleted file mode 100644 index fb513dc7e..000000000 --- a/ios/NativeZipArchive/RCTNativeZipArchive.mm +++ /dev/null @@ -1,35 +0,0 @@ -// -// RCTNativeZipArchive.m -// LNReader -// -// Created by QUAN on 18/5/25. -// - -#import "RCTNativeZipArchive.h" - -@implementation RCTNativeZipArchive - -+ (NSString *)moduleName { - return @"NativeZipArchive"; -} - -- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { - return std::make_shared(params); -} - -- (void)remoteUnzip:(nonnull NSString *)distDirPath url:(nonnull NSString *)url headers:(nonnull NSDictionary *)headers resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject { - // TODO: implement remoteUnzip -} - -- (void)remoteZip:(nonnull NSString *)sourceDirPath url:(nonnull NSString *)url headers:(nonnull NSDictionary *)headers resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject { - // TODO: implement remoteZip -} - -- (void)unzip:(nonnull NSString *)sourceFilePath distDirPath:(nonnull NSString *)distDirPath resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject { - // TODO: implement unzip -} -- (void)zip:(nonnull NSString *)sourceDirPath zipFilePath:(nonnull NSString *)zipFilePath resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject { - // TODO: implement zip -} - -@end diff --git a/ios/Podfile b/ios/Podfile deleted file mode 100644 index fc71ca6de..000000000 --- a/ios/Podfile +++ /dev/null @@ -1,53 +0,0 @@ -require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking") -# Resolve react_native_pods.rb with node to allow for hoisting -require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native/scripts/react_native_pods.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip - -platform :ios, 15.5 -prepare_react_native_project! - -linkage = ENV['USE_FRAMEWORKS'] -if linkage != nil - Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green - use_frameworks! :linkage => linkage.to_sym -end - -target 'LNReader' do - use_expo_modules! - - if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' - config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; - else - config_command = [ - 'node', - '--no-warnings', - '--eval', - 'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))', - 'react-native-config', - '--json', - '--platform', - 'ios' - ] - end - - config = use_native_modules!(config_command) - - use_react_native!( - :path => config[:reactNativePath], - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/.." - ) - - post_install do |installer| - # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - # :ccache_enabled => true - ) - end -end diff --git a/ios/Podfile.lock b/ios/Podfile.lock deleted file mode 100644 index 30fe80875..000000000 --- a/ios/Podfile.lock +++ /dev/null @@ -1,2735 +0,0 @@ -PODS: - - AppAuth (1.7.6): - - AppAuth/Core (= 1.7.6) - - AppAuth/ExternalUserAgent (= 1.7.6) - - AppAuth/Core (1.7.6) - - AppAuth/ExternalUserAgent (1.7.6): - - AppAuth/Core - - boost (1.84.0) - - DoubleConversion (1.1.6) - - EXApplication (6.1.4): - - ExpoModulesCore - - EXConstants (17.1.6): - - ExpoModulesCore - - EXNotifications (0.31.1): - - ExpoModulesCore - - Expo (53.0.8): - - DoubleConversion - - ExpoModulesCore - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTAppDelegate - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactAppDependencyProvider - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - ExpoAdapterGoogleSignIn (13.2.0): - - ExpoModulesCore - - GoogleSignIn (~> 7.1) - - React-Core - - ExpoAsset (11.1.5): - - ExpoModulesCore - - ExpoClipboard (7.1.4): - - ExpoModulesCore - - ExpoDocumentPicker (13.1.5): - - ExpoModulesCore - - ExpoFileSystem (18.1.9): - - ExpoModulesCore - - ExpoFont (13.3.1): - - ExpoModulesCore - - ExpoHaptics (14.1.4): - - ExpoModulesCore - - ExpoKeepAwake (14.1.4): - - ExpoModulesCore - - ExpoLinearGradient (14.1.4): - - ExpoModulesCore - - ExpoLinking (7.1.4): - - ExpoModulesCore - - ExpoLocalization (16.1.5): - - ExpoModulesCore - - ExpoModulesCore (2.3.12): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-jsinspector - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - ExpoSpeech (13.1.6): - - ExpoModulesCore - - ExpoSQLite (15.2.9): - - ExpoModulesCore - - ExpoWebBrowser (14.1.6): - - ExpoModulesCore - - fast_float (6.1.4) - - FBLazyVector (0.79.2) - - fmt (11.0.2) - - glog (0.3.5) - - GoogleSignIn (7.1.0): - - AppAuth (< 2.0, >= 1.7.3) - - GTMAppAuth (< 5.0, >= 4.1.1) - - GTMSessionFetcher/Core (~> 3.3) - - GTMAppAuth (4.1.1): - - AppAuth/Core (~> 1.7) - - GTMSessionFetcher/Core (< 4.0, >= 3.3) - - GTMSessionFetcher/Core (3.5.0) - - hermes-engine (0.79.2): - - hermes-engine/Pre-built (= 0.79.2) - - hermes-engine/Pre-built (0.79.2) - - lottie-ios (3.2.3) - - RCT-Folly (2024.11.18.00): - - boost - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - RCT-Folly/Default (= 2024.11.18.00) - - RCT-Folly/Default (2024.11.18.00): - - boost - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - RCT-Folly/Fabric (2024.11.18.00): - - boost - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - RCTDeprecation (0.79.2) - - RCTRequired (0.79.2) - - RCTTypeSafety (0.79.2): - - FBLazyVector (= 0.79.2) - - RCTRequired (= 0.79.2) - - React-Core (= 0.79.2) - - React (0.79.2): - - React-Core (= 0.79.2) - - React-Core/DevSupport (= 0.79.2) - - React-Core/RCTWebSocket (= 0.79.2) - - React-RCTActionSheet (= 0.79.2) - - React-RCTAnimation (= 0.79.2) - - React-RCTBlob (= 0.79.2) - - React-RCTImage (= 0.79.2) - - React-RCTLinking (= 0.79.2) - - React-RCTNetwork (= 0.79.2) - - React-RCTSettings (= 0.79.2) - - React-RCTText (= 0.79.2) - - React-RCTVibration (= 0.79.2) - - React-callinvoker (0.79.2) - - React-Core (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default (= 0.79.2) - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/CoreModulesHeaders (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/Default (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/DevSupport (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default (= 0.79.2) - - React-Core/RCTWebSocket (= 0.79.2) - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/RCTActionSheetHeaders (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/RCTAnimationHeaders (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/RCTBlobHeaders (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/RCTImageHeaders (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/RCTLinkingHeaders (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/RCTNetworkHeaders (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/RCTSettingsHeaders (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/RCTTextHeaders (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/RCTVibrationHeaders (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-Core/RCTWebSocket (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTDeprecation - - React-Core/Default (= 0.79.2) - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.7.1) - - Yoga - - React-CoreModules (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - RCT-Folly (= 2024.11.18.00) - - RCTTypeSafety (= 0.79.2) - - React-Core/CoreModulesHeaders (= 0.79.2) - - React-jsi (= 0.79.2) - - React-jsinspector - - React-jsinspectortracing - - React-NativeModulesApple - - React-RCTBlob - - React-RCTFBReactNativeSpec - - React-RCTImage (= 0.79.2) - - ReactCommon - - SocketRocket (= 0.7.1) - - React-cxxreact (0.79.2): - - boost - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - React-callinvoker (= 0.79.2) - - React-debug (= 0.79.2) - - React-jsi (= 0.79.2) - - React-jsinspector - - React-jsinspectortracing - - React-logger (= 0.79.2) - - React-perflogger (= 0.79.2) - - React-runtimeexecutor (= 0.79.2) - - React-timing (= 0.79.2) - - React-debug (0.79.2) - - React-defaultsnativemodule (0.79.2): - - hermes-engine - - RCT-Folly - - React-domnativemodule - - React-featureflagsnativemodule - - React-hermes - - React-idlecallbacksnativemodule - - React-jsi - - React-jsiexecutor - - React-microtasksnativemodule - - React-RCTFBReactNativeSpec - - React-domnativemodule (0.79.2): - - hermes-engine - - RCT-Folly - - React-Fabric - - React-FabricComponents - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-RCTFBReactNativeSpec - - ReactCommon/turbomodule/core - - Yoga - - React-Fabric (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/animations (= 0.79.2) - - React-Fabric/attributedstring (= 0.79.2) - - React-Fabric/componentregistry (= 0.79.2) - - React-Fabric/componentregistrynative (= 0.79.2) - - React-Fabric/components (= 0.79.2) - - React-Fabric/consistency (= 0.79.2) - - React-Fabric/core (= 0.79.2) - - React-Fabric/dom (= 0.79.2) - - React-Fabric/imagemanager (= 0.79.2) - - React-Fabric/leakchecker (= 0.79.2) - - React-Fabric/mounting (= 0.79.2) - - React-Fabric/observers (= 0.79.2) - - React-Fabric/scheduler (= 0.79.2) - - React-Fabric/telemetry (= 0.79.2) - - React-Fabric/templateprocessor (= 0.79.2) - - React-Fabric/uimanager (= 0.79.2) - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/animations (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/attributedstring (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/componentregistry (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/componentregistrynative (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/components/legacyviewmanagerinterop (= 0.79.2) - - React-Fabric/components/root (= 0.79.2) - - React-Fabric/components/scrollview (= 0.79.2) - - React-Fabric/components/view (= 0.79.2) - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/legacyviewmanagerinterop (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/root (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/scrollview (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/view (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-renderercss - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-Fabric/consistency (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/core (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/dom (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/imagemanager (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/leakchecker (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/mounting (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/observers (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/observers/events (= 0.79.2) - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/observers/events (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/scheduler (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/observers/events - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-performancetimeline - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/telemetry (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/templateprocessor (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/uimanager (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/uimanager/consistency (= 0.79.2) - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererconsistency - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/uimanager/consistency (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererconsistency - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-FabricComponents (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-FabricComponents/components (= 0.79.2) - - React-FabricComponents/textlayoutmanager (= 0.79.2) - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-FabricComponents/components (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-FabricComponents/components/inputaccessory (= 0.79.2) - - React-FabricComponents/components/iostextinput (= 0.79.2) - - React-FabricComponents/components/modal (= 0.79.2) - - React-FabricComponents/components/rncore (= 0.79.2) - - React-FabricComponents/components/safeareaview (= 0.79.2) - - React-FabricComponents/components/scrollview (= 0.79.2) - - React-FabricComponents/components/text (= 0.79.2) - - React-FabricComponents/components/textinput (= 0.79.2) - - React-FabricComponents/components/unimplementedview (= 0.79.2) - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-FabricComponents/components/inputaccessory (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-FabricComponents/components/iostextinput (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-FabricComponents/components/modal (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-FabricComponents/components/rncore (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-FabricComponents/components/safeareaview (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-FabricComponents/components/scrollview (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-FabricComponents/components/text (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-FabricComponents/components/textinput (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-FabricComponents/components/unimplementedview (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-FabricComponents/textlayoutmanager (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-FabricImage (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired (= 0.79.2) - - RCTTypeSafety (= 0.79.2) - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-jsiexecutor (= 0.79.2) - - React-logger - - React-rendererdebug - - React-utils - - ReactCommon - - Yoga - - React-featureflags (0.79.2): - - RCT-Folly (= 2024.11.18.00) - - React-featureflagsnativemodule (0.79.2): - - hermes-engine - - RCT-Folly - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-RCTFBReactNativeSpec - - ReactCommon/turbomodule/core - - React-graphics (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - React-hermes - - React-jsi - - React-jsiexecutor - - React-utils - - React-hermes (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - React-cxxreact (= 0.79.2) - - React-jsi - - React-jsiexecutor (= 0.79.2) - - React-jsinspector - - React-jsinspectortracing - - React-perflogger (= 0.79.2) - - React-runtimeexecutor - - React-idlecallbacksnativemodule (0.79.2): - - glog - - hermes-engine - - RCT-Folly - - React-hermes - - React-jsi - - React-jsiexecutor - - React-RCTFBReactNativeSpec - - React-runtimescheduler - - ReactCommon/turbomodule/core - - React-ImageManager (0.79.2): - - glog - - RCT-Folly/Fabric - - React-Core/Default - - React-debug - - React-Fabric - - React-graphics - - React-rendererdebug - - React-utils - - React-jserrorhandler (0.79.2): - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - React-cxxreact - - React-debug - - React-featureflags - - React-jsi - - ReactCommon/turbomodule/bridging - - React-jsi (0.79.2): - - boost - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - React-jsiexecutor (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - React-cxxreact (= 0.79.2) - - React-jsi (= 0.79.2) - - React-jsinspector - - React-jsinspectortracing - - React-perflogger (= 0.79.2) - - React-jsinspector (0.79.2): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly - - React-featureflags - - React-jsi - - React-jsinspectortracing - - React-perflogger (= 0.79.2) - - React-runtimeexecutor (= 0.79.2) - - React-jsinspectortracing (0.79.2): - - RCT-Folly - - React-oscompat - - React-jsitooling (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - RCT-Folly (= 2024.11.18.00) - - React-cxxreact (= 0.79.2) - - React-jsi (= 0.79.2) - - React-jsinspector - - React-jsinspectortracing - - React-jsitracing (0.79.2): - - React-jsi - - React-logger (0.79.2): - - glog - - React-Mapbuffer (0.79.2): - - glog - - React-debug - - React-microtasksnativemodule (0.79.2): - - hermes-engine - - RCT-Folly - - React-hermes - - React-jsi - - React-jsiexecutor - - React-RCTFBReactNativeSpec - - ReactCommon/turbomodule/core - - react-native-background-actions (4.0.1): - - React-Core - - react-native-cookies (6.2.1): - - React-Core - - react-native-lottie-splash-screen (1.1.2): - - React - - react-native-mmkv (3.2.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - react-native-pager-view (6.7.1): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - react-native-saf-x (2.2.3): - - React-Core - - react-native-safe-area-context (5.4.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - react-native-safe-area-context/common (= 5.4.0) - - react-native-safe-area-context/fabric (= 5.4.0) - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - react-native-safe-area-context/common (5.4.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - react-native-safe-area-context/fabric (5.4.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - react-native-safe-area-context/common - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - react-native-slider (4.5.6): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - react-native-slider/common (= 4.5.6) - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - react-native-slider/common (4.5.6): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - react-native-vector-icons (11.0.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - react-native-webview (13.13.5): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - React-NativeModulesApple (0.79.2): - - glog - - hermes-engine - - React-callinvoker - - React-Core - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsinspector - - React-runtimeexecutor - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - React-oscompat (0.79.2) - - React-perflogger (0.79.2): - - DoubleConversion - - RCT-Folly (= 2024.11.18.00) - - React-performancetimeline (0.79.2): - - RCT-Folly (= 2024.11.18.00) - - React-cxxreact - - React-featureflags - - React-jsinspectortracing - - React-perflogger - - React-timing - - React-RCTActionSheet (0.79.2): - - React-Core/RCTActionSheetHeaders (= 0.79.2) - - React-RCTAnimation (0.79.2): - - RCT-Folly (= 2024.11.18.00) - - RCTTypeSafety - - React-Core/RCTAnimationHeaders - - React-jsi - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - ReactCommon - - React-RCTAppDelegate (0.79.2): - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-CoreModules - - React-debug - - React-defaultsnativemodule - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-jsitooling - - React-NativeModulesApple - - React-RCTFabric - - React-RCTFBReactNativeSpec - - React-RCTImage - - React-RCTNetwork - - React-RCTRuntime - - React-rendererdebug - - React-RuntimeApple - - React-RuntimeCore - - React-runtimescheduler - - React-utils - - ReactCommon - - React-RCTBlob (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - React-Core/RCTBlobHeaders - - React-Core/RCTWebSocket - - React-jsi - - React-jsinspector - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - React-RCTNetwork - - ReactCommon - - React-RCTFabric (0.79.2): - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - React-Core - - React-debug - - React-Fabric - - React-FabricComponents - - React-FabricImage - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-jsinspector - - React-jsinspectortracing - - React-performancetimeline - - React-RCTAnimation - - React-RCTImage - - React-RCTText - - React-rendererconsistency - - React-renderercss - - React-rendererdebug - - React-runtimescheduler - - React-utils - - Yoga - - React-RCTFBReactNativeSpec (0.79.2): - - hermes-engine - - RCT-Folly - - RCTRequired - - RCTTypeSafety - - React-Core - - React-hermes - - React-jsi - - React-jsiexecutor - - React-NativeModulesApple - - ReactCommon - - React-RCTImage (0.79.2): - - RCT-Folly (= 2024.11.18.00) - - RCTTypeSafety - - React-Core/RCTImageHeaders - - React-jsi - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - React-RCTNetwork - - ReactCommon - - React-RCTLinking (0.79.2): - - React-Core/RCTLinkingHeaders (= 0.79.2) - - React-jsi (= 0.79.2) - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - ReactCommon - - ReactCommon/turbomodule/core (= 0.79.2) - - React-RCTNetwork (0.79.2): - - RCT-Folly (= 2024.11.18.00) - - RCTTypeSafety - - React-Core/RCTNetworkHeaders - - React-jsi - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - ReactCommon - - React-RCTRuntime (0.79.2): - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - React-Core - - React-hermes - - React-jsi - - React-jsinspector - - React-jsinspectortracing - - React-jsitooling - - React-RuntimeApple - - React-RuntimeCore - - React-RuntimeHermes - - React-RCTSettings (0.79.2): - - RCT-Folly (= 2024.11.18.00) - - RCTTypeSafety - - React-Core/RCTSettingsHeaders - - React-jsi - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - ReactCommon - - React-RCTText (0.79.2): - - React-Core/RCTTextHeaders (= 0.79.2) - - Yoga - - React-RCTVibration (0.79.2): - - RCT-Folly (= 2024.11.18.00) - - React-Core/RCTVibrationHeaders - - React-jsi - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - ReactCommon - - React-rendererconsistency (0.79.2) - - React-renderercss (0.79.2): - - React-debug - - React-utils - - React-rendererdebug (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - RCT-Folly (= 2024.11.18.00) - - React-debug - - React-rncore (0.79.2) - - React-RuntimeApple (0.79.2): - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - React-callinvoker - - React-Core/Default - - React-CoreModules - - React-cxxreact - - React-featureflags - - React-jserrorhandler - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-Mapbuffer - - React-NativeModulesApple - - React-RCTFabric - - React-RCTFBReactNativeSpec - - React-RuntimeCore - - React-runtimeexecutor - - React-RuntimeHermes - - React-runtimescheduler - - React-utils - - React-RuntimeCore (0.79.2): - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - React-cxxreact - - React-Fabric - - React-featureflags - - React-hermes - - React-jserrorhandler - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-performancetimeline - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - React-runtimeexecutor (0.79.2): - - React-jsi (= 0.79.2) - - React-RuntimeHermes (0.79.2): - - hermes-engine - - RCT-Folly/Fabric (= 2024.11.18.00) - - React-featureflags - - React-hermes - - React-jsi - - React-jsinspector - - React-jsinspectortracing - - React-jsitooling - - React-jsitracing - - React-RuntimeCore - - React-utils - - React-runtimescheduler (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - React-callinvoker - - React-cxxreact - - React-debug - - React-featureflags - - React-hermes - - React-jsi - - React-jsinspectortracing - - React-performancetimeline - - React-rendererconsistency - - React-rendererdebug - - React-runtimeexecutor - - React-timing - - React-utils - - React-timing (0.79.2) - - React-utils (0.79.2): - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - React-debug - - React-hermes - - React-jsi (= 0.79.2) - - ReactAppDependencyProvider (0.79.2): - - ReactCodegen - - ReactCodegen (0.79.2): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-FabricImage - - React-featureflags - - React-graphics - - React-hermes - - React-jsi - - React-jsiexecutor - - React-NativeModulesApple - - React-RCTAppDelegate - - React-rendererdebug - - React-utils - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - ReactCommon (0.79.2): - - ReactCommon/turbomodule (= 0.79.2) - - ReactCommon/turbomodule (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - React-callinvoker (= 0.79.2) - - React-cxxreact (= 0.79.2) - - React-jsi (= 0.79.2) - - React-logger (= 0.79.2) - - React-perflogger (= 0.79.2) - - ReactCommon/turbomodule/bridging (= 0.79.2) - - ReactCommon/turbomodule/core (= 0.79.2) - - ReactCommon/turbomodule/bridging (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - React-callinvoker (= 0.79.2) - - React-cxxreact (= 0.79.2) - - React-jsi (= 0.79.2) - - React-logger (= 0.79.2) - - React-perflogger (= 0.79.2) - - ReactCommon/turbomodule/core (0.79.2): - - DoubleConversion - - fast_float (= 6.1.4) - - fmt (= 11.0.2) - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - React-callinvoker (= 0.79.2) - - React-cxxreact (= 0.79.2) - - React-debug (= 0.79.2) - - React-featureflags (= 0.79.2) - - React-jsi (= 0.79.2) - - React-logger (= 0.79.2) - - React-perflogger (= 0.79.2) - - React-utils (= 0.79.2) - - ReactNativeFileAccess (3.1.1): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - ZIPFoundation - - RNDeviceInfo (14.0.4): - - React-Core - - RNFlashList (1.7.6): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - RNGestureHandler (2.25.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - RNGoogleSignin (13.2.0): - - DoubleConversion - - glog - - GoogleSignIn (~> 7.1) - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - RNReanimated (3.17.5): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - RNReanimated/reanimated (= 3.17.5) - - RNReanimated/worklets (= 3.17.5) - - Yoga - - RNReanimated/reanimated (3.17.5): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - RNReanimated/reanimated/apple (= 3.17.5) - - Yoga - - RNReanimated/reanimated/apple (3.17.5): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - RNReanimated/worklets (3.17.5): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - RNReanimated/worklets/apple (= 3.17.5) - - Yoga - - RNReanimated/worklets/apple (3.17.5): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - RNScreens (4.10.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-RCTImage - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - RNScreens/common (= 4.10.0) - - Yoga - - RNScreens/common (4.10.0): - - DoubleConversion - - glog - - hermes-engine - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-RCTImage - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - RNZipArchive (7.0.1): - - React-Core - - RNZipArchive/Core (= 7.0.1) - - SSZipArchive (~> 2.5.5) - - RNZipArchive/Core (7.0.1): - - React-Core - - SSZipArchive (~> 2.5.5) - - SocketRocket (0.7.1) - - SSZipArchive (2.5.5) - - Yoga (0.0.0) - - ZIPFoundation (0.9.19) - -DEPENDENCIES: - - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - - EXApplication (from `../node_modules/expo-application/ios`) - - EXConstants (from `../node_modules/expo-constants/ios`) - - EXNotifications (from `../node_modules/expo-notifications/ios`) - - Expo (from `../node_modules/expo`) - - "ExpoAdapterGoogleSignIn (from `../node_modules/@react-native-google-signin/google-signin/expo/ios`)" - - ExpoAsset (from `../node_modules/expo-asset/ios`) - - ExpoClipboard (from `../node_modules/expo-clipboard/ios`) - - ExpoDocumentPicker (from `../node_modules/expo-document-picker/ios`) - - ExpoFileSystem (from `../node_modules/expo-file-system/ios`) - - ExpoFont (from `../node_modules/expo-font/ios`) - - ExpoHaptics (from `../node_modules/expo-haptics/ios`) - - ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`) - - ExpoLinearGradient (from `../node_modules/expo-linear-gradient/ios`) - - ExpoLinking (from `../node_modules/expo-linking/ios`) - - ExpoLocalization (from `../node_modules/expo-localization/ios`) - - ExpoModulesCore (from `../node_modules/expo-modules-core`) - - ExpoSpeech (from `../node_modules/expo-speech/ios`) - - ExpoSQLite (from `../node_modules/expo-sqlite/ios`) - - ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`) - - fast_float (from `../node_modules/react-native/third-party-podspecs/fast_float.podspec`) - - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) - - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - - lottie-ios (from `../node_modules/lottie-ios`) - - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - - RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) - - RCTRequired (from `../node_modules/react-native/Libraries/Required`) - - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) - - React (from `../node_modules/react-native/`) - - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) - - React-Core (from `../node_modules/react-native/`) - - React-Core/RCTWebSocket (from `../node_modules/react-native/`) - - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) - - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) - - React-debug (from `../node_modules/react-native/ReactCommon/react/debug`) - - React-defaultsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/defaults`) - - React-domnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/dom`) - - React-Fabric (from `../node_modules/react-native/ReactCommon`) - - React-FabricComponents (from `../node_modules/react-native/ReactCommon`) - - React-FabricImage (from `../node_modules/react-native/ReactCommon`) - - React-featureflags (from `../node_modules/react-native/ReactCommon/react/featureflags`) - - React-featureflagsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/featureflags`) - - React-graphics (from `../node_modules/react-native/ReactCommon/react/renderer/graphics`) - - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`) - - React-idlecallbacksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`) - - React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) - - React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`) - - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) - - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`) - - React-jsinspectortracing (from `../node_modules/react-native/ReactCommon/jsinspector-modern/tracing`) - - React-jsitooling (from `../node_modules/react-native/ReactCommon/jsitooling`) - - React-jsitracing (from `../node_modules/react-native/ReactCommon/hermes/executor/`) - - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) - - react-native-background-actions (from `../node_modules/react-native-background-actions`) - - "react-native-cookies (from `../node_modules/@react-native-cookies/cookies`)" - - react-native-lottie-splash-screen (from `../node_modules/react-native-lottie-splash-screen`) - - react-native-mmkv (from `../node_modules/react-native-mmkv`) - - react-native-pager-view (from `../node_modules/react-native-pager-view`) - - react-native-saf-x (from `../node_modules/react-native-saf-x`) - - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - - "react-native-slider (from `../node_modules/@react-native-community/slider`)" - - "react-native-vector-icons (from `../node_modules/@react-native-vector-icons/common`)" - - react-native-webview (from `../node_modules/react-native-webview`) - - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - - React-oscompat (from `../node_modules/react-native/ReactCommon/oscompat`) - - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) - - React-performancetimeline (from `../node_modules/react-native/ReactCommon/react/performance/timeline`) - - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) - - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) - - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`) - - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) - - React-RCTFabric (from `../node_modules/react-native/React`) - - React-RCTFBReactNativeSpec (from `../node_modules/react-native/React`) - - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) - - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) - - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) - - React-RCTRuntime (from `../node_modules/react-native/React/Runtime`) - - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) - - React-RCTText (from `../node_modules/react-native/Libraries/Text`) - - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - - React-rendererconsistency (from `../node_modules/react-native/ReactCommon/react/renderer/consistency`) - - React-renderercss (from `../node_modules/react-native/ReactCommon/react/renderer/css`) - - React-rendererdebug (from `../node_modules/react-native/ReactCommon/react/renderer/debug`) - - React-rncore (from `../node_modules/react-native/ReactCommon`) - - React-RuntimeApple (from `../node_modules/react-native/ReactCommon/react/runtime/platform/ios`) - - React-RuntimeCore (from `../node_modules/react-native/ReactCommon/react/runtime`) - - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) - - React-RuntimeHermes (from `../node_modules/react-native/ReactCommon/react/runtime`) - - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) - - React-timing (from `../node_modules/react-native/ReactCommon/react/timing`) - - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - - ReactAppDependencyProvider (from `build/generated/ios`) - - ReactCodegen (from `build/generated/ios`) - - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - - ReactNativeFileAccess (from `../node_modules/react-native-file-access`) - - RNDeviceInfo (from `../node_modules/react-native-device-info`) - - "RNFlashList (from `../node_modules/@shopify/flash-list`)" - - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - - "RNGoogleSignin (from `../node_modules/@react-native-google-signin/google-signin`)" - - RNReanimated (from `../node_modules/react-native-reanimated`) - - RNScreens (from `../node_modules/react-native-screens`) - - RNZipArchive (from `../node_modules/react-native-zip-archive`) - - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) - -SPEC REPOS: - trunk: - - AppAuth - - GoogleSignIn - - GTMAppAuth - - GTMSessionFetcher - - SocketRocket - - SSZipArchive - - ZIPFoundation - -EXTERNAL SOURCES: - boost: - :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" - DoubleConversion: - :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" - EXApplication: - :path: "../node_modules/expo-application/ios" - EXConstants: - :path: "../node_modules/expo-constants/ios" - EXNotifications: - :path: "../node_modules/expo-notifications/ios" - Expo: - :path: "../node_modules/expo" - ExpoAdapterGoogleSignIn: - :path: "../node_modules/@react-native-google-signin/google-signin/expo/ios" - ExpoAsset: - :path: "../node_modules/expo-asset/ios" - ExpoClipboard: - :path: "../node_modules/expo-clipboard/ios" - ExpoDocumentPicker: - :path: "../node_modules/expo-document-picker/ios" - ExpoFileSystem: - :path: "../node_modules/expo-file-system/ios" - ExpoFont: - :path: "../node_modules/expo-font/ios" - ExpoHaptics: - :path: "../node_modules/expo-haptics/ios" - ExpoKeepAwake: - :path: "../node_modules/expo-keep-awake/ios" - ExpoLinearGradient: - :path: "../node_modules/expo-linear-gradient/ios" - ExpoLinking: - :path: "../node_modules/expo-linking/ios" - ExpoLocalization: - :path: "../node_modules/expo-localization/ios" - ExpoModulesCore: - :path: "../node_modules/expo-modules-core" - ExpoSpeech: - :path: "../node_modules/expo-speech/ios" - ExpoSQLite: - :path: "../node_modules/expo-sqlite/ios" - ExpoWebBrowser: - :path: "../node_modules/expo-web-browser/ios" - fast_float: - :podspec: "../node_modules/react-native/third-party-podspecs/fast_float.podspec" - FBLazyVector: - :path: "../node_modules/react-native/Libraries/FBLazyVector" - fmt: - :podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec" - glog: - :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" - hermes-engine: - :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" - :tag: hermes-2025-03-03-RNv0.79.0-bc17d964d03743424823d7dd1a9f37633459c5c5 - lottie-ios: - :path: "../node_modules/lottie-ios" - RCT-Folly: - :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" - RCTDeprecation: - :path: "../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation" - RCTRequired: - :path: "../node_modules/react-native/Libraries/Required" - RCTTypeSafety: - :path: "../node_modules/react-native/Libraries/TypeSafety" - React: - :path: "../node_modules/react-native/" - React-callinvoker: - :path: "../node_modules/react-native/ReactCommon/callinvoker" - React-Core: - :path: "../node_modules/react-native/" - React-CoreModules: - :path: "../node_modules/react-native/React/CoreModules" - React-cxxreact: - :path: "../node_modules/react-native/ReactCommon/cxxreact" - React-debug: - :path: "../node_modules/react-native/ReactCommon/react/debug" - React-defaultsnativemodule: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/defaults" - React-domnativemodule: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/dom" - React-Fabric: - :path: "../node_modules/react-native/ReactCommon" - React-FabricComponents: - :path: "../node_modules/react-native/ReactCommon" - React-FabricImage: - :path: "../node_modules/react-native/ReactCommon" - React-featureflags: - :path: "../node_modules/react-native/ReactCommon/react/featureflags" - React-featureflagsnativemodule: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/featureflags" - React-graphics: - :path: "../node_modules/react-native/ReactCommon/react/renderer/graphics" - React-hermes: - :path: "../node_modules/react-native/ReactCommon/hermes" - React-idlecallbacksnativemodule: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks" - React-ImageManager: - :path: "../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios" - React-jserrorhandler: - :path: "../node_modules/react-native/ReactCommon/jserrorhandler" - React-jsi: - :path: "../node_modules/react-native/ReactCommon/jsi" - React-jsiexecutor: - :path: "../node_modules/react-native/ReactCommon/jsiexecutor" - React-jsinspector: - :path: "../node_modules/react-native/ReactCommon/jsinspector-modern" - React-jsinspectortracing: - :path: "../node_modules/react-native/ReactCommon/jsinspector-modern/tracing" - React-jsitooling: - :path: "../node_modules/react-native/ReactCommon/jsitooling" - React-jsitracing: - :path: "../node_modules/react-native/ReactCommon/hermes/executor/" - React-logger: - :path: "../node_modules/react-native/ReactCommon/logger" - React-Mapbuffer: - :path: "../node_modules/react-native/ReactCommon" - React-microtasksnativemodule: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" - react-native-background-actions: - :path: "../node_modules/react-native-background-actions" - react-native-cookies: - :path: "../node_modules/@react-native-cookies/cookies" - react-native-lottie-splash-screen: - :path: "../node_modules/react-native-lottie-splash-screen" - react-native-mmkv: - :path: "../node_modules/react-native-mmkv" - react-native-pager-view: - :path: "../node_modules/react-native-pager-view" - react-native-saf-x: - :path: "../node_modules/react-native-saf-x" - react-native-safe-area-context: - :path: "../node_modules/react-native-safe-area-context" - react-native-slider: - :path: "../node_modules/@react-native-community/slider" - react-native-vector-icons: - :path: "../node_modules/@react-native-vector-icons/common" - react-native-webview: - :path: "../node_modules/react-native-webview" - React-NativeModulesApple: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" - React-oscompat: - :path: "../node_modules/react-native/ReactCommon/oscompat" - React-perflogger: - :path: "../node_modules/react-native/ReactCommon/reactperflogger" - React-performancetimeline: - :path: "../node_modules/react-native/ReactCommon/react/performance/timeline" - React-RCTActionSheet: - :path: "../node_modules/react-native/Libraries/ActionSheetIOS" - React-RCTAnimation: - :path: "../node_modules/react-native/Libraries/NativeAnimation" - React-RCTAppDelegate: - :path: "../node_modules/react-native/Libraries/AppDelegate" - React-RCTBlob: - :path: "../node_modules/react-native/Libraries/Blob" - React-RCTFabric: - :path: "../node_modules/react-native/React" - React-RCTFBReactNativeSpec: - :path: "../node_modules/react-native/React" - React-RCTImage: - :path: "../node_modules/react-native/Libraries/Image" - React-RCTLinking: - :path: "../node_modules/react-native/Libraries/LinkingIOS" - React-RCTNetwork: - :path: "../node_modules/react-native/Libraries/Network" - React-RCTRuntime: - :path: "../node_modules/react-native/React/Runtime" - React-RCTSettings: - :path: "../node_modules/react-native/Libraries/Settings" - React-RCTText: - :path: "../node_modules/react-native/Libraries/Text" - React-RCTVibration: - :path: "../node_modules/react-native/Libraries/Vibration" - React-rendererconsistency: - :path: "../node_modules/react-native/ReactCommon/react/renderer/consistency" - React-renderercss: - :path: "../node_modules/react-native/ReactCommon/react/renderer/css" - React-rendererdebug: - :path: "../node_modules/react-native/ReactCommon/react/renderer/debug" - React-rncore: - :path: "../node_modules/react-native/ReactCommon" - React-RuntimeApple: - :path: "../node_modules/react-native/ReactCommon/react/runtime/platform/ios" - React-RuntimeCore: - :path: "../node_modules/react-native/ReactCommon/react/runtime" - React-runtimeexecutor: - :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" - React-RuntimeHermes: - :path: "../node_modules/react-native/ReactCommon/react/runtime" - React-runtimescheduler: - :path: "../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler" - React-timing: - :path: "../node_modules/react-native/ReactCommon/react/timing" - React-utils: - :path: "../node_modules/react-native/ReactCommon/react/utils" - ReactAppDependencyProvider: - :path: build/generated/ios - ReactCodegen: - :path: build/generated/ios - ReactCommon: - :path: "../node_modules/react-native/ReactCommon" - ReactNativeFileAccess: - :path: "../node_modules/react-native-file-access" - RNDeviceInfo: - :path: "../node_modules/react-native-device-info" - RNFlashList: - :path: "../node_modules/@shopify/flash-list" - RNGestureHandler: - :path: "../node_modules/react-native-gesture-handler" - RNGoogleSignin: - :path: "../node_modules/@react-native-google-signin/google-signin" - RNReanimated: - :path: "../node_modules/react-native-reanimated" - RNScreens: - :path: "../node_modules/react-native-screens" - RNZipArchive: - :path: "../node_modules/react-native-zip-archive" - Yoga: - :path: "../node_modules/react-native/ReactCommon/yoga" - -SPEC CHECKSUMS: - AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73 - boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 - DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb - EXApplication: 63b87ca5204304007fac6b1be432f8afa3731c17 - EXConstants: 9f310f44bfedba09087042756802040e464323c0 - EXNotifications: 9123abeeecc540790f2ae0e2f08bcb5e97b4ebaa - Expo: 769ab5c190382eedebc733af6708bbc9ca5f643b - ExpoAdapterGoogleSignIn: f5ca2b67502442629d99dbe202b6ae821d77c75d - ExpoAsset: 3bc9adb7dbbf27ae82c18ca97eb988a3ae7e73b1 - ExpoClipboard: c9771e7aa5f1316183e4a6f40a5755a0b98a6b8d - ExpoDocumentPicker: 5487ce33530198f4ecf724e650d390b2abe84c15 - ExpoFileSystem: 0f3f466ecd3560f55768cd3f94ac3a17f093b8e6 - ExpoFont: abbb91a911eb961652c2b0a22eef801860425ed6 - ExpoHaptics: 0ff6e0d83cd891178a306e548da1450249d54500 - ExpoKeepAwake: bf0811570c8da182bfb879169437d4de298376e7 - ExpoLinearGradient: a3126d055dd021c1eb38a2cf28052ccbd1a2b3e2 - ExpoLinking: b6a0320bf46ae36807d3dc27b4a1d4474505f99c - ExpoLocalization: f6c6aaa3bfff77b666bb958bdfeb5c55df21d990 - ExpoModulesCore: 3ac17421302df62928fc99c133cf25bdbcf0b004 - ExpoSpeech: 45bbe42358d8ed3d101cc0222890f93e58cece38 - ExpoSQLite: 06c004fa0c71f62de09f34710534a90958bdeb50 - ExpoWebBrowser: 06fb5f767f53ad53944b068cdd207984cb998712 - fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 - FBLazyVector: 84b955f7b4da8b895faf5946f73748267347c975 - fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd - glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 - GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db - GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de - GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 - hermes-engine: 314be5250afa5692b57b4dd1705959e1973a8ebe - lottie-ios: c058aeafa76daa4cf64d773554bccc8385d0150e - RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82 - RCTDeprecation: 83ffb90c23ee5cea353bd32008a7bca100908f8c - RCTRequired: eb7c0aba998009f47a540bec9e9d69a54f68136e - RCTTypeSafety: 659ae318c09de0477fd27bbc9e140071c7ea5c93 - React: c2d3aa44c49bb34e4dfd49d3ee92da5ebacc1c1c - React-callinvoker: 1bdfb7549b5af266d85757193b5069f60659ef9d - React-Core: 10597593fdbae06f0089881e025a172e51d4a769 - React-CoreModules: 6907b255529dd46895cf687daa67b24484a612c2 - React-cxxreact: a9f5b8180d6955bc3f6a3fcd657c4d9b4d95c1f6 - React-debug: e74e76912b91e08d580c481c34881899ccf63da9 - React-defaultsnativemodule: 11f6ee2cf69bf3af9d0f28a6253def33d21b5266 - React-domnativemodule: f940bbc4fa9e134190acbf3a4a9f95621b5a8f51 - React-Fabric: 6f5c357bf3a42ff11f8844ad3fc7a1eb04f4b9de - React-FabricComponents: 10e0c0209822ac9e69412913a8af1ca33573379b - React-FabricImage: f582e764072dfa4715ae8c42979a5bace9cbcc12 - React-featureflags: d5facceff8f8f6de430e0acecf4979a9a0839ba9 - React-featureflagsnativemodule: a7dd141f1ef4b7c1331af0035689fbc742a49ff4 - React-graphics: 36ae3407172c1c77cea29265d2b12b90aaef6aa0 - React-hermes: 9116d4e6d07abeb519a2852672de087f44da8f12 - React-idlecallbacksnativemodule: ae7f5ffc6cf2d2058b007b78248e5b08172ad5c3 - React-ImageManager: 9daee0dc99ad6a001d4b9e691fbf37107e2b7b54 - React-jserrorhandler: 1e6211581071edaf4ecd5303147328120c73f4dc - React-jsi: 753ba30c902f3a41fa7f956aca8eea3317a44ee6 - React-jsiexecutor: 47520714aa7d9589c51c0f3713dfbfca4895d4f9 - React-jsinspector: cfd27107f6d6f1076a57d88c932401251560fe5f - React-jsinspectortracing: 76a7d791f3c0c09a0d2bf6f46dfb0e79a4fcc0ac - React-jsitooling: 995e826570dd58f802251490486ebd3244a037ab - React-jsitracing: 094ae3d8c123cea67b50211c945b7c0443d3e97b - React-logger: 8edfcedc100544791cd82692ca5a574240a16219 - React-Mapbuffer: c3f4b608e4a59dd2f6a416ef4d47a14400194468 - React-microtasksnativemodule: 054f34e9b82f02bd40f09cebd4083828b5b2beb6 - react-native-background-actions: 48e6bad9e2a47e3b04858634c5a05ea11062f680 - react-native-cookies: d648ab7025833b977c0b19e142503034f5f29411 - react-native-lottie-splash-screen: 2d84b1c81c176981d3e5e17df14da5a99a2f5082 - react-native-mmkv: d3cc73d2554fafa20dc5b86386359034d1faf8ff - react-native-pager-view: f238ed7fb53458bd03366944a33686f067c83e9a - react-native-saf-x: 684113757246eb8f86d2bae21d965652f78f8eeb - react-native-safe-area-context: 562163222d999b79a51577eda2ea8ad2c32b4d06 - react-native-slider: 78ccabe016aef7418b1a846b31115b4165c4dde6 - react-native-vector-icons: 9bc211082d39578babf726855c2f7f01ae3e9834 - react-native-webview: 520bcb79c3f2af91e157cdd695732a34ab5f25c8 - React-NativeModulesApple: 2c4377e139522c3d73f5df582e4f051a838ff25e - React-oscompat: ef5df1c734f19b8003e149317d041b8ce1f7d29c - React-perflogger: 9a151e0b4c933c9205fd648c246506a83f31395d - React-performancetimeline: 5b0dfc0acba29ea0269ddb34cd6dd59d3b8a1c66 - React-RCTActionSheet: a499b0d6d9793886b67ba3e16046a3fef2cdbbc3 - React-RCTAnimation: cc64adc259aabc3354b73065e2231d796dfce576 - React-RCTAppDelegate: 9d523da768f1c9e84c5f3b7e3624d097dfb0e16b - React-RCTBlob: e727f53eeefded7e6432eb76bd22b57bc880e5d1 - React-RCTFabric: 58590aa4fdb4ad546c06a7449b486cf6844e991f - React-RCTFBReactNativeSpec: 9064c63d99e467a3893e328ba3612745c3c3a338 - React-RCTImage: 7159cbdbb18a09d97ba1a611416eced75b3ccb29 - React-RCTLinking: 46293afdb859bccc63e1d3dedc6901a3c04ef360 - React-RCTNetwork: 4a6cd18f5bcd0363657789c64043123a896b1170 - React-RCTRuntime: 5ab904fd749aa52f267ef771d265612582a17880 - React-RCTSettings: 61e361dc85136d1cb0e148b7541993d2ee950ea7 - React-RCTText: abd1e196c3167175e6baef18199c6d9d8ac54b4e - React-RCTVibration: 490e0dcb01a3fe4a0dfb7bc51ad5856d8b84f343 - React-rendererconsistency: 351fdbc5c1fe4da24243d939094a80f0e149c7a1 - React-renderercss: 3438814bee838ae7840a633ab085ac81699fd5cf - React-rendererdebug: 0ac2b9419ad6f88444f066d4b476180af311fb1e - React-rncore: 57ed480649bb678d8bdc386d20fee8bf2b0c307c - React-RuntimeApple: 8b7a9788f31548298ba1990620fe06b40de65ad7 - React-RuntimeCore: e03d96fbd57ce69fd9bca8c925942194a5126dbc - React-runtimeexecutor: d60846710facedd1edb70c08b738119b3ee2c6c2 - React-RuntimeHermes: aab794755d9f6efd249b61f3af4417296904e3ba - React-runtimescheduler: c3cd124fa5db7c37f601ee49ca0d97019acd8788 - React-timing: a90f4654cbda9c628614f9bee68967f1768bd6a5 - React-utils: a612d50555b6f0f90c74b7d79954019ad47f5de6 - ReactAppDependencyProvider: 04d5eb15eb46be6720e17a4a7fa92940a776e584 - ReactCodegen: d366308232580af80966d4dac79123c899fa2d37 - ReactCommon: 76d2dc87136d0a667678668b86f0fca0c16fdeb0 - ReactNativeFileAccess: 2eddebbd53df4aba596debebe3c0a5893e1996bd - RNDeviceInfo: d863506092aef7e7af3a1c350c913d867d795047 - RNFlashList: 7ad51f0d0d51a3b7b1d1bb07947b927cb352afc4 - RNGestureHandler: ebef699ea17e7c0006c1074e1e423ead60ce0121 - RNGoogleSignin: f3168a11bf7c59eaa34a1b466e2b372f1e5ca95a - RNReanimated: 2313402fe27fecb7237619e9c6fcee3177f08a65 - RNScreens: 5621e3ad5a329fbd16de683344ac5af4192b40d3 - RNZipArchive: f4a5af907d1581e995c879e34799be35c6bc3e21 - SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - SSZipArchive: c69881e8ac5521f0e622291387add5f60f30f3c4 - Yoga: c758bfb934100bb4bf9cbaccb52557cee35e8bdf - ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c - -PODFILE CHECKSUM: 720aa79d2a01f2036c463649ebeda6adc89b71e0 - -COCOAPODS: 1.15.2 diff --git a/ios/loading.json b/ios/loading.json deleted file mode 100644 index 906dbc98d..000000000 --- a/ios/loading.json +++ /dev/null @@ -1,2 +0,0 @@ -{"v":"5.9.0","fr":90,"ip":0,"op":100,"w":500,"h":500,"nm":"Untitled file","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Formebene 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":29,"s":[0]},{"t":30,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[262.75,248.531,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.281,0.906],[0,0],[-0.188,0.625],[0.188,0.281]],"o":[[0,0],[0,0],[0.156,-0.531],[0,0],[0.188,-0.625],[-0.531,-0.5]],"v":[[34.531,-109.562],[34.938,-103.844],[35.594,-105.625],[36.875,-106.875],[37.688,-108.219],[37.5,-109.344]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100.453,99.743],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42,"s":[0]},{"t":43,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Form 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[21,65],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[25,-109.5],"ix":2},"a":{"a":0,"k":[0,-32.5],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":42,"s":[100,0]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":47,"s":[100,100]},{"t":71,"s":[100,101.54]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 7","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[101,18.75],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[25.25,-35.625],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":47,"s":[0,100]},{"t":52,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 6","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[122.25,18.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[24.625,-75.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":45,"s":[0,100]},{"t":50,"s":[93.456,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[26.625,-68.125],"ix":2},"a":{"a":0,"k":[26.625,-68.125],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 3","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-122,-60],"ix":2},"a":{"a":0,"k":[-32,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":32,"s":[0,47.945]},{"t":38,"s":[119.685,47.945]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[63.5,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-109,-95],"ix":2},"a":{"a":0,"k":[-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[0,47]},{"t":35,"s":[87.222,47]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 2","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[63.5,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-109,9.5],"ix":2},"a":{"a":0,"k":[-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":36,"s":[0,47]},{"t":41,"s":[87.7,47]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 3","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[63.5,37.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.172549024224,0.200000017881,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-109,-26],"ix":2},"a":{"a":0,"k":[-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":34,"s":[0,47]},{"t":39,"s":[87.7,47]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 4","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,62],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-56.8,50.5],"ix":2},"a":{"a":0,"k":[0,-31],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":44,"s":[100,0]},{"t":49,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k" -:0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 8","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[50.8,15],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-98.2,44],"ix":2},"a":{"a":0,"k":[-25,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":40,"s":[0,100]},{"t":44,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 7","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[33.6,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-97.9,98],"ix":2},"a":{"a":0,"k":[-16,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":44,"s":[0,99.889]},{"t":49,"s":[100,99.889]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 6","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,80.75],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-106.75,36.525],"ix":2},"a":{"a":0,"k":[0,-40.375],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":39,"s":[100,0]},{"t":44,"s":[100,100]}],"ix":3},"r":{"a":0,"k":-0.026,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-81.5,76.875],"ix":2},"a":{"a":0,"k":[-81.5,76.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 1","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[19.5,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-30.125,3],"ix":2},"a":{"a":0,"k":[0,-13],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":56,"s":[100,0]},{"t":59,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 7","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[19,26.779],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[81.562,3],"ix":2},"a":{"a":0,"k":[0,-14],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":56,"s":[100,0]},{"t":59,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 6","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[131,18.25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[25.5,-5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":50,"s":[0,100]},{"t":56,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[25.562,8.062],"ix":2},"a":{"a":0,"k":[25.562,8.062],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 2","np":3,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[65.5,111],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847058883368,0.89019613827,0.90588241278,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":57,"s":[-13.5,66.75],"to":[0,0],"ti":[0,0]},{"t":63,"s":[-46,120]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":57,"s":[100,100]},{"t":63,"s":[100,0]}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":57,"s":[0]},{"t":63,"s":[90]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[9.438,0.188],[0,0],[0,0],[41.507,0],[0,0],[-6.5,54.25],[0,0],[0,0]],"o":[[-9.438,-0.188],[0,0],[0,0],[-0.25,0],[0,0],[0.328,-2.742],[0,0],[0,0]],"v":[[14,17.438],[-3.313,17.5],[-3.5,50.875],[-45,95.5],[-38.5,116.75],[14.25,61.25],[14.5,22.75],[14.656,21.688]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Form 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-13.318,67.059],"ix":2},"a":{"a":0,"k":[-13.318,67.059],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 4","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[75,104.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rechteckpfad: 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847058883368,0.89019613827,0.90588241278,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[62.75,62.5],"to":[0,0],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63,"s":[60,96],"to":[0,0],"ti":[0,0]},{"t":68,"s":[98,74]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[100,100]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":63,"s":[100,62.5]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":64,"s":[110,50]},{"t":68,"s":[100,0]}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":63,"s":[0]},{"t":68,"s":[-160]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Rechteck 5","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-13,0],[0,0],[-3.938,11.938],[0,0],[0,0],[0,0],[6.5,0],[0,0],[0.31,6.262],[0,0],[9.062,0.125]],"o":[[0,0],[0,0],[13,0],[0,0],[4.009,-12.155],[0,0],[0,0],[0,0],[-6.5,0],[0,0],[-0.312,-6.312],[0,0],[-9.062,-0.125]],"v":[[31.062,16.938],[30.812,95.938],[44.062,109.938],[73.688,109.938],[90.312,97.188],[95.812,74.438],[79.062,69.688],[74.562,83.938],[66.75,90.625],[56.062,90.938],[50.562,84.938],[50.312,21.312],[50.125,17]],"c":true},"ix":2},"nm":"Pfad 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Kontur 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509803922,0.172549019608,0.20000001496,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fläche 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Form 4","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[63.312,63.43],"ix":2},"a":{"a":0,"k":[63.312,63.43],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Gruppe 5","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Ellipse 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[0,0,100]},{"t":30,"s":[340,340,100]}],"ix":6,"l":2,"x":"var $bm_rt;\n$bm_rt = transform.scale;"}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 3","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847100019455,0.890200018883,0.905900001526,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":2,"bm":0,"nm":"Fill","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Group","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":5,"s":[0,0,100]},{"t":25,"s":[400,400,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.070600003004,0.431400001049,0.505900025368,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":2,"bm":0,"nm":"Fill","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Group","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Ellipse 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[0,0,100]},{"t":20,"s":[500,500,100]}],"ix":6,"l":2,"x":"var $bm_rt;\n$bm_rt = transform.scale;"}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 4","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074500001967,0.172499999404,0.20000000298,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":2,"bm":0,"nm":"Fill","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformieren"}],"nm":"Group","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 5268de455..4b993cd48 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,7 +6,7 @@ const baseModuleNameMapper = { '^@hooks$': '/src/hooks/index', '^@hooks/(.*)$': '/src/hooks/$1', '^@screens/(.*)$': '/src/screens/$1', - '^@strings/(.*)$': '/strings/$1', + '^@i18n/(.*)$': '/src/i18n/$1', '^@theme/(.*)$': '/src/theme/$1', '^@utils/(.*)$': '/src/utils/$1', '^@plugins/(.*)$': '/src/plugins/$1', @@ -16,9 +16,13 @@ const baseModuleNameMapper = { '^@api/(.*)$': '/src/api/$1', '^@type/(.*)$': '/src/type/$1', '^@specs/(.*)$': '/specs/$1', - '^@test-utils$': '/__tests-modules__/test-utils', + '^@modules/nitro-epub$': '/modules/nitro-epub/src/index', + '^@modules/nitro-tts$': '/modules/nitro-tts/src/index', + '^@modules/(.*)$': '/modules/$1', + '^@test-utils$': '/test/test-utils', + '^@env$': '/src/generated/build-info', // Mock static assets - '\\.(jpg|jpeg|png|gif|webp|svg)$': '/__mocks__/fileMock.js', + '\\.(jpg|jpeg|png|gif|webp|svg)$': '/test/mocks/fileMock.js', }; const baseTransform = { @@ -31,7 +35,7 @@ const baseTransformIgnorePatterns = [ ]; module.exports = { - moduleDirectories: ['node_modules', '__tests-modules__'], + moduleDirectories: ['node_modules'], projects: [ // --- Project 1: Database / pure logic tests (node environment) --- { @@ -65,8 +69,8 @@ module.exports = { transform: baseTransform, transformIgnorePatterns: baseTransformIgnorePatterns, moduleNameMapper: baseModuleNameMapper, - setupFiles: ['/__mocks__/index.js'], - setupFilesAfterEnv: ['/__tests__/jest.setup.ts'], + setupFiles: ['/test/mocks/index.js'], + setupFilesAfterEnv: ['/test/setup/jest.ts'], collectCoverageFrom: [ 'src/**/*.{ts,tsx}', '!src/database/queries/**/__tests__/**', diff --git a/metro.config.js b/metro.config.js index 07147c371..2987f0660 100644 --- a/metro.config.js +++ b/metro.config.js @@ -1,55 +1,64 @@ -const { getDefaultConfig } = require('expo/metro-config'); -const { mergeConfig } = require('@react-native/metro-config'); - -/** - * Metro configuration - * https://reactnative.dev/docs/metro - * - * @type {import('@react-native/metro-config').MetroConfig} - */ +const { withRozenite } = require('@rozenite/metro'); +const { withRozeniteExpoAtlasPlugin } = require('@rozenite/expo-atlas-plugin'); -const path = require('path'); +const { getDefaultConfig } = require('expo/metro-config'); const fs = require('fs'); -const defaultConfig = getDefaultConfig(__dirname); - -const map = { - '.ico': 'image/x-icon', - '.html': 'text/html', - '.js': 'text/javascript', - '.json': 'application/json', - '.css': 'text/css', - '.png': 'image/png', - '.jpg': 'image/jpeg', +const path = require('path'); + +const config = getDefaultConfig(__dirname); +const readerAssetsRoot = path.resolve(__dirname, 'assets', 'reader'); +const readerAssetContentTypes = { + '.css': 'text/css; charset=utf-8', + '.js': 'text/javascript; charset=utf-8', + '.ttf': 'font/ttf', }; -const customConfig = { - resolver: { - unstable_enableSymlinks: true, - sourceExts: [...defaultConfig.resolver.sourceExts, 'sql'], - }, - server: { - port: 8081, - enhanceMiddleware: (metroMiddleware, metroServer) => { - return (request, res, next) => { - const filePath = path.join( - __dirname, - 'android/app/src/main', - request._parsedUrl.path || '', - ); - const ext = path.parse(filePath).ext; - if (fs.existsSync(filePath)) { - try { - const data = fs.readFileSync(filePath); - res.setHeader('Content-type', map[ext] || 'text/plain'); - res.end(data); - } catch (err) { - res.statusCode = 500; - res.end(`Error getting the file: ${err}.`); - } - } else { - return metroMiddleware(request, res, next); - } - }; - }, - }, + +config.resolver.sourceExts.push('sql'); + +config.server.enhanceMiddleware = metroMiddleware => { + return (request, response, next) => { + let pathname; + + try { + pathname = decodeURIComponent( + new URL(request.url, 'http://localhost').pathname, + ); + } catch { + return metroMiddleware(request, response, next); + } + + if (!pathname.startsWith('/assets/')) { + return metroMiddleware(request, response, next); + } + + const assetPath = path.resolve( + readerAssetsRoot, + pathname.slice('/assets/'.length), + ); + const isReaderAsset = + assetPath.startsWith(`${readerAssetsRoot}${path.sep}`) && + fs.existsSync(assetPath) && + fs.statSync(assetPath).isFile(); + + if (!isReaderAsset) { + return metroMiddleware(request, response, next); + } + + response.setHeader( + 'Content-Type', + readerAssetContentTypes[path.extname(assetPath)] || + 'application/octet-stream', + ); + + if (request.method === 'HEAD') { + return response.end(); + } + + fs.createReadStream(assetPath).pipe(response); + }; }; -module.exports = mergeConfig(defaultConfig, customConfig); + +module.exports = withRozenite(config, { + enabled: process.env.WITH_ROZENITE === 'true', + enhanceMetroConfig: metroConfig => withRozeniteExpoAtlasPlugin(metroConfig), +}); diff --git a/modules/native-background-tasks/android/build.gradle b/modules/native-background-tasks/android/build.gradle new file mode 100644 index 000000000..6a0a03991 --- /dev/null +++ b/modules/native-background-tasks/android/build.gradle @@ -0,0 +1,28 @@ +plugins { + id 'com.android.library' + id 'expo-module-gradle-plugin' + id 'org.jetbrains.kotlin.kapt' +} + +group = 'expo.modules.nativebackgroundtasks' +version = '0.1.0' + +android { + namespace "expo.modules.nativebackgroundtasks" + defaultConfig { + versionCode 1 + versionName "0.1.0" + } + lintOptions { + abortOnError false + } +} + +dependencies { + implementation 'com.facebook.react:react-android' + implementation 'androidx.room:room-runtime:2.7.0' + implementation 'androidx.room:room-ktx:2.7.0' + implementation 'androidx.work:work-runtime-ktx:2.10.0' + kapt 'androidx.room:room-compiler:2.7.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0' +} \ No newline at end of file diff --git a/modules/native-background-tasks/android/src/main/AndroidManifest.xml b/modules/native-background-tasks/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..cc38dfd3b --- /dev/null +++ b/modules/native-background-tasks/android/src/main/AndroidManifest.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/AutomaticBackupScheduleWorker.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/AutomaticBackupScheduleWorker.kt new file mode 100644 index 000000000..c29471058 --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/AutomaticBackupScheduleWorker.kt @@ -0,0 +1,30 @@ +package expo.modules.nativebackgroundtasks + +import android.content.Context +import androidx.work.CoroutineWorker +import androidx.work.WorkerParameters + +class AutomaticBackupScheduleWorker( + appContext: Context, + workerParams: WorkerParameters, +) : CoroutineWorker(appContext, workerParams) { + override suspend fun doWork(): Result { + val title = inputData.getString(AutomaticBackupScheduler.TITLE) + ?: AutomaticBackupScheduler.DEFAULT_TITLE + val description = inputData.getString(AutomaticBackupScheduler.DESCRIPTION) + ?: AutomaticBackupScheduler.DEFAULT_DESCRIPTION + val directoryUri = inputData.getString(AutomaticBackupScheduler.DIRECTORY_URI) + + return try { + BackgroundTaskScheduler.enqueueAutomaticBackup( + applicationContext, + title, + description, + directoryUri, + ) + Result.success() + } catch (_: Exception) { + Result.retry() + } + } +} diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/AutomaticBackupScheduler.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/AutomaticBackupScheduler.kt new file mode 100644 index 000000000..24eb68aea --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/AutomaticBackupScheduler.kt @@ -0,0 +1,56 @@ +package expo.modules.nativebackgroundtasks + +import android.content.Context +import androidx.work.Data +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager +import java.util.concurrent.TimeUnit + +object AutomaticBackupScheduler { + const val TITLE = "title" + const val DESCRIPTION = "description" + const val DIRECTORY_URI = "directoryUri" + const val DEFAULT_TITLE = "Local Backup" + const val DEFAULT_DESCRIPTION = "Preparing" + + private const val WORK_NAME = "lnreader-automatic-backup" + private val allowedIntervals = setOf(6L, 12L, 24L, 48L, 168L) + + fun schedule( + context: Context, + intervalHours: Long, + title: String, + description: String, + directoryUri: String?, + ) { + require(intervalHours in allowedIntervals) { + "Unsupported automatic backup interval: $intervalHours" + } + + val request = + PeriodicWorkRequestBuilder( + intervalHours, + TimeUnit.HOURS, + ) + .setInitialDelay(intervalHours, TimeUnit.HOURS) + .setInputData( + Data.Builder() + .putString(TITLE, title) + .putString(DESCRIPTION, description) + .putString(DIRECTORY_URI, directoryUri) + .build(), + ) + .build() + + WorkManager.getInstance(context).enqueueUniquePeriodicWork( + WORK_NAME, + ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, + request, + ) + } + + fun cancel(context: Context) { + WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME) + } +} diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskDao.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskDao.kt new file mode 100644 index 000000000..bda77abcb --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskDao.kt @@ -0,0 +1,59 @@ +package expo.modules.nativebackgroundtasks + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query + +@Dao +interface BackgroundTaskDao { + @Insert(onConflict = OnConflictStrategy.ABORT) + suspend fun insert(task: BackgroundTaskEntity) + + @Query("SELECT * FROM background_tasks ORDER BY createdAt ASC") + suspend fun getAll(): List + + @Query("SELECT * FROM background_tasks WHERE id = :id LIMIT 1") + suspend fun get(id: String): BackgroundTaskEntity? + + @Query("SELECT * FROM background_tasks WHERE type = :type AND state IN ('queued', 'running', 'paused') LIMIT 1") + suspend fun getActiveByType(type: String): BackgroundTaskEntity? + + @Query("UPDATE background_tasks SET state = :state, updatedAt = :updatedAt WHERE id = :id") + suspend fun updateState(id: String, state: String, updatedAt: Long) + + @Query("UPDATE background_tasks SET state = :state, updatedAt = :updatedAt WHERE id = :id AND state = 'running'") + suspend fun finishRunning(id: String, state: String, updatedAt: Long) + + @Query( + """ + UPDATE background_tasks + SET state = :state, attempt = attempt + 1, updatedAt = :updatedAt + WHERE id = :id + AND state = 'queued' + AND ( + type != 'DOWNLOAD_CHAPTER' + OR ( + SELECT COUNT(*) + FROM background_tasks + WHERE type = 'DOWNLOAD_CHAPTER' AND state = 'running' + ) < :maxConcurrentDownloads + ) + """, + ) + suspend fun tryMarkRunning( + id: String, + state: String, + updatedAt: Long, + maxConcurrentDownloads: Int, + ): Int + + @Query("UPDATE background_tasks SET progress = :progress, progressText = :progressText, updatedAt = :updatedAt WHERE id = :id") + suspend fun updateProgress(id: String, progress: Double?, progressText: String?, updatedAt: Long) + + @Query("UPDATE background_tasks SET checkpoint = :checkpoint, updatedAt = :updatedAt WHERE id = :id") + suspend fun updateCheckpoint(id: String, checkpoint: String?, updatedAt: Long) + + @Query("UPDATE background_tasks SET workId = :workId, updatedAt = :updatedAt WHERE id = :id") + suspend fun assignWork(id: String, workId: String, updatedAt: Long) +} diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskDatabase.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskDatabase.kt new file mode 100644 index 000000000..006ab0440 --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskDatabase.kt @@ -0,0 +1,37 @@ +package expo.modules.nativebackgroundtasks + +import android.content.Context +import androidx.room.Database +import androidx.room.migration.Migration +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.sqlite.db.SupportSQLiteDatabase + +@Database(entities = [BackgroundTaskEntity::class], version = 2, exportSchema = false) +abstract class BackgroundTaskDatabase : RoomDatabase() { + abstract fun tasks(): BackgroundTaskDao + + companion object { + @Volatile private var instance: BackgroundTaskDatabase? = null + + fun get(context: Context): BackgroundTaskDatabase = + instance ?: synchronized(this) { + instance ?: Room.databaseBuilder( + context.applicationContext, + BackgroundTaskDatabase::class.java, + "lnreader-background-tasks.db", + ).addMigrations(MIGRATION_1_2) + .build() + .also { instance = it } + } + + private val MIGRATION_1_2 = object : Migration(1, 2) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL( + "ALTER TABLE background_tasks ADD COLUMN queueName TEXT NOT NULL " + + "DEFAULT 'lnreader-background-task-queue'", + ) + } + } + } +} diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskEntity.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskEntity.kt new file mode 100644 index 000000000..018e70bc0 --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskEntity.kt @@ -0,0 +1,33 @@ +package expo.modules.nativebackgroundtasks + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "background_tasks") +data class BackgroundTaskEntity( + @PrimaryKey val id: String, + val type: String, + val payload: String, + val title: String, + val description: String, + val queueName: String, + val state: String, + val progress: Double?, + val progressText: String?, + val checkpoint: String?, + val attempt: Int, + val workId: String?, + val createdAt: Long, + val updatedAt: Long, +) + +object BackgroundTaskState { + const val QUEUED = "queued" + const val RUNNING = "running" + const val PAUSED = "paused" + const val SUCCEEDED = "succeeded" + const val FAILED = "failed" + const val CANCELLED = "cancelled" + + val active = listOf(QUEUED, RUNNING, PAUSED) +} diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskScheduler.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskScheduler.kt new file mode 100644 index 000000000..6ef1018e0 --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/BackgroundTaskScheduler.kt @@ -0,0 +1,143 @@ +package expo.modules.nativebackgroundtasks + +import android.content.Context +import android.net.Uri +import android.provider.DocumentsContract +import androidx.work.Data +import androidx.work.Constraints +import androidx.work.ExistingWorkPolicy +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager +import androidx.work.await +import java.util.UUID + +object BackgroundTaskScheduler { + const val TASK_ID = "taskId" + + suspend fun enqueue(context: Context, taskId: String): UUID { + val task = BackgroundTaskDatabase.get(context).tasks().get(taskId) + ?: throw IllegalArgumentException("Unknown background task: $taskId") + val requestBuilder = OneTimeWorkRequestBuilder() + .setInputData(Data.Builder().putString(TASK_ID, taskId).build()) + .addTag(taskId) + if (task.type in NETWORK_TASKS) { + requestBuilder.setConstraints( + Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build(), + ) + } + val request = requestBuilder.build() + BackgroundTaskDatabase.get(context).tasks() + .assignWork(taskId, request.id.toString(), System.currentTimeMillis()) + WorkManager.getInstance(context).enqueueUniqueWork( + task.queueName, + ExistingWorkPolicy.APPEND_OR_REPLACE, + request, + ) + return request.id + } + + suspend fun cancel(context: Context, taskId: String) { + WorkManager.getInstance(context).cancelAllWorkByTag(taskId).await() + } + + suspend fun enqueueLibraryUpdate( + context: Context, + title: String, + description: String, + ): UUID? { + val dao = BackgroundTaskDatabase.get(context).tasks() + if (dao.getActiveByType(LIBRARY_UPDATE_TASK_TYPE) != null) { + return null + } + + val now = System.currentTimeMillis() + val task = BackgroundTaskEntity( + id = UUID.randomUUID().toString(), + type = LIBRARY_UPDATE_TASK_TYPE, + payload = """{"name":"$LIBRARY_UPDATE_TASK_TYPE"}""", + title = title, + description = description, + queueName = "lnreader-background-task:task:$LIBRARY_UPDATE_TASK_TYPE", + state = BackgroundTaskState.QUEUED, + progress = null, + progressText = null, + checkpoint = null, + attempt = 0, + workId = null, + createdAt = now, + updatedAt = now, + ) + dao.insert(task) + return enqueue(context, task.id) + } + + suspend fun enqueueAutomaticBackup( + context: Context, + title: String, + description: String, + directoryUri: String?, + ): UUID? { + val dao = BackgroundTaskDatabase.get(context).tasks() + if (dao.getActiveByType(LOCAL_BACKUP_TASK_TYPE) != null) { + return null + } + + val filename = "lnreader_backup_${System.currentTimeMillis()}.zip" + val destination = if (directoryUri != null) { + val treeUri = Uri.parse(directoryUri) + val documentId = DocumentsContract.getTreeDocumentId(treeUri) + val parentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId) + DocumentsContract.createDocument( + context.contentResolver, + parentUri, + "application/zip", + filename, + )?.toString() ?: throw IllegalStateException( + "Could not create a backup in the selected directory", + ) + } else { + val backupDirectory = context.getExternalFilesDir(null)?.resolve("Backups") + ?: throw IllegalStateException("External files directory is unavailable") + if (!backupDirectory.exists() && !backupDirectory.mkdirs()) { + throw IllegalStateException("Could not create automatic backup directory") + } + backupDirectory.resolve(filename).absolutePath + } + val escapedDestination = destination + .replace("\\", "\\\\") + .replace("\"", "\\\"") + val now = System.currentTimeMillis() + val task = BackgroundTaskEntity( + id = UUID.randomUUID().toString(), + type = LOCAL_BACKUP_TASK_TYPE, + payload = """{"name":"$LOCAL_BACKUP_TASK_TYPE","data":{"destinationUri":"$escapedDestination","automatic":true}}""", + title = title, + description = description, + queueName = "lnreader-background-task:task:$LOCAL_BACKUP_TASK_TYPE", + state = BackgroundTaskState.QUEUED, + progress = null, + progressText = null, + checkpoint = null, + attempt = 0, + workId = null, + createdAt = now, + updatedAt = now, + ) + dao.insert(task) + return enqueue(context, task.id) + } + + private val NETWORK_TASKS = setOf( + LIBRARY_UPDATE_TASK_TYPE, + "DRIVE_BACKUP", + "DRIVE_RESTORE", + "SELF_HOST_BACKUP", + "SELF_HOST_RESTORE", + "MIGRATE_NOVEL", + "DOWNLOAD_CHAPTER", + ) + + private const val LIBRARY_UPDATE_TASK_TYPE = "UPDATE_LIBRARY" + private const val LOCAL_BACKUP_TASK_TYPE = "LOCAL_BACKUP" +} diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LNReaderHeadlessTaskService.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LNReaderHeadlessTaskService.kt new file mode 100644 index 000000000..a80d88790 --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LNReaderHeadlessTaskService.kt @@ -0,0 +1,18 @@ +package expo.modules.nativebackgroundtasks + +import android.content.Intent +import com.facebook.react.HeadlessJsTaskService +import com.facebook.react.bridge.Arguments +import com.facebook.react.jstasks.HeadlessJsTaskConfig + +class LNReaderHeadlessTaskService : HeadlessJsTaskService() { + override fun getTaskConfig(intent: Intent?): HeadlessJsTaskConfig? { + val extras = intent?.extras ?: return null + return HeadlessJsTaskConfig( + "LNReaderBackgroundTask", + Arguments.fromBundle(extras), + 0, + true, + ) + } +} \ No newline at end of file diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LNReaderTaskWorker.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LNReaderTaskWorker.kt new file mode 100644 index 000000000..5f5dd4898 --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LNReaderTaskWorker.kt @@ -0,0 +1,109 @@ +package expo.modules.nativebackgroundtasks + +import android.content.Context +import android.content.Intent +import android.content.pm.ServiceInfo +import android.os.Build +import androidx.work.CoroutineWorker +import androidx.work.ForegroundInfo +import androidx.work.WorkerParameters +import kotlinx.coroutines.CancellationException + +class LNReaderTaskWorker( + appContext: Context, + workerParams: WorkerParameters, +) : CoroutineWorker(appContext, workerParams) { + override suspend fun doWork(): Result { + val taskId = inputData.getString(BackgroundTaskScheduler.TASK_ID) ?: return Result.failure() + val dao = BackgroundTaskDatabase.get(applicationContext).tasks() + val task = dao.get(taskId) ?: return Result.failure() + if (task.state == BackgroundTaskState.CANCELLED || task.state == BackgroundTaskState.PAUSED) { + return Result.success() + } + + val claimed = dao.tryMarkRunning( + taskId, + BackgroundTaskState.RUNNING, + System.currentTimeMillis(), + MAX_CONCURRENT_DOWNLOADS, + ) + if (claimed == 0) { + return if (dao.get(taskId)?.state == BackgroundTaskState.QUEUED) { + Result.retry() + } else { + Result.success() + } + } + val runningTask = dao.get(taskId) ?: return Result.failure() + setForeground(createForegroundInfo(runningTask)) + val execution = TaskExecutionRegistry.register(taskId) + + applicationContext.startService( + Intent(applicationContext, LNReaderHeadlessTaskService::class.java).apply { + putExtra("taskId", runningTask.id) + putExtra("type", runningTask.type) + putExtra("payload", runningTask.payload) + runningTask.checkpoint?.let { putExtra("checkpoint", it) } + }, + ) + + return try { + when (val executionResult = execution.await()) { + TaskExecutionResult.Success -> { + val currentState = dao.get(taskId)?.state + if (currentState == BackgroundTaskState.CANCELLED) { + TaskNotificationFactory.dismiss(applicationContext, taskId) + return Result.success() + } + if (currentState == BackgroundTaskState.PAUSED) { + dao.get(taskId)?.let { TaskNotificationFactory.update(applicationContext, it) } + return Result.success() + } + dao.finishRunning(taskId, BackgroundTaskState.SUCCEEDED, System.currentTimeMillis()) + dao.get(taskId)?.let { + TaskNotificationFactory.postTerminal(applicationContext, it) + } + Result.success() + } + is TaskExecutionResult.Failure -> { + val currentState = dao.get(taskId)?.state + if (currentState == BackgroundTaskState.PAUSED || currentState == BackgroundTaskState.CANCELLED) { + return Result.success() + } + if (executionResult.shouldRetry) { + dao.updateState(taskId, BackgroundTaskState.QUEUED, System.currentTimeMillis()) + Result.retry() + } else { + dao.updateState(taskId, BackgroundTaskState.FAILED, System.currentTimeMillis()) + dao.get(taskId)?.let { + TaskNotificationFactory.postTerminal(applicationContext, it) + } + Result.success() + } + } + } + } catch (error: CancellationException) { + val latestState = dao.get(taskId)?.state + if (latestState !in listOf(BackgroundTaskState.PAUSED, BackgroundTaskState.CANCELLED)) { + dao.updateState(taskId, BackgroundTaskState.QUEUED, System.currentTimeMillis()) + } + throw error + } finally { + TaskExecutionRegistry.cancel(taskId) + } + } + + private fun createForegroundInfo(task: BackgroundTaskEntity): ForegroundInfo { + val id = TaskNotificationFactory.notificationId(task.id) + val notification = TaskNotificationFactory.build(applicationContext, task) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ForegroundInfo(id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC) + } else { + ForegroundInfo(id, notification) + } + } + + companion object { + private const val MAX_CONCURRENT_DOWNLOADS = 3 + } +} diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LibraryUpdateScheduleWorker.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LibraryUpdateScheduleWorker.kt new file mode 100644 index 000000000..b7f1c216b --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LibraryUpdateScheduleWorker.kt @@ -0,0 +1,28 @@ +package expo.modules.nativebackgroundtasks + +import android.content.Context +import androidx.work.CoroutineWorker +import androidx.work.WorkerParameters + +class LibraryUpdateScheduleWorker( + appContext: Context, + workerParams: WorkerParameters, +) : CoroutineWorker(appContext, workerParams) { + override suspend fun doWork(): Result { + val title = inputData.getString(LibraryUpdateScheduler.TITLE) + ?: LibraryUpdateScheduler.DEFAULT_TITLE + val description = inputData.getString(LibraryUpdateScheduler.DESCRIPTION) + ?: LibraryUpdateScheduler.DEFAULT_DESCRIPTION + + return try { + BackgroundTaskScheduler.enqueueLibraryUpdate( + applicationContext, + title, + description, + ) + Result.success() + } catch (_: Exception) { + Result.retry() + } + } +} diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LibraryUpdateScheduler.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LibraryUpdateScheduler.kt new file mode 100644 index 000000000..ed7a8a2cc --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/LibraryUpdateScheduler.kt @@ -0,0 +1,60 @@ +package expo.modules.nativebackgroundtasks + +import android.content.Context +import androidx.work.Constraints +import androidx.work.Data +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.NetworkType +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager +import java.util.concurrent.TimeUnit + +object LibraryUpdateScheduler { + const val TITLE = "title" + const val DESCRIPTION = "description" + const val DEFAULT_TITLE = "Updating Library" + const val DEFAULT_DESCRIPTION = "Preparing" + + private const val WORK_NAME = "lnreader-automatic-library-update" + private val allowedIntervals = setOf(12L, 24L, 48L, 72L, 168L) + + fun schedule( + context: Context, + intervalHours: Long, + title: String, + description: String, + ) { + require(intervalHours in allowedIntervals) { + "Unsupported automatic library update interval: $intervalHours" + } + + val request = + PeriodicWorkRequestBuilder( + intervalHours, + TimeUnit.HOURS, + ) + .setInitialDelay(intervalHours, TimeUnit.HOURS) + .setInputData( + Data.Builder() + .putString(TITLE, title) + .putString(DESCRIPTION, description) + .build(), + ) + .setConstraints( + Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build(), + ) + .build() + + WorkManager.getInstance(context).enqueueUniquePeriodicWork( + WORK_NAME, + ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, + request, + ) + } + + fun cancel(context: Context) { + WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME) + } +} diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/NativeBackgroundTasksModule.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/NativeBackgroundTasksModule.kt new file mode 100644 index 000000000..45d010f9b --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/NativeBackgroundTasksModule.kt @@ -0,0 +1,205 @@ +package expo.modules.nativebackgroundtasks + +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.modules.core.DeviceEventManagerModule +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import java.lang.ref.WeakReference +import java.util.UUID + +class NativeBackgroundTasksModule : Module() { + private val dao by lazy { + BackgroundTaskDatabase.get(appContext.reactContext!!).tasks() + } + + override fun definition() = ModuleDefinition { + Name("NativeBackgroundTasks") + + OnCreate { + appContext.reactContext?.let { reactContextRef = WeakReference(it as ReactApplicationContext) } + } + + OnDestroy { + reactContextRef?.clear() + reactContextRef = null + } + + AsyncFunction("enqueue") { type: String, payload: String, title: String, description: String, allowsDuplicates: Boolean, queueName: String -> + runBlocking(Dispatchers.IO) { + if (!allowsDuplicates) { + dao.getActiveByType(type)?.let { return@runBlocking it.id } + } + val now = System.currentTimeMillis() + val task = BackgroundTaskEntity( + id = UUID.randomUUID().toString(), + type = type, + payload = payload, + title = title, + description = description, + queueName = queueName, + state = BackgroundTaskState.QUEUED, + progress = null, + progressText = null, + checkpoint = null, + attempt = 0, + workId = null, + createdAt = now, + updatedAt = now, + ) + dao.insert(task) + BackgroundTaskScheduler.enqueue(appContext.reactContext!!, task.id) + task.id + } + } + + AsyncFunction("getTasks") { + runBlocking(Dispatchers.IO) { + val result = mutableListOf>() + dao.getAll().forEach { task -> + result.add(mapOf( + "id" to task.id, + "type" to task.type, + "payload" to task.payload, + "title" to task.title, + "description" to task.description, + "state" to task.state, + "progress" to task.progress, + "progressText" to task.progressText, + "checkpoint" to task.checkpoint, + "attempt" to task.attempt, + "createdAt" to task.createdAt.toDouble(), + "updatedAt" to task.updatedAt.toDouble(), + )) + } + result + } + } + + AsyncFunction("pause") { taskId: String -> + runBlocking(Dispatchers.IO) { + requireTask(taskId) + dao.updateState(taskId, BackgroundTaskState.PAUSED, System.currentTimeMillis()) + if (TaskExecutionRegistry.isActive(taskId)) { + emitInterruption(taskId, "pause") + } + dao.get(taskId)?.let { TaskNotificationFactory.update(appContext.reactContext!!, it) } + } + } + + AsyncFunction("resume") { taskId: String -> + runBlocking(Dispatchers.IO) { + requireTask(taskId) + if (TaskExecutionRegistry.isActive(taskId)) { + throw IllegalStateException("Task is still pausing; try resuming again shortly") + } + dao.updateState(taskId, BackgroundTaskState.QUEUED, System.currentTimeMillis()) + BackgroundTaskScheduler.enqueue(appContext.reactContext!!, taskId) + } + } + + AsyncFunction("cancel") { taskId: String -> + runBlocking(Dispatchers.IO) { + requireTask(taskId) + dao.updateState(taskId, BackgroundTaskState.CANCELLED, System.currentTimeMillis()) + if (TaskExecutionRegistry.isActive(taskId)) { + emitInterruption(taskId, "cancel") + } + BackgroundTaskScheduler.cancel(appContext.reactContext!!, taskId) + dao.updateCheckpoint(taskId, null, System.currentTimeMillis()) + TaskNotificationFactory.dismiss(appContext.reactContext!!, taskId) + } + } + + AsyncFunction("updateProgress") { taskId: String, progress: Double, progressText: String -> + runBlocking(Dispatchers.IO) { + dao.updateProgress( + taskId, + progress.takeUnless { it < 0 }, + progressText.ifEmpty { null }, + System.currentTimeMillis(), + ) + dao.get(taskId)?.let { TaskNotificationFactory.update(appContext.reactContext!!, it) } + } + } + + AsyncFunction("updateCheckpoint") { taskId: String, checkpoint: String -> + runBlocking(Dispatchers.IO) { + requireTask(taskId) + dao.updateCheckpoint(taskId, checkpoint, System.currentTimeMillis()) + } + } + + AsyncFunction("complete") { taskId: String, completionText: String -> + runBlocking(Dispatchers.IO) { + val now = System.currentTimeMillis() + dao.updateCheckpoint(taskId, null, now) + dao.updateProgress(taskId, null, completionText, now) + dao.finishRunning(taskId, BackgroundTaskState.SUCCEEDED, now) + TaskExecutionRegistry.complete(taskId, TaskExecutionResult.Success) + } + } + + AsyncFunction("fail") { taskId: String, error: String, shouldRetry: Boolean -> + runBlocking(Dispatchers.IO) { + val currentState = dao.get(taskId)?.state + if (currentState !in listOf(BackgroundTaskState.PAUSED, BackgroundTaskState.CANCELLED)) { + dao.updateProgress(taskId, null, error, System.currentTimeMillis()) + dao.finishRunning( + taskId, + if (shouldRetry) BackgroundTaskState.QUEUED else BackgroundTaskState.FAILED, + System.currentTimeMillis(), + ) + } + TaskExecutionRegistry.complete(taskId, TaskExecutionResult.Failure(error, shouldRetry)) + } + } + + AsyncFunction("scheduleLibraryUpdates") { intervalHours: Long, title: String, description: String -> + LibraryUpdateScheduler.schedule( + appContext.reactContext!!, + intervalHours, + title, + description, + ) + } + + AsyncFunction("cancelLibraryUpdates") { + LibraryUpdateScheduler.cancel(appContext.reactContext!!) + } + + AsyncFunction("scheduleAutomaticBackups") { intervalHours: Long, title: String, description: String, directoryUri: String -> + AutomaticBackupScheduler.schedule( + appContext.reactContext!!, + intervalHours, + title, + description, + directoryUri.ifEmpty { null }, + ) + } + + AsyncFunction("cancelAutomaticBackups") { + AutomaticBackupScheduler.cancel(appContext.reactContext!!) + } + } + + private suspend fun requireTask(taskId: String): BackgroundTaskEntity = + dao.get(taskId) ?: throw IllegalArgumentException("Unknown background task: $taskId") + + companion object { + @Volatile + private var reactContextRef: WeakReference? = null + + fun emitInterruption(taskId: String, action: String) { + reactContextRef?.get()?.let { ctx -> + ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) + ?.emit("LNReaderTaskInterrupted", Arguments.createMap().apply { + putString("taskId", taskId) + putString("action", action) + }) + } + } + } +} diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/TaskActionReceiver.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/TaskActionReceiver.kt new file mode 100644 index 000000000..3770ab896 --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/TaskActionReceiver.kt @@ -0,0 +1,45 @@ +package expo.modules.nativebackgroundtasks + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class TaskActionReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val taskId = intent.getStringExtra(TaskNotificationFactory.EXTRA_TASK_ID) ?: return + val pendingResult = goAsync() + CoroutineScope(Dispatchers.IO).launch { + try { + val dao = BackgroundTaskDatabase.get(context).tasks() + dao.get(taskId) ?: return@launch + when (intent.action) { + TaskNotificationFactory.ACTION_PAUSE -> { + dao.updateState(taskId, BackgroundTaskState.PAUSED, System.currentTimeMillis()) + if (TaskExecutionRegistry.isActive(taskId)) { + NativeBackgroundTasksModule.emitInterruption(taskId, "pause") + } + dao.get(taskId)?.let { TaskNotificationFactory.update(context, it) } + } + TaskNotificationFactory.ACTION_RESUME -> { + if (TaskExecutionRegistry.isActive(taskId)) return@launch + dao.updateState(taskId, BackgroundTaskState.QUEUED, System.currentTimeMillis()) + BackgroundTaskScheduler.enqueue(context, taskId) + } + TaskNotificationFactory.ACTION_CANCEL -> { + dao.updateState(taskId, BackgroundTaskState.CANCELLED, System.currentTimeMillis()) + if (TaskExecutionRegistry.isActive(taskId)) { + NativeBackgroundTasksModule.emitInterruption(taskId, "cancel") + } + BackgroundTaskScheduler.cancel(context, taskId) + TaskNotificationFactory.dismiss(context, taskId) + } + } + } finally { + pendingResult.finish() + } + } + } +} diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/TaskExecutionRegistry.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/TaskExecutionRegistry.kt new file mode 100644 index 000000000..e57bae6b0 --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/TaskExecutionRegistry.kt @@ -0,0 +1,29 @@ +package expo.modules.nativebackgroundtasks + +import kotlinx.coroutines.CompletableDeferred +import java.util.concurrent.ConcurrentHashMap + +sealed interface TaskExecutionResult { + data object Success : TaskExecutionResult + data class Failure(val error: String, val shouldRetry: Boolean) : TaskExecutionResult +} + +object TaskExecutionRegistry { + private val executions = ConcurrentHashMap>() + + fun register(taskId: String): CompletableDeferred { + val execution = CompletableDeferred() + executions.put(taskId, execution)?.cancel() + return execution + } + + fun complete(taskId: String, result: TaskExecutionResult) { + executions.remove(taskId)?.complete(result) + } + + fun cancel(taskId: String) { + executions.remove(taskId)?.cancel() + } + + fun isActive(taskId: String): Boolean = executions.containsKey(taskId) +} \ No newline at end of file diff --git a/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/TaskNotificationFactory.kt b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/TaskNotificationFactory.kt new file mode 100644 index 000000000..25d83ea4f --- /dev/null +++ b/modules/native-background-tasks/android/src/main/java/expo/modules/nativebackgroundtasks/TaskNotificationFactory.kt @@ -0,0 +1,154 @@ +package expo.modules.nativebackgroundtasks + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.os.Build +import androidx.core.app.NotificationCompat + +object TaskNotificationFactory { + const val CHANNEL_ID = "lnreader_background_tasks" + const val ACTION_PAUSE = "expo.modules.nativebackgroundtasks.PAUSE" + const val ACTION_RESUME = "expo.modules.nativebackgroundtasks.RESUME" + const val ACTION_CANCEL = "expo.modules.nativebackgroundtasks.CANCEL" + const val EXTRA_TASK_ID = "taskId" + private const val TERMINAL_NOTIFICATION_MASK = 0x40000000 + + fun ensureChannel(context: Context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return + val manager = context.getSystemService(NotificationManager::class.java) + manager.createNotificationChannel( + NotificationChannel( + CHANNEL_ID, + "Background tasks", + NotificationManager.IMPORTANCE_LOW, + ).apply { + description = "Downloads, imports, exports, backups, and library updates" + setSound(null, null) + }, + ) + } + + fun notificationId(taskId: String): Int = taskId.hashCode().and(Int.MAX_VALUE).coerceAtLeast(1) + + fun terminalNotificationId(taskId: String): Int = + notificationId(taskId).xor(TERMINAL_NOTIFICATION_MASK).coerceAtLeast(1) + + fun build(context: Context, task: BackgroundTaskEntity): Notification { + ensureChannel(context) + val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName) + val contentIntent = launchIntent?.let { + PendingIntent.getActivity( + context, + notificationId(task.id), + it, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, + ) + } + + val toggleAction = if (task.state == BackgroundTaskState.PAUSED) ACTION_RESUME else ACTION_PAUSE + val toggleLabel = if (task.state == BackgroundTaskState.PAUSED) "Resume" else "Pause" + + val notificationIconId = context.resources.getIdentifier( + "notification_icon", "drawable", context.packageName + ) + val icon = if (notificationIconId != 0) notificationIconId else android.R.drawable.ic_dialog_info + + val progressLines = task.progressText + ?.lineSequence() + ?.filter { it.isNotBlank() } + ?.toList() + .orEmpty() + val contentText = progressLines.firstOrNull() ?: task.description + + val builder = NotificationCompat.Builder(context, CHANNEL_ID) + .setSmallIcon(icon) + .setContentTitle(task.title) + .setContentText(contentText) + .setContentIntent(contentIntent) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setCategory( + if (task.state in listOf(BackgroundTaskState.SUCCEEDED, BackgroundTaskState.FAILED)) { + NotificationCompat.CATEGORY_STATUS + } else { + NotificationCompat.CATEGORY_PROGRESS + }, + ) + .setOngoing(task.state == BackgroundTaskState.RUNNING || task.state == BackgroundTaskState.QUEUED) + .setAutoCancel(task.state == BackgroundTaskState.SUCCEEDED || task.state == BackgroundTaskState.FAILED) + .setOnlyAlertOnce(true) + + if (task.state in listOf(BackgroundTaskState.QUEUED, BackgroundTaskState.RUNNING, BackgroundTaskState.PAUSED)) { + builder.addAction(0, toggleLabel, actionIntent(context, task.id, toggleAction, 1)) + .addAction(0, "Cancel", actionIntent(context, task.id, ACTION_CANCEL, 2)) + } + + val progress = task.progress + if (task.state == BackgroundTaskState.SUCCEEDED) { + builder.setContentText(contentText).setProgress(0, 0, false) + } else if (task.state == BackgroundTaskState.FAILED) { + builder.setContentText(contentText).setProgress(0, 0, false) + } else if (progress == null) { + builder.setProgress(100, 0, true) + } else { + builder.setProgress(100, (progress.coerceIn(0.0, 1.0) * 100).toInt(), false) + } + + if ( + task.state in listOf(BackgroundTaskState.SUCCEEDED, BackgroundTaskState.FAILED) && + contentText.isNotBlank() + ) { + builder.setStyle(NotificationCompat.BigTextStyle().bigText(contentText)) + } + + if ( + task.type == "UPDATE_LIBRARY" && + task.state in listOf(BackgroundTaskState.QUEUED, BackgroundTaskState.RUNNING, BackgroundTaskState.PAUSED) && + progressLines.size > 1 + ) { + builder.setStyle( + NotificationCompat.InboxStyle().also { style -> + progressLines.forEach(style::addLine) + }, + ) + } + + return builder.build() + } + + fun update(context: Context, task: BackgroundTaskEntity) { + context.getSystemService(NotificationManager::class.java) + .notify(notificationId(task.id), build(context, task)) + } + + fun postTerminal(context: Context, task: BackgroundTaskEntity) { + require(task.state in listOf(BackgroundTaskState.SUCCEEDED, BackgroundTaskState.FAILED)) + context.getSystemService(NotificationManager::class.java) + .notify(terminalNotificationId(task.id), build(context, task)) + } + + fun dismiss(context: Context, taskId: String) { + context.getSystemService(NotificationManager::class.java).apply { + cancel(notificationId(taskId)) + cancel(terminalNotificationId(taskId)) + } + } + + private fun actionIntent( + context: Context, + taskId: String, + action: String, + requestOffset: Int, + ): PendingIntent = PendingIntent.getBroadcast( + context, + notificationId(taskId) + requestOffset, + Intent(context, TaskActionReceiver::class.java).apply { + this.action = action + putExtra(EXTRA_TASK_ID, taskId) + }, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, + ) +} diff --git a/modules/native-background-tasks/expo-module.config.json b/modules/native-background-tasks/expo-module.config.json new file mode 100644 index 000000000..63a898f7e --- /dev/null +++ b/modules/native-background-tasks/expo-module.config.json @@ -0,0 +1,9 @@ +{ + "platforms": ["apple", "android"], + "apple": { + "modules": ["NativeBackgroundTasksModule"] + }, + "android": { + "modules": ["expo.modules.nativebackgroundtasks.NativeBackgroundTasksModule"] + } +} \ No newline at end of file diff --git a/modules/native-background-tasks/index.ts b/modules/native-background-tasks/index.ts new file mode 100644 index 000000000..bff18a689 --- /dev/null +++ b/modules/native-background-tasks/index.ts @@ -0,0 +1,5 @@ +import NativeBackgroundTasks, { + NativeBackgroundTaskRecord, +} from './src/NativeBackgroundTasksModule'; +export default NativeBackgroundTasks; +export { NativeBackgroundTaskRecord }; diff --git a/modules/native-background-tasks/package.json b/modules/native-background-tasks/package.json new file mode 100644 index 000000000..5e37991ff --- /dev/null +++ b/modules/native-background-tasks/package.json @@ -0,0 +1 @@ +{"name": "native-background-tasks"} \ No newline at end of file diff --git a/modules/native-background-tasks/src/NativeBackgroundTasksModule.ts b/modules/native-background-tasks/src/NativeBackgroundTasksModule.ts new file mode 100644 index 000000000..17660d63e --- /dev/null +++ b/modules/native-background-tasks/src/NativeBackgroundTasksModule.ts @@ -0,0 +1,56 @@ +import { requireNativeModule } from 'expo-modules-core'; + +export type NativeBackgroundTaskRecord = { + id: string; + type: string; + payload: string; + title: string; + description?: string; + state: string; + progress?: number; + progressText?: string; + checkpoint?: string; + attempt: number; + createdAt: number; + updatedAt: number; +}; + +type NativeBackgroundTasksModule = { + enqueue( + type: string, + payload: string, + title: string, + description: string, + allowsDuplicates: boolean, + queueName: string, + ): Promise; + getTasks(): Promise; + pause(taskId: string): Promise; + resume(taskId: string): Promise; + cancel(taskId: string): Promise; + updateProgress( + taskId: string, + progress: number, + progressText: string, + ): Promise; + updateCheckpoint(taskId: string, checkpoint: string): Promise; + complete(taskId: string, completionText: string): Promise; + fail(taskId: string, error: string, shouldRetry: boolean): Promise; + scheduleLibraryUpdates( + intervalHours: number, + title: string, + description: string, + ): Promise; + cancelLibraryUpdates(): Promise; + scheduleAutomaticBackups( + intervalHours: number, + title: string, + description: string, + directoryUri: string, + ): Promise; + cancelAutomaticBackups(): Promise; +}; + +export default requireNativeModule( + 'NativeBackgroundTasks', +); diff --git a/modules/native-doh/android/build.gradle b/modules/native-doh/android/build.gradle new file mode 100644 index 000000000..a88c59655 --- /dev/null +++ b/modules/native-doh/android/build.gradle @@ -0,0 +1,20 @@ +plugins { + id 'com.android.library' + id 'expo-module-gradle-plugin' +} + +group = 'expo.modules.nativedoh' +version = '0.1.0' + +android { + namespace "expo.modules.nativedoh" + defaultConfig { + versionCode 1 + versionName "0.1.0" + } +} + +dependencies { + implementation 'com.facebook.react:react-android' + implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.2' +} diff --git a/modules/native-doh/android/src/main/AndroidManifest.xml b/modules/native-doh/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..0c85249a7 --- /dev/null +++ b/modules/native-doh/android/src/main/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/modules/native-doh/android/src/main/java/expo/modules/nativedoh/DohInitializationProvider.kt b/modules/native-doh/android/src/main/java/expo/modules/nativedoh/DohInitializationProvider.kt new file mode 100644 index 000000000..d637a2f36 --- /dev/null +++ b/modules/native-doh/android/src/main/java/expo/modules/nativedoh/DohInitializationProvider.kt @@ -0,0 +1,36 @@ +package expo.modules.nativedoh + +import android.content.ContentProvider +import android.content.ContentValues +import android.database.Cursor +import android.net.Uri + +class DohInitializationProvider : ContentProvider() { + override fun onCreate(): Boolean { + context?.let { + installDohClientFactory(it, DohPreferences.getProvider(it)) + } + return true + } + + override fun query( + uri: Uri, + projection: Array?, + selection: String?, + selectionArgs: Array?, + sortOrder: String?, + ): Cursor? = null + + override fun getType(uri: Uri): String? = null + + override fun insert(uri: Uri, values: ContentValues?): Uri? = null + + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int = 0 + + override fun update( + uri: Uri, + values: ContentValues?, + selection: String?, + selectionArgs: Array?, + ): Int = 0 +} diff --git a/modules/native-doh/android/src/main/java/expo/modules/nativedoh/DohPreferences.kt b/modules/native-doh/android/src/main/java/expo/modules/nativedoh/DohPreferences.kt new file mode 100644 index 000000000..16976fbea --- /dev/null +++ b/modules/native-doh/android/src/main/java/expo/modules/nativedoh/DohPreferences.kt @@ -0,0 +1,20 @@ +package expo.modules.nativedoh + +import android.content.Context + +object DohPreferences { + private const val PREFERENCES_NAME = "lnreader_network" + private const val PROVIDER_KEY = "doh_provider" + + fun getProvider(context: Context): Int = + context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) + .getInt(PROVIDER_KEY, DohProviders.DISABLED) + + fun setProvider(context: Context, provider: Int) { + require(provider in DohProviders.ALL) { "Unknown DNS over HTTPS provider: $provider" } + context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) + .edit() + .putInt(PROVIDER_KEY, provider) + .commit() + } +} diff --git a/modules/native-doh/android/src/main/java/expo/modules/nativedoh/DohProviders.kt b/modules/native-doh/android/src/main/java/expo/modules/nativedoh/DohProviders.kt new file mode 100644 index 000000000..c58aadee0 --- /dev/null +++ b/modules/native-doh/android/src/main/java/expo/modules/nativedoh/DohProviders.kt @@ -0,0 +1,129 @@ +package expo.modules.nativedoh + +import com.facebook.react.modules.network.OkHttpClientProvider +import java.net.InetAddress +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.dnsoverhttps.DnsOverHttps + +object DohProviders { + const val DISABLED = 0 + const val CLOUDFLARE = 1 + const val GOOGLE = 2 + const val ADGUARD = 3 + const val QUAD9 = 4 + const val ALIDNS = 5 + const val DNSPOD = 6 + const val DNS_360 = 7 + const val QUAD_101 = 8 + const val MULLVAD = 9 + const val CONTROL_D = 10 + const val NJALLA = 11 + const val SHECAN = 12 + + val ALL = DISABLED..SHECAN +} + +data class DohProvider( + val url: String, + val bootstrapHosts: List, +) + +private val providers = mapOf( + DohProviders.CLOUDFLARE to DohProvider( + "https://cloudflare-dns.com/dns-query", + listOf( + "162.159.36.1", + "162.159.46.1", + "1.1.1.1", + "1.0.0.1", + "162.159.132.53", + "2606:4700:4700::1111", + "2606:4700:4700::1001", + "2606:4700:4700::0064", + "2606:4700:4700::6400", + ), + ), + DohProviders.GOOGLE to DohProvider( + "https://dns.google/dns-query", + listOf( + "8.8.4.4", + "8.8.8.8", + "2001:4860:4860::8888", + "2001:4860:4860::8844", + ), + ), + DohProviders.ADGUARD to DohProvider( + "https://dns-unfiltered.adguard.com/dns-query", + listOf( + "94.140.14.140", + "94.140.14.141", + "2a10:50c0::1:ff", + "2a10:50c0::2:ff", + ), + ), + DohProviders.QUAD9 to DohProvider( + "https://dns.quad9.net/dns-query", + listOf("9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9"), + ), + DohProviders.ALIDNS to DohProvider( + "https://dns.alidns.com/dns-query", + listOf("223.5.5.5", "223.6.6.6", "2400:3200::1", "2400:3200:baba::1"), + ), + DohProviders.DNSPOD to DohProvider( + "https://doh.pub/dns-query", + listOf("1.12.12.12", "120.53.53.53"), + ), + DohProviders.DNS_360 to DohProvider( + "https://doh.360.cn/dns-query", + listOf( + "101.226.4.6", + "218.30.118.6", + "123.125.81.6", + "140.207.198.6", + "180.163.249.75", + "101.199.113.208", + "36.99.170.86", + ), + ), + DohProviders.QUAD_101 to DohProvider( + "https://dns.twnic.tw/dns-query", + listOf("101.101.101.101", "2001:de4::101", "2001:de4::102"), + ), + DohProviders.MULLVAD to DohProvider( + "https://dns.mullvad.net/dns-query", + listOf("194.242.2.2", "2a07:e340::2"), + ), + DohProviders.CONTROL_D to DohProvider( + "https://freedns.controld.com/p0", + listOf("76.76.2.0", "76.76.10.0", "2606:1a40::", "2606:1a40:1::"), + ), + DohProviders.NJALLA to DohProvider( + "https://dns.njal.la/dns-query", + listOf("95.215.19.53", "2001:67c:2354:2::53"), + ), + DohProviders.SHECAN to DohProvider( + "https://free.shecan.ir/dns-query", + listOf("178.22.122.100", "185.51.200.2"), + ), +) + +fun installDohClientFactory(context: android.content.Context, providerId: Int) { + val provider = providers[providerId] ?: return + val applicationContext = context.applicationContext + + OkHttpClientProvider.setOkHttpClientFactory { + val builder = OkHttpClientProvider.createClientBuilder(applicationContext) + builder.dns( + DnsOverHttps.Builder() + .client(builder.build()) + .url(provider.url.toHttpUrl()) + .bootstrapDnsHosts( + *provider.bootstrapHosts + .map(InetAddress::getByName) + .toTypedArray(), + ) + .build(), + ) + builder.build() + } +} diff --git a/modules/native-doh/android/src/main/java/expo/modules/nativedoh/NativeDohModule.kt b/modules/native-doh/android/src/main/java/expo/modules/nativedoh/NativeDohModule.kt new file mode 100644 index 000000000..9ff42b129 --- /dev/null +++ b/modules/native-doh/android/src/main/java/expo/modules/nativedoh/NativeDohModule.kt @@ -0,0 +1,18 @@ +package expo.modules.nativedoh + +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition + +class NativeDohModule : Module() { + override fun definition() = ModuleDefinition { + Name("NativeDoh") + + Function("getProvider") { + DohPreferences.getProvider(appContext.reactContext!!) + } + + Function("setProvider") { provider: Int -> + DohPreferences.setProvider(appContext.reactContext!!, provider) + } + } +} diff --git a/modules/native-doh/expo-module.config.json b/modules/native-doh/expo-module.config.json new file mode 100644 index 000000000..202d041ee --- /dev/null +++ b/modules/native-doh/expo-module.config.json @@ -0,0 +1,6 @@ +{ + "platforms": ["android"], + "android": { + "modules": ["expo.modules.nativedoh.NativeDohModule"] + } +} diff --git a/modules/native-doh/index.ts b/modules/native-doh/index.ts new file mode 100644 index 000000000..3c6706d85 --- /dev/null +++ b/modules/native-doh/index.ts @@ -0,0 +1,23 @@ +import { requireOptionalNativeModule } from 'expo-modules-core'; + +export type DohProviderId = + | 0 + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12; + +type NativeDohModule = { + getProvider(): DohProviderId; + setProvider(provider: DohProviderId): void; +}; + +export default requireOptionalNativeModule('NativeDoh'); diff --git a/modules/native-doh/package.json b/modules/native-doh/package.json new file mode 100644 index 000000000..6030afe5e --- /dev/null +++ b/modules/native-doh/package.json @@ -0,0 +1,3 @@ +{ + "name": "native-doh" +} diff --git a/modules/native-file/android/build.gradle b/modules/native-file/android/build.gradle new file mode 100644 index 000000000..4aa57b5c6 --- /dev/null +++ b/modules/native-file/android/build.gradle @@ -0,0 +1,24 @@ +plugins { + id 'com.android.library' + id 'expo-module-gradle-plugin' +} + +group = 'expo.modules.nativefile' +version = '0.1.0' + +android { + namespace "expo.modules.nativefile" + defaultConfig { + versionCode 1 + versionName "0.1.0" + } + lintOptions { + abortOnError false + } +} + +dependencies { + implementation 'com.facebook.react:react-android' + implementation 'com.squareup.okhttp3:okhttp:4.12.0' + implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.12.0' +} diff --git a/modules/native-file/android/src/main/java/expo/modules/nativefile/NativeFileModule.kt b/modules/native-file/android/src/main/java/expo/modules/nativefile/NativeFileModule.kt new file mode 100644 index 000000000..3a957a4d0 --- /dev/null +++ b/modules/native-file/android/src/main/java/expo/modules/nativefile/NativeFileModule.kt @@ -0,0 +1,645 @@ +package expo.modules.nativefile + +import android.app.Activity +import android.content.ContentResolver +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.provider.DocumentsContract +import com.facebook.react.bridge.BaseActivityEventListener +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.modules.network.CookieJarContainer +import com.facebook.react.modules.network.ForwardingCookieHandler +import com.facebook.react.modules.network.OkHttpClientProvider +import expo.modules.kotlin.Promise +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.ensureActive +import kotlinx.coroutines.launch +import okhttp3.Call +import okhttp3.Callback +import okhttp3.Headers +import okhttp3.JavaNetCookieJar +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import java.io.File +import java.io.FileOutputStream +import java.io.FileWriter +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.util.UUID +import java.io.PushbackInputStream +import java.util.zip.GZIPInputStream +import kotlin.coroutines.coroutineContext + +class NativeFileModule : Module() { + private val BUFFER_SIZE = 4096 + private val okHttpClient = OkHttpClientProvider.createClient() + private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + private var pendingDocumentPromise: Promise? = null + + private val reactContext: ReactApplicationContext? + get() = appContext.reactContext as? ReactApplicationContext + + private val activityEventListener = object : BaseActivityEventListener() { + override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) { + if ( + requestCode != CREATE_DOCUMENT_REQUEST && + requestCode != PICK_DOCUMENT_REQUEST && + requestCode != PICK_DIRECTORY_REQUEST + ) return + val promise = pendingDocumentPromise ?: return + pendingDocumentPromise = null + val uri = data?.data + if (resultCode != Activity.RESULT_OK || uri == null) { + promise.reject("ECANCELLED", "Document selection was cancelled", null) + return + } + try { + val flags = data.flags and + (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + reactContext?.contentResolver?.takePersistableUriPermission(uri, flags) + } catch (_: SecurityException) { + // Some providers do not support persisted grants. + } + if (requestCode == PICK_DIRECTORY_REQUEST) { + val documentId = try { + DocumentsContract.getTreeDocumentId(uri) + } catch (_: IllegalArgumentException) { + uri.lastPathSegment.orEmpty() + } + promise.resolve( + mapOf( + "uri" to uri.toString(), + "name" to documentId + .substringAfterLast(':') + .substringAfterLast('/') + .ifEmpty { "Selected folder" }, + ), + ) + } else { + promise.resolve(uri.toString()) + } + } + } + + private fun getFileUri(filepath: String): Uri { + var uri = Uri.parse(filepath) + if (uri.scheme == null) { + val file = File(filepath) + if (file.isDirectory) { + throw Exception("Invalid file, folder found!") + } + uri = Uri.parse("file://$filepath") + } + return uri + } + + private fun getInputStream(filepath: String): InputStream { + val uri = getFileUri(filepath) + return reactContext?.contentResolver?.openInputStream(uri) + ?: throw Exception("ENOENT: could not open an input stream for '$filepath'") + } + + private val writeAccessByAPILevel: String + get() = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) "w" else "rwt" + + private fun getOutputStream(filepath: String): OutputStream { + val uri = getFileUri(filepath) + return reactContext?.contentResolver?.openOutputStream(uri, writeAccessByAPILevel) + ?: throw Exception("ENOENT: could not open an output stream for '$filepath'") + } + + private suspend fun copyFileContent( + filepath: String, + destPath: String, + onDone: (() -> Unit)? = null, + ): Long { + try { + val inputStream = getInputStream(filepath) + var copiedBytes = 0L + try { + val outputStream = getOutputStream(destPath) + try { + val buffer = ByteArray(BUFFER_SIZE) + var length: Int + while (inputStream.read(buffer).also { length = it } > 0) { + coroutineContext.ensureActive() + outputStream.write(buffer, 0, length) + copiedBytes += length + } + outputStream.flush() + } finally { + outputStream.close() + } + } finally { + inputStream.close() + } + if (onDone != null) { + onDone() + } + return copiedBytes + } catch (e: IOException) { + throw Exception("Failed to copy file from '$filepath' to '$destPath': ${e.message}") + } + } + + private fun contentResolver(): ContentResolver = + reactContext?.contentResolver + ?: throw IOException("React context is unavailable") + + private suspend fun copyToOutputStream(sourcePath: String, outputStream: OutputStream): Long { + var copiedBytes = 0L + getInputStream(sourcePath).use { inputStream -> + outputStream.use { output -> + val buffer = ByteArray(BUFFER_SIZE) + var length: Int + while (inputStream.read(buffer).also { length = it } > 0) { + coroutineContext.ensureActive() + output.write(buffer, 0, length) + copiedBytes += length + } + output.flush() + } + } + return copiedBytes + } + + private fun resolveDirectoryFile(directoryUri: String): File { + val uri = Uri.parse(directoryUri) + val directory = if (uri.scheme == null) { + File(directoryUri) + } else if (uri.scheme == ContentResolver.SCHEME_FILE) { + File(uri.path ?: throw IOException("Invalid directory URI: '$directoryUri'")) + } else { + throw IOException("Unsupported filesystem directory URI: '$directoryUri'") + } + if (!directory.isDirectory) { + throw IOException("Destination directory does not exist: '$directoryUri'") + } + return directory + } + + private suspend fun copyFileToFilesystemDirectory( + sourcePath: String, + directoryUri: String, + fileName: String, + replace: Boolean, + ): Map { + val directory = resolveDirectoryFile(directoryUri) + val destination = File(directory, fileName) + if (destination.exists() && !replace) { + throw IOException("File already exists: ${destination.absolutePath}") + } + + val staging = File(directory, ".$fileName.${UUID.randomUUID()}.tmp") + val backup = File(directory, ".$fileName.${UUID.randomUUID()}.bak") + try { + val copiedBytes = FileOutputStream(staging).use { output -> + copyToOutputStream(sourcePath, output) + } + if (staging.length() != copiedBytes) { + throw IOException("Copied file size does not match the source stream") + } + + val hadDestination = destination.exists() + if (hadDestination && !destination.renameTo(backup)) { + throw IOException("Could not stage the existing destination for replacement") + } + if (!staging.renameTo(destination)) { + if (hadDestination) { + backup.renameTo(destination) + } + throw IOException("Could not move the completed copy into the destination") + } + if (backup.exists() && !backup.delete()) { + throw IOException("Could not remove the replaced destination backup") + } + return mapOf("uri" to destination.absolutePath, "size" to copiedBytes) + } finally { + staging.delete() + if (backup.exists() && !destination.exists()) { + backup.renameTo(destination) + } + } + } + + private fun resolveTreeDirectoryUri(treeUri: Uri): Uri { + if (!DocumentsContract.isTreeUri(treeUri)) { + throw IOException("Destination is not a Storage Access Framework directory: '$treeUri'") + } + val documentId = DocumentsContract.getTreeDocumentId(treeUri) + return DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId) + } + + private fun findChildDocument(treeUri: Uri, directoryUri: Uri, fileName: String): Uri? { + val resolver = contentResolver() + val documentId = DocumentsContract.getDocumentId(directoryUri) + val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(treeUri, documentId) + val projection = arrayOf( + DocumentsContract.Document.COLUMN_DOCUMENT_ID, + DocumentsContract.Document.COLUMN_DISPLAY_NAME, + ) + resolver.query(childrenUri, projection, null, null, null)?.use { cursor -> + while (cursor.moveToNext()) { + if (cursor.getString(1) == fileName) { + return DocumentsContract.buildDocumentUriUsingTree(treeUri, cursor.getString(0)) + } + } + } + return null + } + + private fun renameDocument(documentUri: Uri, displayName: String): Uri = + DocumentsContract.renameDocument(contentResolver(), documentUri, displayName) + ?: throw IOException("Could not rename destination document to '$displayName'") + + private suspend fun copyFileToSafDirectory( + sourcePath: String, + directoryUriString: String, + fileName: String, + mimeType: String, + replace: Boolean, + ): Map { + val resolver = contentResolver() + val treeUri = Uri.parse(directoryUriString) + val directoryUri = resolveTreeDirectoryUri(treeUri) + val existing = findChildDocument(treeUri, directoryUri, fileName) + if (existing != null && !replace) { + throw IOException("File already exists: $fileName") + } + + val token = UUID.randomUUID().toString() + val stagingName = "$fileName.lnreader-$token.tmp" + val backupName = "$fileName.lnreader-$token.bak" + var stagingUri = DocumentsContract.createDocument( + resolver, + directoryUri, + mimeType, + stagingName, + ) ?: throw IOException("Could not create a temporary destination document") + var backupUri: Uri? = null + var completedUri: Uri? = null + + try { + val copiedBytes = copyToOutputStream( + sourcePath, + resolver.openOutputStream(stagingUri, writeAccessByAPILevel) + ?: throw IOException("Could not open the temporary destination document"), + ) + + if (existing != null) { + backupUri = renameDocument(existing, backupName) + } + try { + completedUri = renameDocument(stagingUri, fileName) + stagingUri = completedUri + } catch (error: Exception) { + backupUri?.let { + try { + renameDocument(it, fileName) + backupUri = null + } catch (_: Exception) { + // Keep the original replacement error. + } + } + throw error + } + + backupUri?.let { + try { + DocumentsContract.deleteDocument(resolver, it) + } catch (_: Exception) { + // The completed destination is already in place. + } + backupUri = null + } + return mapOf("uri" to completedUri.toString(), "size" to copiedBytes) + } finally { + if (completedUri == null) { + try { + DocumentsContract.deleteDocument(resolver, stagingUri) + } catch (_: Exception) { + // Preserve the original copy or replacement error. + } + } + if (completedUri == null) backupUri?.let { + try { + renameDocument(it, fileName) + } catch (_: Exception) { + // Preserve the original copy or replacement error. + } + } + } + } + + private suspend fun deleteRecursive(fileOrDirectory: File) { + coroutineContext.ensureActive() + if (fileOrDirectory.isDirectory) { + for (child in fileOrDirectory.listFiles().orEmpty()) { + deleteRecursive(child) + } + } + if (!fileOrDirectory.delete() && fileOrDirectory.exists()) { + throw IOException("Failed to delete '${fileOrDirectory.absolutePath}'") + } + } + + private fun decompressStream(input: InputStream?): InputStream { + val pb = PushbackInputStream(input, 2) + val signature = ByteArray(2) + val len = pb.read(signature) + if (len == -1) return pb + pb.unread(signature, 0, len) + return if (signature[0] == 0x1f.toByte() && signature[1] == 0x8b.toByte()) + GZIPInputStream(pb) else pb + } + + private fun rejectFileOperation(promise: Promise, operation: String, path: String, error: Exception) { + if (error is CancellationException) { + promise.reject("ECANCELLED", "File operation was cancelled", error) + return + } + val code = if (error is SecurityException) "EACCES" else "EIO" + promise.reject(code, "Failed to $operation '$path': ${error.message}", error) + } + + override fun definition() = ModuleDefinition { + Name("NativeFile") + + OnCreate { + val ctx = reactContext ?: return@OnCreate + val cookieContainer = okHttpClient.cookieJar as CookieJarContainer + val cookieHandler = ForwardingCookieHandler(ctx) + cookieContainer.setCookieJar(JavaNetCookieJar(cookieHandler)) + ctx.addActivityEventListener(activityEventListener) + } + + OnDestroy { + reactContext?.removeActivityEventListener(activityEventListener) + pendingDocumentPromise?.reject("ECANCELLED", "Native file module invalidated", null) + pendingDocumentPromise = null + coroutineScope.cancel() + } + + AsyncFunction("createDocument") { filename: String, mimeType: String, promise: Promise -> + launchDocumentIntent( + Intent(Intent.ACTION_CREATE_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = mimeType + putExtra(Intent.EXTRA_TITLE, filename) + }, + CREATE_DOCUMENT_REQUEST, + promise, + ) + } + + AsyncFunction("pickDocument") { mimeType: String, promise: Promise -> + launchDocumentIntent( + Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = mimeType + }, + PICK_DOCUMENT_REQUEST, + promise, + ) + } + + AsyncFunction("pickDirectory") { promise: Promise -> + launchDocumentIntent( + Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), + PICK_DIRECTORY_REQUEST, + promise, + ) + } + + AsyncFunction("writeFile") { path: String, content: String, promise: Promise -> + coroutineScope.launch { + try { + FileWriter(path).use { it.write(content) } + promise.resolve(null) + } catch (e: Exception) { + rejectFileOperation(promise, "write", path, e) + } + } + } + + AsyncFunction("readFile") { path: String, promise: Promise -> + coroutineScope.launch { + try { + val file = File(path) + if (!file.exists()) { + promise.reject("ENOENT", "File not found: '$path'", null) + return@launch + } + promise.resolve(file.bufferedReader().use { it.readText() }) + } catch (e: Exception) { + rejectFileOperation(promise, "read", path, e) + } + } + } + + AsyncFunction("copyFile") { filepath: String, destPath: String, promise: Promise -> + coroutineScope.launch { + try { + copyFileContent(filepath, destPath) + promise.resolve(null) + } catch (e: Exception) { + rejectFileOperation(promise, "copy", filepath, e) + } + } + } + + AsyncFunction("copyFileToDirectory") { sourcePath: String, directoryUri: String, fileName: String, mimeType: String, replace: Boolean, promise: Promise -> + coroutineScope.launch { + try { + require(fileName.isNotBlank() && fileName != "." && fileName != ".." && !fileName.contains('/') && !fileName.contains('\\')) { + "Invalid destination file name" + } + val uri = Uri.parse(directoryUri) + val result = if (uri.scheme == ContentResolver.SCHEME_CONTENT) { + copyFileToSafDirectory(sourcePath, directoryUri, fileName, mimeType, replace) + } else { + copyFileToFilesystemDirectory(sourcePath, directoryUri, fileName, replace) + } + promise.resolve(result) + } catch (e: Exception) { + rejectFileOperation(promise, "copy into directory", directoryUri, e) + } + } + } + + AsyncFunction("moveFile") { filepath: String, destPath: String, promise: Promise -> + coroutineScope.launch { + try { + val inFile = File(filepath) + copyFileContent(filepath, destPath) { + if (!inFile.delete()) { + throw IOException("Failed to delete source file '$filepath'") + } + } + promise.resolve(null) + } catch (e: Exception) { + rejectFileOperation(promise, "move", filepath, e) + } + } + } + + AsyncFunction("exists") { filepath: String, promise: Promise -> + coroutineScope.launch { + try { + promise.resolve(File(filepath).exists()) + } catch (e: Exception) { + rejectFileOperation(promise, "inspect", filepath, e) + } + } + } + + AsyncFunction("mkdir") { filepath: String, promise: Promise -> + coroutineScope.launch { + try { + val file = File(filepath) + if (file.exists() && !file.isDirectory) { + throw IOException("A file already exists at the directory path") + } + if (!file.exists() && !file.mkdirs()) { + throw IOException("Directory could not be created") + } + promise.resolve(null) + } catch (e: Exception) { + rejectFileOperation(promise, "create directory", filepath, e) + } + } + } + + AsyncFunction("unlink") { filepath: String, promise: Promise -> + coroutineScope.launch { + try { + val file = File(filepath) + if (file.exists()) { + deleteRecursive(file) + } + promise.resolve(null) + } catch (e: Exception) { + rejectFileOperation(promise, "delete", filepath, e) + } + } + } + + AsyncFunction("readDir") { directory: String, promise: Promise -> + coroutineScope.launch { + try { + val file = File(directory) + if (!file.exists()) { + promise.reject("ENOENT", "Folder does not exist: '$directory'", null) + return@launch + } + val result = file.listFiles().orEmpty().map { childFile -> + mapOf( + "name" to childFile.name, + "path" to childFile.absolutePath, + "isDirectory" to childFile.isDirectory + ) + } + promise.resolve(result) + } catch (e: Exception) { + rejectFileOperation(promise, "list", directory, e) + } + } + } + + AsyncFunction("downloadFile") { url: String, destPath: String, method: String, headers: Map, body: String?, promise: Promise -> + coroutineScope.launch { + try { + val headersBuilder = Headers.Builder() + headers.forEach { (key, value) -> headersBuilder.add(key, value) } + val requestBuilder = Request.Builder() + .url(url) + .headers(headersBuilder.build()) + if (method.lowercase() == "get") { + requestBuilder.get() + } else if (body != null) { + requestBuilder.post(body.toRequestBody()) + } + + okHttpClient.newCall(requestBuilder.build()) + .enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + promise.reject("DOWNLOAD_FAILED", e.message ?: "Download failed", e) + } + + override fun onResponse(call: Call, response: Response) { + response.use { + if (!it.isSuccessful || it.body == null) { + promise.reject("DOWNLOAD_FAILED", "Failed to download: ${it.code}", Exception("HTTP ${it.code}")) + return + } + try { + decompressStream(it.body!!.byteStream()).use { inputStream -> + FileOutputStream(destPath).use { fos -> + inputStream.copyTo(fos, BUFFER_SIZE) + } + } + promise.resolve(null) + } catch (e: Exception) { + promise.reject("DOWNLOAD_FAILED", e.message ?: "Download error", e) + } + } + } + }) + } catch (e: Exception) { + promise.reject("DOWNLOAD_FAILED", e.message ?: "Download error", e) + } + } + } + + Constant("DocumentDirectoryPath") { + val context = reactContext ?: appContext.currentActivity + context?.filesDir?.absolutePath.orEmpty() + } + + Constant("ExternalDirectoryPath") { + val context = reactContext ?: appContext.currentActivity + val directory = context?.getExternalFilesDir(null) ?: context?.filesDir + directory?.absolutePath.orEmpty() + } + + Constant("ExternalCachesDirectoryPath") { + val context = reactContext ?: appContext.currentActivity + val directory = context?.externalCacheDir ?: context?.cacheDir + directory?.absolutePath.orEmpty() + } + } + + private fun launchDocumentIntent(intent: Intent, requestCode: Int, promise: Promise) { + val activity = appContext.currentActivity + if (activity == null) { + promise.reject("ENOACTIVITY", "A visible activity is required to select a document", null) + return + } + if (pendingDocumentPromise != null) { + promise.reject("EBUSY", "Another document selection is already active", null) + return + } + pendingDocumentPromise = promise + intent.addFlags( + Intent.FLAG_GRANT_READ_URI_PERMISSION or + Intent.FLAG_GRANT_WRITE_URI_PERMISSION or + Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, + ) + activity.startActivityForResult(intent, requestCode) + } + + companion object { + private const val CREATE_DOCUMENT_REQUEST = 48120 + private const val PICK_DOCUMENT_REQUEST = 48121 + private const val PICK_DIRECTORY_REQUEST = 48122 + } +} diff --git a/modules/native-file/expo-module.config.json b/modules/native-file/expo-module.config.json new file mode 100644 index 000000000..04764053f --- /dev/null +++ b/modules/native-file/expo-module.config.json @@ -0,0 +1,9 @@ +{ + "platforms": ["apple", "android"], + "apple": { + "modules": ["NativeFileModule"] + }, + "android": { + "modules": ["expo.modules.nativefile.NativeFileModule"] + } +} diff --git a/modules/native-file/index.ts b/modules/native-file/index.ts new file mode 100644 index 000000000..d1aa6dc6c --- /dev/null +++ b/modules/native-file/index.ts @@ -0,0 +1,2 @@ +import NativeFile from './src/NativeFileModule'; +export default NativeFile; diff --git a/modules/native-file/ios/NativeFile.podspec b/modules/native-file/ios/NativeFile.podspec new file mode 100644 index 000000000..c888bac71 --- /dev/null +++ b/modules/native-file/ios/NativeFile.podspec @@ -0,0 +1,16 @@ +Pod::Spec.new do |s| + s.name = 'NativeFile' + s.version = '1.0.0' + s.summary = 'NativeFile module' + s.description = 'NativeFile module' + s.license = 'MIT' + s.author = '' + s.homepage = 'https://github.com/LNReader/lnreader' + s.platforms = { :ios => '15.5', :tvos => '15.5' } + s.source = { git: '' } + s.static_framework = true + + s.dependency 'ExpoModulesCore' + + s.source_files = "**/*.{h,m,swift}" +end diff --git a/modules/native-file/ios/NativeFileModule.swift b/modules/native-file/ios/NativeFileModule.swift new file mode 100644 index 000000000..e82f63528 --- /dev/null +++ b/modules/native-file/ios/NativeFileModule.swift @@ -0,0 +1,119 @@ +import ExpoModulesCore +import Foundation + +public class NativeFileModule: Module { + public func definition() -> ModuleDefinition { + Name("NativeFile") + + Function("writeFile") { (path: String, content: String) in + try content.write(toFile: path, atomically: true, encoding: .utf8) + } + + Function("readFile") { (path: String) in + try String(contentsOfFile: path, encoding: .utf8) + } + + Function("copyFile") { (sourcePath: String, destPath: String) in + try FileManager.default.copyItem(atPath: sourcePath, toPath: destPath) + } + + AsyncFunction("copyFileToDirectory") { (sourcePath: String, directoryUri: String, fileName: String, mimeType: String, replace: Bool) -> [String: Any] in + _ = mimeType + guard !fileName.isEmpty, + fileName != ".", + fileName != "..", + !fileName.contains("/"), + !fileName.contains("\\") else { + throw NSError( + domain: "NativeFile", + code: 1, + userInfo: [NSLocalizedDescriptionKey: "Invalid destination file name"] + ) + } + + let fileManager = FileManager.default + let directoryURL: URL + if let parsedURL = URL(string: directoryUri), parsedURL.isFileURL { + directoryURL = parsedURL + } else { + directoryURL = URL(fileURLWithPath: directoryUri, isDirectory: true) + } + let sourceURL = URL(fileURLWithPath: sourcePath) + let destinationURL = directoryURL.appendingPathComponent(fileName) + let stagingURL = directoryURL.appendingPathComponent(".\(fileName).\(UUID().uuidString).tmp") + + guard fileManager.fileExists(atPath: directoryURL.path) else { + throw NSError( + domain: "NativeFile", + code: 2, + userInfo: [NSLocalizedDescriptionKey: "Destination directory does not exist"] + ) + } + if fileManager.fileExists(atPath: destinationURL.path) && !replace { + throw CocoaError(.fileWriteFileExists) + } + + defer { try? fileManager.removeItem(at: stagingURL) } + try fileManager.copyItem(at: sourceURL, to: stagingURL) + let attributes = try fileManager.attributesOfItem(atPath: stagingURL.path) + let copiedSize = (attributes[.size] as? NSNumber)?.int64Value ?? 0 + + if fileManager.fileExists(atPath: destinationURL.path) { + _ = try fileManager.replaceItemAt(destinationURL, withItemAt: stagingURL) + } else { + try fileManager.moveItem(at: stagingURL, to: destinationURL) + } + return ["uri": destinationURL.absoluteString, "size": copiedSize] + } + + Function("moveFile") { (sourcePath: String, destPath: String) in + try FileManager.default.moveItem(atPath: sourcePath, toPath: destPath) + } + + Function("exists") { (filePath: String) in + FileManager.default.fileExists(atPath: filePath) + } + + Function("mkdir") { (filePath: String) in + try FileManager.default.createDirectory(atPath: filePath, withIntermediateDirectories: true, attributes: nil) + } + + Function("unlink") { (filePath: String) in + try FileManager.default.removeItem(atPath: filePath) + } + + Function("readDir") { (dirPath: String) -> [[String: Any]] in + let contents = try FileManager.default.contentsOfDirectory(atPath: dirPath) + return contents.map { fileName in + let path = (dirPath as NSString).appendingPathComponent(fileName) + var isDirectory: ObjCBool = false + FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) + return [ + "name": fileName, + "path": path, + "isDirectory": isDirectory.boolValue + ] + } + } + + AsyncFunction("downloadFile") { (url: String, destPath: String, method: String, headers: [String: String], body: String?, promise: Promise) in + // Stub — download implementation not ported for iOS + promise.reject("NOT_IMPLEMENTED", "downloadFile is not implemented on iOS") + } + + Constant("DocumentDirectoryPath") { + let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) + return paths.first ?? "" + } + + Constant("ExternalDirectoryPath") { + let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) + return paths.first ?? "" + } + + Constant("ExternalCachesDirectoryPath") { + let paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true) + return paths.first ?? "" + } + } +} diff --git a/modules/native-file/package.json b/modules/native-file/package.json new file mode 100644 index 000000000..f9ac4674e --- /dev/null +++ b/modules/native-file/package.json @@ -0,0 +1 @@ +{"name": "native-file"} \ No newline at end of file diff --git a/modules/native-file/src/NativeFileModule.ts b/modules/native-file/src/NativeFileModule.ts new file mode 100644 index 000000000..0f97716d3 --- /dev/null +++ b/modules/native-file/src/NativeFileModule.ts @@ -0,0 +1,50 @@ +import { requireNativeModule } from 'expo-modules-core'; + +export type ReadDirResult = { + name: string; + path: string; + isDirectory: boolean; +}; + +export type DirectorySelection = { + uri: string; + name: string; +}; + +export type FileCopyResult = { + uri: string; + size: number; +}; + +type NativeFileModule = { + DocumentDirectoryPath: string; + ExternalDirectoryPath: string; + ExternalCachesDirectoryPath: string; + createDocument(filename: string, mimeType: string): Promise; + pickDocument(mimeType: string): Promise; + pickDirectory(): Promise; + writeFile(path: string, content: string): Promise; + readFile(path: string): Promise; + copyFile(filepath: string, destPath: string): Promise; + copyFileToDirectory( + sourcePath: string, + directoryUri: string, + fileName: string, + mimeType: string, + replace: boolean, + ): Promise; + moveFile(filepath: string, destPath: string): Promise; + exists(filepath: string): Promise; + mkdir(filepath: string): Promise; + unlink(filepath: string): Promise; + readDir(directory: string): Promise; + downloadFile( + url: string, + destPath: string, + method: string, + headers: Record, + body?: string, + ): Promise; +}; + +export default requireNativeModule('NativeFile'); diff --git a/modules/native-volume-button-listener/android/build.gradle b/modules/native-volume-button-listener/android/build.gradle new file mode 100644 index 000000000..bb376f421 --- /dev/null +++ b/modules/native-volume-button-listener/android/build.gradle @@ -0,0 +1,18 @@ +plugins { + id 'com.android.library' + id 'expo-module-gradle-plugin' +} + +group = 'expo.modules.nativevolumebuttonlistener' +version = '0.1.0' + +android { + namespace "expo.modules.nativevolumebuttonlistener" + defaultConfig { + versionCode 1 + versionName "0.1.0" + } + lintOptions { + abortOnError false + } +} diff --git a/modules/native-volume-button-listener/android/src/main/java/expo/modules/nativevolumebuttonlistener/NativeVolumeButtonListenerModule.kt b/modules/native-volume-button-listener/android/src/main/java/expo/modules/nativevolumebuttonlistener/NativeVolumeButtonListenerModule.kt new file mode 100644 index 000000000..3a2864623 --- /dev/null +++ b/modules/native-volume-button-listener/android/src/main/java/expo/modules/nativevolumebuttonlistener/NativeVolumeButtonListenerModule.kt @@ -0,0 +1,82 @@ +package expo.modules.nativevolumebuttonlistener + +import android.view.KeyEvent +import android.view.Window +import expo.modules.kotlin.AppContext +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition + +class NativeVolumeButtonListenerModule : Module() { + override fun definition() = ModuleDefinition { + Name("NativeVolumeButtonListener") + + Events("VolumeUp", "VolumeDown") + + Function("setActive") { active: Boolean -> + isActive = active + } + + OnCreate { + Companion.module = this@NativeVolumeButtonListenerModule + } + + OnActivityEntersForeground { + setupKeyInterceptor(appContext) + } + + OnDestroy { + Companion.cleanup() + } + } + + companion object { + var module: NativeVolumeButtonListenerModule? = null + var isActive = false + private var callbackAttached = false + private var originalCallback: Window.Callback? = null + + fun sendEvent(up: Boolean) { + if (!isActive) return + val eventName = if (up) "VolumeUp" else "VolumeDown" + module?.sendEvent(eventName, mapOf()) + } + + private fun setupKeyInterceptor(appContext: AppContext) { + if (callbackAttached) return + val activity = appContext.currentActivity ?: return + val window = activity.window ?: return + originalCallback = window.callback + window.callback = object : Window.Callback by (originalCallback ?: window.callback) { + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + if (isActive && event.action == KeyEvent.ACTION_DOWN) { + when (event.keyCode) { + KeyEvent.KEYCODE_VOLUME_UP -> { + sendEvent(true) + return true + } + KeyEvent.KEYCODE_VOLUME_DOWN -> { + sendEvent(false) + return true + } + } + } + return originalCallback?.dispatchKeyEvent(event) ?: false + } + } + callbackAttached = true + } + + private fun cleanup() { + val activity = module?.appContext?.currentActivity + if (callbackAttached && activity != null) { + val window = activity.window + if (window != null && originalCallback != null) { + window.callback = originalCallback + } + } + module = null + callbackAttached = false + originalCallback = null + } + } +} diff --git a/modules/native-volume-button-listener/expo-module.config.json b/modules/native-volume-button-listener/expo-module.config.json new file mode 100644 index 000000000..26deeb960 --- /dev/null +++ b/modules/native-volume-button-listener/expo-module.config.json @@ -0,0 +1,9 @@ +{ + "platforms": ["apple", "android"], + "apple": { + "modules": ["NativeVolumeButtonListenerModule"] + }, + "android": { + "modules": ["expo.modules.nativevolumebuttonlistener.NativeVolumeButtonListenerModule"] + } +} diff --git a/modules/native-volume-button-listener/index.ts b/modules/native-volume-button-listener/index.ts new file mode 100644 index 000000000..6565fe44c --- /dev/null +++ b/modules/native-volume-button-listener/index.ts @@ -0,0 +1,2 @@ +import NativeVolumeButtonListener from './src/NativeVolumeButtonListenerModule'; +export default NativeVolumeButtonListener; diff --git a/modules/native-volume-button-listener/ios/NativeVolumeButtonListener.podspec b/modules/native-volume-button-listener/ios/NativeVolumeButtonListener.podspec new file mode 100644 index 000000000..145a53acd --- /dev/null +++ b/modules/native-volume-button-listener/ios/NativeVolumeButtonListener.podspec @@ -0,0 +1,16 @@ +Pod::Spec.new do |s| + s.name = 'NativeVolumeButtonListener' + s.version = '1.0.0' + s.summary = 'NativeVolumeButtonListener module' + s.description = 'NativeVolumeButtonListener module' + s.license = 'MIT' + s.author = '' + s.homepage = 'https://github.com/LNReader/lnreader' + s.platforms = { :ios => '15.5', :tvos => '15.5' } + s.source = { git: '' } + s.static_framework = true + + s.dependency 'ExpoModulesCore' + + s.source_files = "**/*.{h,m,swift}" +end diff --git a/modules/native-volume-button-listener/ios/NativeVolumeButtonListenerModule.swift b/modules/native-volume-button-listener/ios/NativeVolumeButtonListenerModule.swift new file mode 100644 index 000000000..e670e3e9a --- /dev/null +++ b/modules/native-volume-button-listener/ios/NativeVolumeButtonListenerModule.swift @@ -0,0 +1,10 @@ +import ExpoModulesCore +import Foundation + +public class NativeVolumeButtonListenerModule: Module { + public func definition() -> ModuleDefinition { + Name("NativeVolumeButtonListener") + + Events("VolumeUp", "VolumeDown") + } +} diff --git a/modules/native-volume-button-listener/package.json b/modules/native-volume-button-listener/package.json new file mode 100644 index 000000000..e42dacc1e --- /dev/null +++ b/modules/native-volume-button-listener/package.json @@ -0,0 +1 @@ +{"name": "native-volume-button-listener"} \ No newline at end of file diff --git a/modules/native-volume-button-listener/src/NativeVolumeButtonListenerModule.ts b/modules/native-volume-button-listener/src/NativeVolumeButtonListenerModule.ts new file mode 100644 index 000000000..5d972585e --- /dev/null +++ b/modules/native-volume-button-listener/src/NativeVolumeButtonListenerModule.ts @@ -0,0 +1,14 @@ +import { NativeModule, requireNativeModule } from 'expo-modules-core'; + +type NativeVolumeButtonListenerEvents = { + VolumeUp: () => void; + VolumeDown: () => void; +}; + +declare class NativeVolumeButtonListenerModule extends NativeModule { + setActive(active: boolean): void; +} + +export default requireNativeModule( + 'NativeVolumeButtonListener', +); diff --git a/modules/native-zip-archive/android/build.gradle b/modules/native-zip-archive/android/build.gradle new file mode 100644 index 000000000..88f8fff1b --- /dev/null +++ b/modules/native-zip-archive/android/build.gradle @@ -0,0 +1,18 @@ +plugins { + id 'com.android.library' + id 'expo-module-gradle-plugin' +} + +group = 'expo.modules.nativeziparchive' +version = '0.1.0' + +android { + namespace "expo.modules.nativeziparchive" + defaultConfig { + versionCode 1 + versionName "0.1.0" + } + lintOptions { + abortOnError false + } +} diff --git a/modules/native-zip-archive/android/src/main/java/expo/modules/nativeziparchive/NativeZipArchiveModule.kt b/modules/native-zip-archive/android/src/main/java/expo/modules/nativeziparchive/NativeZipArchiveModule.kt new file mode 100644 index 000000000..2ab5ab6f8 --- /dev/null +++ b/modules/native-zip-archive/android/src/main/java/expo/modules/nativeziparchive/NativeZipArchiveModule.kt @@ -0,0 +1,124 @@ +package expo.modules.nativeziparchive + +import expo.modules.kotlin.Promise +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition +import java.io.File +import java.io.FileOutputStream +import java.net.HttpURLConnection +import java.net.URL +import java.util.zip.ZipEntry +import java.util.zip.ZipFile +import java.util.zip.ZipInputStream +import java.util.zip.ZipOutputStream + +class NativeZipArchiveModule : Module() { + private val BUFFER_SIZE = 4096 + + private fun zipProcess(sourceDirPath: String, zos: ZipOutputStream) { + val sourceDir = File(sourceDirPath) + sourceDir.walkBottomUp().filter { it.isFile }.forEach { file -> + val zipFileName = + file.absolutePath.removePrefix(sourceDir.absolutePath).removePrefix("/") + val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}") + zos.putNextEntry(entry) + file.inputStream().use { fis -> + fis.copyTo(zos, BUFFER_SIZE) + fis.close() + } + Thread.yield() + } + } + + override fun definition() = ModuleDefinition { + Name("NativeZipArchive") + + AsyncFunction("unzip") { sourceFilePath: String, distDirPath: String, promise: Promise -> + Thread { + try { + ZipFile(sourceFilePath).use { zis -> + zis.entries().asSequence().filterNot { it.isDirectory }.forEach { zipEntry -> + val newFile = File(distDirPath, zipEntry.name) + newFile.parentFile?.mkdirs() + zis.getInputStream(zipEntry).use { inputStream -> + FileOutputStream(newFile).use { fos -> inputStream.copyTo(fos, BUFFER_SIZE) } + } + Thread.yield() + } + } + promise.resolve(null) + } catch (e: Exception) { + promise.reject("UNZIP_FAILED", e.message ?: "Unzip failed", e) + } + }.start() + } + + AsyncFunction("zip") { sourceDirPath: String, zipFilePath: String, promise: Promise -> + Thread { + try { + FileOutputStream(zipFilePath).use { fos -> + ZipOutputStream(fos).use { zos -> zipProcess(sourceDirPath, zos) } + } + promise.resolve(null) + } catch (e: Exception) { + promise.reject("ZIP_FAILED", e.message ?: "Zip failed", e) + } + }.start() + } + + AsyncFunction("remoteUnzip") { distDirPath: String, urlString: String, headers: Map, promise: Promise -> + Thread { + val connection = URL(urlString).openConnection() as HttpURLConnection + try { + connection.requestMethod = "GET" + headers.forEach { (key, value) -> + connection.setRequestProperty(key, value) + } + ZipInputStream(connection.inputStream).use { zis -> + generateSequence { zis.nextEntry } + .filterNot { it.isDirectory } + .forEach { zipEntry -> + val newFile = File(distDirPath, zipEntry.name) + newFile.parentFile?.mkdirs() + FileOutputStream(newFile).use { fos -> zis.copyTo(fos, BUFFER_SIZE) } + Thread.yield() + } + } + if (connection.responseCode == 200) { + promise.resolve(null) + } else { + throw Exception("Network request failed") + } + } catch (e: Exception) { + promise.reject("REMOTE_UNZIP_FAILED", e.message ?: "Remote unzip failed", e) + } finally { + connection.disconnect() + } + }.start() + } + + AsyncFunction("remoteZip") { sourceDirPath: String, urlString: String, headers: Map, promise: Promise -> + Thread { + val connection = URL(urlString).openConnection() as HttpURLConnection + try { + connection.requestMethod = "POST" + headers.forEach { (key, value) -> + connection.setRequestProperty(key, value) + } + ZipOutputStream(connection.outputStream).use { zipProcess(sourceDirPath, it) } + if (connection.responseCode == 200) { + promise.resolve( + connection.inputStream.bufferedReader().use { it.readText() }) + } else { + throw Exception("Network request failed") + } + } catch (e: Exception) { + promise.reject("REMOTE_ZIP_FAILED", e.message ?: "Remote zip failed", e) + } finally { + connection.disconnect() + } + }.start() + } + } + +} diff --git a/modules/native-zip-archive/expo-module.config.json b/modules/native-zip-archive/expo-module.config.json new file mode 100644 index 000000000..a6aa30313 --- /dev/null +++ b/modules/native-zip-archive/expo-module.config.json @@ -0,0 +1,9 @@ +{ + "platforms": ["apple", "android"], + "apple": { + "modules": ["NativeZipArchiveModule"] + }, + "android": { + "modules": ["expo.modules.nativeziparchive.NativeZipArchiveModule"] + } +} diff --git a/modules/native-zip-archive/index.ts b/modules/native-zip-archive/index.ts new file mode 100644 index 000000000..8b6552c44 --- /dev/null +++ b/modules/native-zip-archive/index.ts @@ -0,0 +1,2 @@ +import NativeZipArchive from './src/NativeZipArchiveModule'; +export default NativeZipArchive; diff --git a/modules/native-zip-archive/ios/NativeZipArchive.podspec b/modules/native-zip-archive/ios/NativeZipArchive.podspec new file mode 100644 index 000000000..cbc2e11c1 --- /dev/null +++ b/modules/native-zip-archive/ios/NativeZipArchive.podspec @@ -0,0 +1,16 @@ +Pod::Spec.new do |s| + s.name = 'NativeZipArchive' + s.version = '1.0.0' + s.summary = 'NativeZipArchive module' + s.description = 'NativeZipArchive module' + s.license = 'MIT' + s.author = '' + s.homepage = 'https://github.com/LNReader/lnreader' + s.platforms = { :ios => '15.5', :tvos => '15.5' } + s.source = { git: '' } + s.static_framework = true + + s.dependency 'ExpoModulesCore' + + s.source_files = "**/*.{h,m,swift}" +end diff --git a/modules/native-zip-archive/ios/NativeZipArchiveModule.swift b/modules/native-zip-archive/ios/NativeZipArchiveModule.swift new file mode 100644 index 000000000..7060acd82 --- /dev/null +++ b/modules/native-zip-archive/ios/NativeZipArchiveModule.swift @@ -0,0 +1,24 @@ +import ExpoModulesCore +import Foundation + +public class NativeZipArchiveModule: Module { + public func definition() -> ModuleDefinition { + Name("NativeZipArchive") + + AsyncFunction("unzip") { (sourceFilePath: String, distDirPath: String, promise: Promise) in + promise.reject("NOT_IMPLEMENTED", "unzip is not implemented on iOS") + } + + AsyncFunction("zip") { (sourceDirPath: String, zipFilePath: String, promise: Promise) in + promise.reject("NOT_IMPLEMENTED", "zip is not implemented on iOS") + } + + AsyncFunction("remoteUnzip") { (distDirPath: String, url: String, headers: [String: String], promise: Promise) in + promise.reject("NOT_IMPLEMENTED", "remoteUnzip is not implemented on iOS") + } + + AsyncFunction("remoteZip") { (sourceDirPath: String, url: String, headers: [String: String], promise: Promise) in + promise.reject("NOT_IMPLEMENTED", "remoteZip is not implemented on iOS") + } + } +} diff --git a/modules/native-zip-archive/package.json b/modules/native-zip-archive/package.json new file mode 100644 index 000000000..7b7f0691a --- /dev/null +++ b/modules/native-zip-archive/package.json @@ -0,0 +1 @@ +{"name": "native-zip-archive"} \ No newline at end of file diff --git a/modules/native-zip-archive/src/NativeZipArchiveModule.ts b/modules/native-zip-archive/src/NativeZipArchiveModule.ts new file mode 100644 index 000000000..cd62ad6fb --- /dev/null +++ b/modules/native-zip-archive/src/NativeZipArchiveModule.ts @@ -0,0 +1,18 @@ +import { requireNativeModule } from 'expo-modules-core'; + +type NativeZipArchiveModule = { + unzip(sourceFilePath: string, distDirPath: string): Promise; + zip(sourceDirPath: string, zipFilePath: string): Promise; + remoteUnzip( + distDirPath: string, + urlString: string, + headers: Record, + ): Promise; + remoteZip( + sourceDirPath: string, + urlString: string, + headers: Record, + ): Promise; +}; + +export default requireNativeModule('NativeZipArchive'); diff --git a/modules/nitro-epub/.gitignore b/modules/nitro-epub/.gitignore new file mode 100644 index 000000000..1279851d6 --- /dev/null +++ b/modules/nitro-epub/.gitignore @@ -0,0 +1,83 @@ +# OSX +# +.DS_Store + +# XDE +.expo/ + +# VSCode +.vscode/ +jsconfig.json + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Android/IJ +# +.classpath +.cxx +.gradle +.idea +.project +.settings +local.properties +android.iml + +# Cocoapods +# +example/ios/Pods + +# Ruby +example/vendor/ + +# node.js +# +node_modules/ +npm-debug.log +yarn-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ +android/app/libs +android/keystores/debug.keystore + +# Yarn +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +# Expo +.expo/ + +# Turborepo +.turbo/ + +# generated by bob +lib/ + +# caches +.eslintcache +.cache +*.tsbuildinfo diff --git a/modules/nitro-epub/.watchmanconfig b/modules/nitro-epub/.watchmanconfig new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/modules/nitro-epub/.watchmanconfig @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/modules/nitro-epub/NitroEpub.podspec b/modules/nitro-epub/NitroEpub.podspec new file mode 100644 index 000000000..876c0cf92 --- /dev/null +++ b/modules/nitro-epub/NitroEpub.podspec @@ -0,0 +1,33 @@ +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "package.json"))) + +Pod::Spec.new do |s| + s.name = "NitroEpub" + s.version = package["version"] + s.summary = package["description"] + s.homepage = package["homepage"] + s.license = package["license"] + s.authors = package["author"] + + s.platforms = { :ios => min_ios_version_supported, :visionos => 1.0 } + s.source = { :git => "https://github.com/mrousavy/nitro.git", :tag => "#{s.version}" } + + s.source_files = [ + # Implementation (Swift) + "ios/**/*.{swift}", + # Autolinking/Registration (Objective-C++) + "ios/**/*.{m,mm}", + # Implementation (C++ objects) + "cpp/**/*.{hpp,cpp}", + ] + s.exclude_files = "cpp/tests/**/*" + + load 'nitrogen/generated/ios/NitroEpub+autolinking.rb' + add_nitrogen_files(s) + + s.dependency 'React-jsi' + s.dependency 'React-callinvoker' + s.library = 'z' + install_modules_dependencies(s) +end diff --git a/modules/nitro-epub/README.md b/modules/nitro-epub/README.md new file mode 100644 index 000000000..4a2ff1681 --- /dev/null +++ b/modules/nitro-epub/README.md @@ -0,0 +1,30 @@ +# Nitro EPUB + +Cross-platform C++ EPUB support for LNReader, exposed through React Native Nitro +Modules. + +## Capabilities + +- Parse an extracted EPUB into novel metadata and chapter paths. +- Export downloaded chapter files as an EPUB 3 archive. +- Copy local chapter images into the archive and remove missing images. +- Generate EPUB navigation, NCX compatibility navigation, metadata, CSS, and + optional JavaScript. +- Write the required `mimetype` entry first and without compression. +- Build exports on a Nitro worker thread and atomically publish the completed + archive. +- Report completed chapter counts while an export is running. + +The exporter accepts chapter file paths rather than chapter bodies so large +novels do not need to be retained in the JavaScript heap. + +Import the shared runtime object from the package: + +```ts +import { epub } from 'nitro-epub' + +const novel = await epub.parseNovelAndChapters(extractedDirectory) +``` + +Run `pnpm specs` in this directory after changing `src/specs/Epub.nitro.ts`. +Generated Nitrogen sources are committed. diff --git a/modules/nitro-epub/android/CMakeLists.txt b/modules/nitro-epub/android/CMakeLists.txt new file mode 100644 index 000000000..b0ba51edc --- /dev/null +++ b/modules/nitro-epub/android/CMakeLists.txt @@ -0,0 +1,38 @@ +project(NitroEpub) +cmake_minimum_required(VERSION 3.9.0) + +set (PACKAGE_NAME NitroEpub) +set (CMAKE_VERBOSE_MAKEFILE ON) +set (CMAKE_CXX_STANDARD 20) + +# Define C++ library and add all sources +add_library(${PACKAGE_NAME} SHARED + src/main/cpp/cpp-adapter.cpp + ../cpp/import/EpubParser.cpp + ../cpp/export/EpubContentProcessor.cpp + ../cpp/export/EpubDocuments.cpp + ../cpp/export/EpubExporter.cpp + ../cpp/export/ZipWriter.cpp + ../cpp/pugixml.cpp + ../cpp/HybridEpub.cpp +) + +# Add Nitrogen specs :) +include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroEpub+autolinking.cmake) + +# Set up local includes +include_directories( + "src/main/cpp" + "../cpp" +) + +find_library(LOG_LIB log) +find_library(Z_LIB z) + +# Link all libraries together +target_link_libraries( + ${PACKAGE_NAME} + ${LOG_LIB} + ${Z_LIB} + android # <-- Android core +) diff --git a/modules/nitro-epub/android/build.gradle b/modules/nitro-epub/android/build.gradle new file mode 100644 index 000000000..5d64ef4b5 --- /dev/null +++ b/modules/nitro-epub/android/build.gradle @@ -0,0 +1,141 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath "com.android.tools.build:gradle:9.2.1" + } +} + +def reactNativeArchitectures() { + def value = rootProject.getProperties().get("reactNativeArchitectures") + return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] +} + +def isNewArchitectureEnabled() { + return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" +} + +apply plugin: "com.android.library" +apply plugin: 'org.jetbrains.kotlin.android' +apply from: '../nitrogen/generated/android/NitroEpub+autolinking.gradle' +apply from: "./fix-prefab.gradle" + +if (isNewArchitectureEnabled()) { + apply plugin: "com.facebook.react" +} + +def getExtOrDefault(name) { + return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["NitroEpub_" + name] +} + +def getExtOrIntegerDefault(name) { + return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroEpub_" + name]).toInteger() +} + +android { + namespace "com.margelo.nitro.nitroepub" + + ndkVersion getExtOrDefault("ndkVersion") + compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") + + defaultConfig { + minSdkVersion getExtOrIntegerDefault("minSdkVersion") + targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() + + externalNativeBuild { + cmake { + cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all" + arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON" + abiFilters (*reactNativeArchitectures()) + + buildTypes { + debug { + cppFlags "-O1 -g" + } + release { + cppFlags "-O2" + } + } + } + } + } + + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } + + packagingOptions { + excludes = [ + "META-INF", + "META-INF/**", + "**/libc++_shared.so", + "**/libNitroModules.so", + "**/libfbjni.so", + "**/libjsi.so", + "**/libfolly_json.so", + "**/libfolly_runtime.so", + "**/libglog.so", + "**/libhermes.so", + "**/libhermes-executor-debug.so", + "**/libhermes_executor.so", + "**/libreactnative.so", + "**/libreactnativejni.so", + "**/libturbomodulejsijni.so", + "**/libreact_nativemodule_core.so", + "**/libjscexecutor.so" + ] + } + + buildFeatures { + buildConfig true + prefab true + } + + buildTypes { + release { + minifyEnabled false + } + } + + lintOptions { + disable "GradleCompatible" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + sourceSets { + main { + if (isNewArchitectureEnabled()) { + java.srcDirs += [ + // React Codegen files + "${project.buildDir}/generated/source/codegen/java" + ] + } + } + } +} + +repositories { + mavenCentral() + google() +} + + +dependencies { + // For < 0.71, this will be from the local maven repo + // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin + //noinspection GradleDynamicVersion + implementation "com.facebook.react:react-native:+" + + // Add a dependency on NitroModules + implementation project(":react-native-nitro-modules") +} diff --git a/modules/nitro-epub/android/fix-prefab.gradle b/modules/nitro-epub/android/fix-prefab.gradle new file mode 100644 index 000000000..d6c010ef7 --- /dev/null +++ b/modules/nitro-epub/android/fix-prefab.gradle @@ -0,0 +1,51 @@ +tasks.configureEach { task -> + // Make sure that we generate our prefab publication file only after having built the native library + // so that not a header publication file, but a full configuration publication will be generated, which + // will include the .so file + + def prefabConfigurePattern = ~/^prefab(.+)ConfigurePackage$/ + def matcher = task.name =~ prefabConfigurePattern + if (matcher.matches()) { + def variantName = matcher[0][1] + task.outputs.upToDateWhen { false } + task.dependsOn("externalNativeBuild${variantName}") + } +} + +afterEvaluate { + def abis = reactNativeArchitectures() + rootProject.allprojects.each { proj -> + if (proj === rootProject) return + + def dependsOnThisLib = proj.configurations.findAll { it.canBeResolved }.any { config -> + config.dependencies.any { dep -> + dep.group == project.group && dep.name == project.name + } + } + if (!dependsOnThisLib && proj != project) return + + if (!proj.plugins.hasPlugin('com.android.application') && !proj.plugins.hasPlugin('com.android.library')) { + return + } + + def variants = proj.android.hasProperty('applicationVariants') ? proj.android.applicationVariants : proj.android.libraryVariants + // Touch the prefab_config.json files to ensure that in ExternalNativeJsonGenerator.kt we will re-trigger the prefab CLI to + // generate a libnameConfig.cmake file that will contain our native library (.so). + // See this condition: https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/ExternalNativeJsonGenerator.kt;l=207-219?q=createPrefabBuildSystemGlue + variants.all { variant -> + def variantName = variant.name + abis.each { abi -> + def searchDir = new File(proj.projectDir, ".cxx/${variantName}") + if (!searchDir.exists()) return + def matches = [] + searchDir.eachDir { randomDir -> + def prefabFile = new File(randomDir, "${abi}/prefab_config.json") + if (prefabFile.exists()) matches << prefabFile + } + matches.each { prefabConfig -> + prefabConfig.setLastModified(System.currentTimeMillis()) + } + } + } + } +} diff --git a/modules/nitro-epub/android/gradle.properties b/modules/nitro-epub/android/gradle.properties new file mode 100644 index 000000000..c5eb791cd --- /dev/null +++ b/modules/nitro-epub/android/gradle.properties @@ -0,0 +1,5 @@ +NitroEpub_kotlinVersion=2.1.21 +NitroEpub_minSdkVersion=23 +NitroEpub_targetSdkVersion=36 +NitroEpub_compileSdkVersion=36 +NitroEpub_ndkVersion=29.0.14206865 diff --git a/modules/nitro-epub/android/src/main/AndroidManifest.xml b/modules/nitro-epub/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a2f47b605 --- /dev/null +++ b/modules/nitro-epub/android/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/modules/nitro-epub/android/src/main/cpp/cpp-adapter.cpp b/modules/nitro-epub/android/src/main/cpp/cpp-adapter.cpp new file mode 100644 index 000000000..b3366e7bb --- /dev/null +++ b/modules/nitro-epub/android/src/main/cpp/cpp-adapter.cpp @@ -0,0 +1,9 @@ +#include +#include +#include "NitroEpubOnLoad.hpp" + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { + return facebook::jni::initialize(vm, []() { + margelo::nitro::nitroepub::registerAllNatives(); + }); +} diff --git a/modules/nitro-epub/android/src/main/java/com/margelo/nitro/nitroepub/NitroEpubPackage.kt b/modules/nitro-epub/android/src/main/java/com/margelo/nitro/nitroepub/NitroEpubPackage.kt new file mode 100644 index 000000000..d33d27cb0 --- /dev/null +++ b/modules/nitro-epub/android/src/main/java/com/margelo/nitro/nitroepub/NitroEpubPackage.kt @@ -0,0 +1,18 @@ +package com.margelo.nitro.nitroepub + +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.module.model.ReactModuleInfoProvider +import com.facebook.react.BaseReactPackage + +class NitroEpubPackage : BaseReactPackage() { + override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? = null + + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider { HashMap() } + + companion object { + init { + NitroEpubOnLoad.initializeNative() + } + } +} diff --git a/modules/nitro-epub/babel.config.js b/modules/nitro-epub/babel.config.js new file mode 100644 index 000000000..3e0218e68 --- /dev/null +++ b/modules/nitro-epub/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['module:@react-native/babel-preset'], +} diff --git a/modules/nitro-epub/cpp/HybridEpub.cpp b/modules/nitro-epub/cpp/HybridEpub.cpp new file mode 100644 index 000000000..6cf3a8ab6 --- /dev/null +++ b/modules/nitro-epub/cpp/HybridEpub.cpp @@ -0,0 +1,88 @@ +#include "HybridEpub.hpp" +#include "export/EpubExporter.hpp" +#include "import/EpubParser.hpp" + +namespace margelo::nitro::nitroepub { + +std::shared_ptr> HybridEpub::parseNovelAndChapters( + const std::string& epubDirPath) { + return Promise::async([epubDirPath]() { + EpubMetadata metadata = parseEpub(epubDirPath); + + EpubNovel result; + result.name = metadata.name; + result.cover = metadata.cover.empty() + ? std::nullopt + : std::optional(metadata.cover); + result.summary = metadata.summary.empty() + ? std::nullopt + : std::optional(metadata.summary); + result.author = metadata.author.empty() + ? std::nullopt + : std::optional(metadata.author); + result.artist = metadata.artist.empty() + ? std::nullopt + : std::optional(metadata.artist); + + for (const auto& chapter : metadata.chapters) { + result.chapters.emplace_back(chapter.name, chapter.path); + } + result.cssPaths = metadata.cssPaths; + result.imagePaths = metadata.imagePaths; + + return result; + }); +} + +std::shared_ptr> HybridEpub::exportEpub( + const EpubExportMetadata& metadata, + const std::vector& chapters, + const std::string& outputPath, + const std::function< + std::shared_ptr>>>( + double, double, const std::string&)>& + onProgress) { + return Promise::async( + [metadata, chapters, outputPath, onProgress]() { + EpubArchiveMetadata archiveMetadata{ + metadata.title, + metadata.language, + metadata.coverPath, + metadata.description, + metadata.author, + metadata.bookId, + metadata.stylesheet, + metadata.javascript, + }; + std::vector archiveChapters; + archiveChapters.reserve(chapters.size()); + for (const EpubExportChapter& chapter : chapters) { + archiveChapters.push_back(EpubArchiveChapter{ + chapter.title, + chapter.htmlPath, + chapter.novelId, + chapter.chapterId, + }); + } + const EpubArchiveResult result = + exportEpubArchive( + archiveMetadata, + archiveChapters, + outputPath, + [onProgress](std::size_t completedChapters, + std::size_t totalChapters, + const std::string& chapterTitle) { + const std::shared_ptr> progressPromise = + onProgress(static_cast(completedChapters), + static_cast(totalChapters), + chapterTitle) + ->await() + .get(); + progressPromise->await().get(); + }); + return EpubExportResult( + result.outputPath, static_cast(result.chapterCount)); + }); +} + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/cpp/HybridEpub.hpp b/modules/nitro-epub/cpp/HybridEpub.hpp new file mode 100644 index 000000000..65826fbbb --- /dev/null +++ b/modules/nitro-epub/cpp/HybridEpub.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "HybridEpubSpec.hpp" + +namespace margelo::nitro::nitroepub { + +class HybridEpub final : public HybridEpubSpec { +public: + HybridEpub() : HybridObject(TAG) {} + ~HybridEpub() override = default; + + std::shared_ptr> parseNovelAndChapters( + const std::string& epubDirPath) override; + std::shared_ptr> exportEpub( + const EpubExportMetadata& metadata, + const std::vector& chapters, + const std::string& outputPath, + const std::function< + std::shared_ptr>>>( + double, double, const std::string&)>& + onProgress) override; +}; + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/cpp/export/EpubAsset.hpp b/modules/nitro-epub/cpp/export/EpubAsset.hpp new file mode 100644 index 000000000..cf0b1bb38 --- /dev/null +++ b/modules/nitro-epub/cpp/export/EpubAsset.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace margelo::nitro::nitroepub { + +struct EpubAsset { + std::string id; + std::string archivePath; + std::string mediaType; +}; + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/cpp/export/EpubContentProcessor.cpp b/modules/nitro-epub/cpp/export/EpubContentProcessor.cpp new file mode 100644 index 000000000..68de0eb2b --- /dev/null +++ b/modules/nitro-epub/cpp/export/EpubContentProcessor.cpp @@ -0,0 +1,261 @@ +#include "EpubContentProcessor.hpp" + +#include "../pugixml.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace margelo::nitro::nitroepub { +namespace { + +std::string readTextFile(const std::filesystem::path& path) { + std::ifstream input(path, std::ios::binary); + if (!input) { + throw std::runtime_error("Unable to read downloaded chapter " + + path.string()); + } + return std::string(std::istreambuf_iterator(input), + std::istreambuf_iterator()); +} + +std::string lowercase(std::string value) { + std::transform(value.begin(), value.end(), value.begin(), [](char character) { + return static_cast( + std::tolower(static_cast(character))); + }); + return value; +} + +int hexValue(char character) { + if (character >= '0' && character <= '9') { + return character - '0'; + } + character = static_cast(std::tolower( + static_cast(character))); + if (character >= 'a' && character <= 'f') { + return character - 'a' + 10; + } + return -1; +} + +std::string percentDecode(const std::string& value) { + std::string decoded; + decoded.reserve(value.size()); + for (std::size_t index = 0; index < value.size(); ++index) { + if (value[index] == '%' && index + 2 < value.size()) { + const int high = hexValue(value[index + 1]); + const int low = hexValue(value[index + 2]); + if (high >= 0 && low >= 0) { + decoded += static_cast((high << 4) | low); + index += 2; + continue; + } + } + decoded += value[index]; + } + return decoded; +} + +std::string extractBody(std::string html) { + html = std::regex_replace( + html, std::regex(R"(]*>)", std::regex::icase), ""); + const std::regex bodyPattern( + R"(]*>([\s\S]*?))", + std::regex::icase); + std::smatch match; + if (std::regex_search(html, match, bodyPattern)) { + return match[1].str(); + } + return html; +} + +std::string makeVoidElementsXmlCompatible(std::string html) { + const std::regex voidElement( + R"(<(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)(\b[^<>]*?)(/?)>)", + std::regex::icase); + std::string result; + std::size_t cursor = 0; + for (std::sregex_iterator iterator(html.begin(), html.end(), voidElement), + end; + iterator != end; ++iterator) { + const auto& match = *iterator; + result.append(html, cursor, static_cast(match.position()) - + cursor); + result += "<" + match[1].str() + match[2].str() + " />"; + cursor = static_cast(match.position() + match.length()); + } + result.append(html, cursor, std::string::npos); + return result; +} + +std::string escapeUnsupportedNamedEntities(const std::string& html) { + static const std::unordered_set xmlEntities = { + "amp", "apos", "gt", "lt", "quot"}; + const std::regex entity(R"(&([A-Za-z][A-Za-z0-9]+);)"); + std::string result; + std::size_t cursor = 0; + for (std::sregex_iterator iterator(html.begin(), html.end(), entity), end; + iterator != end; ++iterator) { + const auto& match = *iterator; + result.append(html, cursor, static_cast(match.position()) - + cursor); + const std::string name = match[1].str(); + if (name == "nbsp") { + result += " "; + } else { + result += xmlEntities.contains(name) ? match.str() + : "&" + name + ";"; + } + cursor = static_cast(match.position() + match.length()); + } + result.append(html, cursor, std::string::npos); + return result; +} + +void removeUnsafeContent(pugi::xml_node node) { + for (pugi::xml_node child = node.first_child(); child;) { + pugi::xml_node next = child.next_sibling(); + const std::string name = lowercase(child.name()); + if (name == "script" || name == "iframe" || name == "object" || + name == "embed") { + node.remove_child(child); + } else { + for (pugi::xml_attribute attribute = child.first_attribute(); + attribute;) { + pugi::xml_attribute nextAttribute = attribute.next_attribute(); + const std::string attributeName = lowercase(attribute.name()); + if (attributeName.rfind("on", 0) == 0) { + child.remove_attribute(attribute); + } + attribute = nextAttribute; + } + removeUnsafeContent(child); + } + child = next; + } +} + +std::string serializeChildren(const pugi::xml_node& root) { + std::ostringstream output; + for (const pugi::xml_node& child : root.children()) { + child.print(output, "", pugi::format_raw, pugi::encoding_utf8); + } + return output.str(); +} + +void processImages( + pugi::xml_node node, + ZipWriter& zip, + std::unordered_map& knownAssets, + std::vector& assets) { + for (pugi::xml_node child = node.first_child(); child;) { + pugi::xml_node next = child.next_sibling(); + if (lowercase(child.name()) != "img") { + processImages(child, zip, knownAssets, assets); + child = next; + continue; + } + + pugi::xml_attribute source = child.attribute("src"); + const std::filesystem::path localPath = + source ? localPathFromUri(source.value()) : std::filesystem::path(); + if (localPath.empty() || !std::filesystem::is_regular_file(localPath)) { + node.remove_child(child); + child = next; + continue; + } + + const std::string key = + std::filesystem::weakly_canonical(localPath).string(); + auto assetIterator = knownAssets.find(key); + if (assetIterator == knownAssets.end()) { + const std::string extension = normalizedImageExtension(localPath); + const std::string index = std::to_string(assets.size()); + EpubAsset asset{ + "image-" + index, + "EPUB/images/image-" + index + extension, + imageMediaType(extension), + }; + zip.addFile(asset.archivePath, localPath); + assets.push_back(asset); + assetIterator = knownAssets.emplace(key, std::move(asset)).first; + } + const std::string relativePath = + "../images/" + + std::filesystem::path(assetIterator->second.archivePath) + .filename() + .string(); + source.set_value(relativePath.c_str()); + child.remove_attribute("srcset"); + child = next; + } +} + +} // namespace + +std::filesystem::path localPathFromUri(const std::string& uri) { + constexpr const char* FILE_SCHEME = "file://"; + if (uri.rfind(FILE_SCHEME, 0) == 0) { + return percentDecode(uri.substr(7)); + } + if (!uri.empty() && uri.front() == '/') { + return percentDecode(uri); + } + return {}; +} + +std::string normalizedImageExtension(const std::filesystem::path& path) { + const std::string extension = lowercase(path.extension().string()); + static const std::unordered_set supported = { + ".avif", ".gif", ".jpeg", ".jpg", ".png", ".svg", ".webp"}; + return supported.contains(extension) ? extension : ".png"; +} + +std::string imageMediaType(const std::string& extension) { + if (extension == ".avif") { + return "image/avif"; + } + if (extension == ".gif") { + return "image/gif"; + } + if (extension == ".jpeg" || extension == ".jpg") { + return "image/jpeg"; + } + if (extension == ".svg") { + return "image/svg+xml"; + } + if (extension == ".webp") { + return "image/webp"; + } + return "image/png"; +} + +std::string prepareChapterBody( + const EpubArchiveChapter& chapter, + ZipWriter& zip, + std::unordered_map& knownAssets, + std::vector& assets) { + std::string body = escapeUnsupportedNamedEntities( + makeVoidElementsXmlCompatible(extractBody(readTextFile(chapter.htmlPath)))); + pugi::xml_document document; + const std::string wrapped = "" + body + ""; + const pugi::xml_parse_result parsed = + document.load_string(wrapped.c_str(), pugi::parse_default); + if (!parsed) { + throw std::runtime_error( + "Downloaded chapter is not valid XHTML near byte " + + std::to_string(parsed.offset) + ": " + parsed.description()); + } + + pugi::xml_node root = document.child("root"); + removeUnsafeContent(root); + processImages(root, zip, knownAssets, assets); + return serializeChildren(root); +} + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/cpp/export/EpubContentProcessor.hpp b/modules/nitro-epub/cpp/export/EpubContentProcessor.hpp new file mode 100644 index 000000000..301959741 --- /dev/null +++ b/modules/nitro-epub/cpp/export/EpubContentProcessor.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "EpubAsset.hpp" +#include "EpubExporter.hpp" +#include "ZipWriter.hpp" + +#include +#include +#include +#include + +namespace margelo::nitro::nitroepub { + +std::filesystem::path localPathFromUri(const std::string& uri); +std::string normalizedImageExtension(const std::filesystem::path& path); +std::string imageMediaType(const std::string& extension); +std::string prepareChapterBody( + const EpubArchiveChapter& chapter, + ZipWriter& zip, + std::unordered_map& knownAssets, + std::vector& assets); + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/cpp/export/EpubDocuments.cpp b/modules/nitro-epub/cpp/export/EpubDocuments.cpp new file mode 100644 index 000000000..221b760f8 --- /dev/null +++ b/modules/nitro-epub/cpp/export/EpubDocuments.cpp @@ -0,0 +1,217 @@ +#include "EpubDocuments.hpp" + +#include +#include +#include +#include + +namespace margelo::nitro::nitroepub { +namespace { + +std::string xmlEscape(const std::string& value) { + std::string escaped; + escaped.reserve(value.size()); + for (const char character : value) { + switch (character) { + case '&': + escaped += "&"; + break; + case '<': + escaped += "<"; + break; + case '>': + escaped += ">"; + break; + case '"': + escaped += """; + break; + case '\'': + escaped += "'"; + break; + default: + escaped += character; + break; + } + } + return escaped; +} + +std::string modifiedTimestamp() { + const std::time_t now = std::chrono::system_clock::to_time_t( + std::chrono::system_clock::now()); + std::tm utc{}; +#ifdef _WIN32 + gmtime_s(&utc, &now); +#else + gmtime_r(&now, &utc); +#endif + std::ostringstream timestamp; + timestamp << std::put_time(&utc, "%Y-%m-%dT%H:%M:%SZ"); + return timestamp.str(); +} + +} // namespace + +std::string containerDocument() { + return R"( + + + + + +)"; +} + +std::string chapterDocument(const EpubArchiveChapter& chapter, + const std::string& body, + bool hasJavaScript) { + const std::string title = xmlEscape(chapter.title); + const std::string script = + hasJavaScript ? R"( + )" + : ""; + const std::string onLoad = + hasJavaScript ? " onload=\"fnEpub()\"" : ""; + return R"( + + + + + )" + + title + + R"( + )" + + script + R"( + + + )" + body + R"( + + +)"; +} + +std::string navigationDocument( + const EpubArchiveMetadata& metadata, + const std::vector& chapters) { + std::ostringstream items; + for (std::size_t index = 0; index < chapters.size(); ++index) { + items << "
  • " + << xmlEscape(chapters[index].title) << "
  • \n"; + } + return R"( + + + )" + + xmlEscape(metadata.title) + R"( + + + + +)"; +} + +std::string ncxDocument( + const EpubArchiveMetadata& metadata, + const std::vector& chapters) { + std::ostringstream points; + for (std::size_t index = 0; index < chapters.size(); ++index) { + points << " \n" + << " " + << xmlEscape(chapters[index].title) << "\n" + << " \n" + << " \n"; + } + return R"( + + + )" + + xmlEscape(metadata.title) + R"( + +)" + points.str() + + R"( + +)"; +} + +std::string packageDocument( + const EpubArchiveMetadata& metadata, + const std::vector& chapters, + const std::vector& assets, + const EpubAsset* cover, + bool hasJavaScript) { + std::ostringstream manifest; + manifest << " \n" + << " \n" + << " \n"; + if (hasJavaScript) { + manifest << " \n"; + } + if (cover != nullptr) { + manifest << " archivePath.substr(5)) + << "\" media-type=\"" << cover->mediaType + << "\" properties=\"cover-image\"/>\n"; + } + for (const EpubAsset& asset : assets) { + manifest << " \n"; + } + for (std::size_t index = 0; index < chapters.size(); ++index) { + manifest << " \n"; + } + + std::ostringstream spine; + for (std::size_t index = 0; index < chapters.size(); ++index) { + spine << " \n"; + } + + return R"( + + + )" + + xmlEscape(metadata.bookId) + R"( + )" + + xmlEscape(metadata.title) + R"( + )" + + xmlEscape(metadata.language) + R"( + )" + + xmlEscape(metadata.author) + R"( + )" + + xmlEscape(metadata.description) + R"( + )" + + modifiedTimestamp() + R"( + + +)" + manifest.str() + + R"( + +)" + spine.str() + + R"( + +)"; +} + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/cpp/export/EpubDocuments.hpp b/modules/nitro-epub/cpp/export/EpubDocuments.hpp new file mode 100644 index 000000000..205ffdf2b --- /dev/null +++ b/modules/nitro-epub/cpp/export/EpubDocuments.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "EpubAsset.hpp" +#include "EpubExporter.hpp" + +#include +#include + +namespace margelo::nitro::nitroepub { + +std::string containerDocument(); +std::string chapterDocument(const EpubArchiveChapter& chapter, + const std::string& body, + bool hasJavaScript); +std::string navigationDocument( + const EpubArchiveMetadata& metadata, + const std::vector& chapters); +std::string ncxDocument( + const EpubArchiveMetadata& metadata, + const std::vector& chapters); +std::string packageDocument( + const EpubArchiveMetadata& metadata, + const std::vector& chapters, + const std::vector& assets, + const EpubAsset* cover, + bool hasJavaScript); + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/cpp/export/EpubExporter.cpp b/modules/nitro-epub/cpp/export/EpubExporter.cpp new file mode 100644 index 000000000..f23f180dd --- /dev/null +++ b/modules/nitro-epub/cpp/export/EpubExporter.cpp @@ -0,0 +1,109 @@ +#include "EpubExporter.hpp" + +#include "EpubAsset.hpp" +#include "EpubContentProcessor.hpp" +#include "EpubDocuments.hpp" +#include "ZipWriter.hpp" + +#include +#include +#include + +namespace margelo::nitro::nitroepub { + +EpubArchiveResult exportEpubArchive( + const EpubArchiveMetadata& metadata, + const std::vector& chapters, + const std::string& outputPath, + std::function + onProgress) { + if (metadata.title.empty()) { + throw std::runtime_error("EPUB title cannot be empty"); + } + if (outputPath.empty()) { + throw std::runtime_error("EPUB output path cannot be empty"); + } + + std::vector availableChapters; + availableChapters.reserve(chapters.size()); + for (const EpubArchiveChapter& chapter : chapters) { + if (std::filesystem::is_regular_file(chapter.htmlPath)) { + availableChapters.push_back(chapter); + } + } + if (availableChapters.empty()) { + throw std::runtime_error("EPUB requires at least one downloaded chapter"); + } + + const std::filesystem::path finalPath(outputPath); + const std::filesystem::path temporaryPath = finalPath.string() + ".tmp"; + if (!finalPath.parent_path().empty()) { + std::filesystem::create_directories(finalPath.parent_path()); + } + std::error_code ignored; + std::filesystem::remove(temporaryPath, ignored); + + try { + ZipWriter zip(temporaryPath); + + // EPUB requires this exact file to be the first entry and uncompressed. + zip.addStored("mimetype", "application/epub+zip"); + zip.addDeflated("META-INF/container.xml", containerDocument()); + zip.addDeflated("EPUB/styles.css", metadata.stylesheet); + + const bool hasJavaScript = !metadata.javascript.empty(); + if (hasJavaScript) { + zip.addDeflated("EPUB/script.js", + "function fnEpub(){\n" + metadata.javascript + "\n}\n"); + } + + std::vector assets; + std::unordered_map knownAssets; + EpubAsset cover; + EpubAsset* coverPointer = nullptr; + const std::filesystem::path coverPath = + localPathFromUri(metadata.coverPath); + if (!coverPath.empty() && std::filesystem::is_regular_file(coverPath)) { + const std::string extension = normalizedImageExtension(coverPath); + cover = EpubAsset{ + "cover-image", + "EPUB/images/cover" + extension, + imageMediaType(extension), + }; + zip.addFile(cover.archivePath, coverPath); + coverPointer = &cover; + } + + for (std::size_t index = 0; index < availableChapters.size(); ++index) { + const std::string body = prepareChapterBody( + availableChapters[index], zip, knownAssets, assets); + zip.addDeflated( + "EPUB/text/chapter-" + std::to_string(index) + ".xhtml", + chapterDocument(availableChapters[index], body, hasJavaScript)); + if (onProgress) { + onProgress(index + 1, + availableChapters.size(), + availableChapters[index].title); + } + } + + zip.addDeflated( + "EPUB/nav.xhtml", + navigationDocument(metadata, availableChapters)); + zip.addDeflated("EPUB/toc.ncx", ncxDocument(metadata, availableChapters)); + zip.addDeflated( + "EPUB/content.opf", + packageDocument(metadata, availableChapters, assets, coverPointer, + hasJavaScript)); + zip.finish(); + + std::filesystem::remove(finalPath, ignored); + std::filesystem::rename(temporaryPath, finalPath); + return EpubArchiveResult{outputPath, availableChapters.size()}; + } catch (...) { + std::filesystem::remove(temporaryPath, ignored); + throw; + } +} + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/cpp/export/EpubExporter.hpp b/modules/nitro-epub/cpp/export/EpubExporter.hpp new file mode 100644 index 000000000..8b57327b2 --- /dev/null +++ b/modules/nitro-epub/cpp/export/EpubExporter.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include + +namespace margelo::nitro::nitroepub { + +struct EpubArchiveMetadata { + std::string title; + std::string language; + std::string coverPath; + std::string description; + std::string author; + std::string bookId; + std::string stylesheet; + std::string javascript; +}; + +struct EpubArchiveChapter { + std::string title; + std::string htmlPath; + std::string novelId; + std::string chapterId; +}; + +struct EpubArchiveResult { + std::string outputPath; + std::size_t chapterCount; +}; + +EpubArchiveResult exportEpubArchive( + const EpubArchiveMetadata& metadata, + const std::vector& chapters, + const std::string& outputPath, + std::function + onProgress = {}); + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/cpp/export/ZipWriter.cpp b/modules/nitro-epub/cpp/export/ZipWriter.cpp new file mode 100644 index 000000000..70bbe5593 --- /dev/null +++ b/modules/nitro-epub/cpp/export/ZipWriter.cpp @@ -0,0 +1,213 @@ +#include "ZipWriter.hpp" + +#include +#include +#include +#include +#include + +namespace margelo::nitro::nitroepub { +namespace { + +constexpr std::uint16_t UTF8_FLAG = 0x0800; +constexpr std::uint16_t STORED = 0; +constexpr std::uint16_t DEFLATED = 8; +constexpr std::uint16_t DOS_DATE_1980_01_01 = 0x0021; + +std::uint32_t checked32(std::uint64_t value, const char* description) { + if (value > std::numeric_limits::max()) { + throw std::runtime_error(std::string("EPUB exceeds ZIP32 ") + description); + } + return static_cast(value); +} + +std::uint16_t checked16(std::size_t value, const char* description) { + if (value > std::numeric_limits::max()) { + throw std::runtime_error(std::string("EPUB exceeds ZIP16 ") + description); + } + return static_cast(value); +} + +std::vector deflateRaw(const std::vector& input) { + z_stream stream{}; + if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, + Z_DEFAULT_STRATEGY) != Z_OK) { + throw std::runtime_error("Failed to initialize EPUB compression"); + } + + std::vector output( + std::max(128, compressBound(input.size()))); + stream.next_in = const_cast( + reinterpret_cast(input.data())); + stream.avail_in = checked32(input.size(), "entry size"); + stream.next_out = reinterpret_cast(output.data()); + stream.avail_out = checked32(output.size(), "compressed entry size"); + + const int result = deflate(&stream, Z_FINISH); + if (result != Z_STREAM_END) { + deflateEnd(&stream); + throw std::runtime_error("Failed to compress EPUB entry"); + } + output.resize(stream.total_out); + deflateEnd(&stream); + return output; +} + +} // namespace + +ZipWriter::ZipWriter(const std::filesystem::path& outputPath) + : output_(outputPath, std::ios::binary | std::ios::trunc) { + if (!output_) { + throw std::runtime_error("Unable to create EPUB at " + outputPath.string()); + } +} + +ZipWriter::~ZipWriter() { + if (!finished_) { + output_.close(); + } +} + +void ZipWriter::addStored(const std::string& archivePath, + const std::string& content) { + add(archivePath, + std::vector(content.begin(), content.end()), false); +} + +void ZipWriter::addDeflated(const std::string& archivePath, + const std::string& content) { + add(archivePath, + std::vector(content.begin(), content.end()), true); +} + +void ZipWriter::addFile(const std::string& archivePath, + const std::filesystem::path& sourcePath, + bool compress) { + std::ifstream input(sourcePath, std::ios::binary); + if (!input) { + throw std::runtime_error("Unable to read EPUB asset " + sourcePath.string()); + } + std::vector content( + (std::istreambuf_iterator(input)), + std::istreambuf_iterator()); + add(archivePath, content, compress); +} + +void ZipWriter::add(const std::string& archivePath, + const std::vector& content, + bool compress) { + if (finished_) { + throw std::runtime_error("Cannot add an entry to a finished EPUB"); + } + if (archivePath.empty() || archivePath.front() == '/' || + archivePath.find("..") != std::string::npos) { + throw std::runtime_error("Unsafe EPUB archive path: " + archivePath); + } + + const auto compressed = compress ? deflateRaw(content) : content; + const auto crc = static_cast( + crc32(0, reinterpret_cast(content.data()), + checked32(content.size(), "entry size"))); + const auto offset = checked32(static_cast(output_.tellp()), + "local header offset"); + const auto pathLength = checked16(archivePath.size(), "entry name length"); + const auto method = static_cast(compress ? DEFLATED : STORED); + + write32(0x04034b50); + write16(20); + write16(UTF8_FLAG); + write16(method); + write16(0); + write16(DOS_DATE_1980_01_01); + write32(crc); + write32(checked32(compressed.size(), "compressed entry size")); + write32(checked32(content.size(), "entry size")); + write16(pathLength); + write16(0); + output_.write(archivePath.data(), pathLength); + output_.write(reinterpret_cast(compressed.data()), + static_cast(compressed.size())); + if (!output_) { + throw std::runtime_error("Failed while writing EPUB entry " + archivePath); + } + + entries_.push_back(CentralEntry{ + archivePath, + method, + crc, + checked32(compressed.size(), "compressed entry size"), + checked32(content.size(), "entry size"), + offset, + }); +} + +void ZipWriter::finish() { + if (finished_) { + return; + } + + const auto directoryOffset = + checked32(static_cast(output_.tellp()), + "central directory offset"); + for (const auto& entry : entries_) { + const auto pathLength = + checked16(entry.archivePath.size(), "entry name length"); + write32(0x02014b50); + write16(20); + write16(20); + write16(UTF8_FLAG); + write16(entry.method); + write16(0); + write16(DOS_DATE_1980_01_01); + write32(entry.crc); + write32(entry.compressedSize); + write32(entry.uncompressedSize); + write16(pathLength); + write16(0); + write16(0); + write16(0); + write16(0); + write32(0); + write32(entry.localHeaderOffset); + output_.write(entry.archivePath.data(), pathLength); + } + + const auto directoryEnd = + checked32(static_cast(output_.tellp()), + "central directory size"); + const auto entryCount = checked16(entries_.size(), "entry count"); + write32(0x06054b50); + write16(0); + write16(0); + write16(entryCount); + write16(entryCount); + write32(directoryEnd - directoryOffset); + write32(directoryOffset); + write16(0); + output_.flush(); + if (!output_) { + throw std::runtime_error("Failed to finalize EPUB archive"); + } + output_.close(); + finished_ = true; +} + +void ZipWriter::write16(std::uint16_t value) { + const char bytes[] = { + static_cast(value & 0xff), + static_cast((value >> 8) & 0xff), + }; + output_.write(bytes, sizeof(bytes)); +} + +void ZipWriter::write32(std::uint32_t value) { + const char bytes[] = { + static_cast(value & 0xff), + static_cast((value >> 8) & 0xff), + static_cast((value >> 16) & 0xff), + static_cast((value >> 24) & 0xff), + }; + output_.write(bytes, sizeof(bytes)); +} + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/cpp/export/ZipWriter.hpp b/modules/nitro-epub/cpp/export/ZipWriter.hpp new file mode 100644 index 000000000..b12968d33 --- /dev/null +++ b/modules/nitro-epub/cpp/export/ZipWriter.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace margelo::nitro::nitroepub { + +class ZipWriter final { +public: + explicit ZipWriter(const std::filesystem::path& outputPath); + ~ZipWriter(); + + ZipWriter(const ZipWriter&) = delete; + ZipWriter& operator=(const ZipWriter&) = delete; + + void addStored(const std::string& archivePath, const std::string& content); + void addDeflated(const std::string& archivePath, const std::string& content); + void addFile(const std::string& archivePath, + const std::filesystem::path& sourcePath, + bool compress = false); + void finish(); + +private: + struct CentralEntry { + std::string archivePath; + std::uint16_t method; + std::uint32_t crc; + std::uint32_t compressedSize; + std::uint32_t uncompressedSize; + std::uint32_t localHeaderOffset; + }; + + std::ofstream output_; + std::vector entries_; + bool finished_ = false; + + void add(const std::string& archivePath, + const std::vector& content, + bool compress); + void write16(std::uint16_t value); + void write32(std::uint32_t value); +}; + +} // namespace margelo::nitro::nitroepub diff --git a/shared/Epub.cpp b/modules/nitro-epub/cpp/import/EpubParser.cpp similarity index 73% rename from shared/Epub.cpp rename to modules/nitro-epub/cpp/import/EpubParser.cpp index 082e11710..33497cfa3 100644 --- a/shared/Epub.cpp +++ b/modules/nitro-epub/cpp/import/EpubParser.cpp @@ -1,9 +1,10 @@ #include -#include +#include "../pugixml.hpp" #include #include +#include #include -#include +#include "EpubParser.hpp" #include std::string join(const std::string &folder_path, const std::string &child_path) @@ -62,6 +63,78 @@ std::string getParentPath(const std::string &path) return path.substr(0, pos); } +std::string getLocalName(const std::string &qualified_name) +{ + size_t separator = qualified_name.find(':'); + return separator == std::string::npos + ? qualified_name + : qualified_name.substr(separator + 1); +} + +std::string findImageReference(const pugi::xml_node &node) +{ + std::string node_name = getLocalName(node.name()); + if (node_name == "img" || node_name == "image") + { + for (const char *attribute_name : {"src", "href", "xlink:href"}) + { + std::string reference = node.attribute(attribute_name).as_string(); + if (!reference.empty()) + { + size_t fragment = reference.find('#'); + return fragment == std::string::npos + ? reference + : reference.substr(0, fragment); + } + } + } + + for (pugi::xml_node child : node.children()) + { + std::string reference = findImageReference(child); + if (!reference.empty()) + { + return reference; + } + } + + return ""; +} + +std::string findCoverImagePath( + const std::string &cover_document_path, + const std::unordered_set &image_paths) +{ + pugi::xml_document cover_document; + if (!cover_document.load_file(cover_document_path.c_str())) + { + return ""; + } + + std::string image_reference = findImageReference(cover_document); + if (image_reference.empty()) + { + return ""; + } + + std::string image_path = join(getParentPath(cover_document_path), image_reference); + return image_paths.count(image_path) ? image_path : ""; +} + +bool hasProperty(const std::string &properties, const std::string &property) +{ + std::stringstream property_stream(properties); + std::string value; + while (property_stream >> value) + { + if (value == property) + { + return true; + } + } + return false; +} + std::string find_toc_href(const pugi::xml_document &opf_doc) { auto manifest = opf_doc.child("package").child("manifest"); @@ -200,6 +273,9 @@ void parse_opf_from_folder(const std::string &base_dir, meta_out.summary = metadata.child("dc:description").text().as_string(); std::unordered_map id_to_href; + std::unordered_map id_to_media_type; + std::unordered_set image_paths; + std::string property_cover_id; std::string cover_id; for (pugi::xml_node meta : metadata.children("meta")) @@ -217,22 +293,45 @@ void parse_opf_from_folder(const std::string &base_dir, std::string id = item.attribute("id").value(); std::string href = item.attribute("href").value(); std::string media_type = item.attribute("media-type").value(); + std::string properties = item.attribute("properties").value(); id_to_href[id] = href; + id_to_media_type[id] = media_type; if (media_type == "text/css") { meta_out.cssPaths.push_back(join(opf_dir, href)); } - else if (media_type == "image/jpeg" || media_type == "image/png" || media_type == "image/jpg") + else if (media_type.rfind("image/", 0) == 0) { - meta_out.imagePaths.push_back(join(opf_dir, href)); + std::string image_path = join(opf_dir, href); + meta_out.imagePaths.push_back(image_path); + image_paths.insert(image_path); + if (property_cover_id.empty() && hasProperty(properties, "cover-image")) + { + property_cover_id = id; + } } } - if (!cover_id.empty() && id_to_href.count(cover_id)) + if (!cover_id.empty() && id_to_href.count(cover_id) && + id_to_media_type[cover_id].rfind("image/", 0) == 0) { meta_out.cover = join(opf_dir, id_to_href[cover_id]); } + else if (!property_cover_id.empty()) + { + meta_out.cover = join(opf_dir, id_to_href[property_cover_id]); + } + else if (id_to_href.count("cover-image") && + id_to_media_type["cover-image"].rfind("image/", 0) == 0) + { + meta_out.cover = join(opf_dir, id_to_href["cover-image"]); + } + else if (!cover_id.empty() && id_to_href.count(cover_id)) + { + std::string cover_document_path = join(opf_dir, id_to_href[cover_id]); + meta_out.cover = findCoverImagePath(cover_document_path, image_paths); + } auto spine = opf_doc.child("package").child("spine"); std::string prev_name = ""; @@ -300,4 +399,4 @@ EpubMetadata parseEpub(const std::string epub_path) parse_opf_from_folder(epub_path, opf_path, metadata); return metadata; -} \ No newline at end of file +} diff --git a/modules/nitro-epub/cpp/import/EpubParser.hpp b/modules/nitro-epub/cpp/import/EpubParser.hpp new file mode 100644 index 000000000..f25c37673 --- /dev/null +++ b/modules/nitro-epub/cpp/import/EpubParser.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +struct Chapter { + std::string name; + std::string path; +}; + +struct EpubMetadata { + std::string name; + std::string path; + std::string cover; + std::string summary; + std::string author; + std::string artist; + std::vector chapters; + std::vector cssPaths; + std::vector imagePaths; +}; + +EpubMetadata parseEpub(const std::string epub_path); diff --git a/shared/pugiconfig.hpp b/modules/nitro-epub/cpp/pugiconfig.hpp similarity index 100% rename from shared/pugiconfig.hpp rename to modules/nitro-epub/cpp/pugiconfig.hpp diff --git a/shared/pugixml.cpp b/modules/nitro-epub/cpp/pugixml.cpp similarity index 100% rename from shared/pugixml.cpp rename to modules/nitro-epub/cpp/pugixml.cpp diff --git a/shared/pugixml.hpp b/modules/nitro-epub/cpp/pugixml.hpp similarity index 100% rename from shared/pugixml.hpp rename to modules/nitro-epub/cpp/pugixml.hpp diff --git a/modules/nitro-epub/cpp/tests/EpubExporterTest.cpp b/modules/nitro-epub/cpp/tests/EpubExporterTest.cpp new file mode 100644 index 000000000..329a18ff5 --- /dev/null +++ b/modules/nitro-epub/cpp/tests/EpubExporterTest.cpp @@ -0,0 +1,93 @@ +#include "../export/EpubExporter.hpp" + +#include +#include +#include +#include +#include +#include +#include + +using namespace margelo::nitro::nitroepub; + +namespace { + +std::uint16_t read16(const std::vector& bytes, + std::size_t offset) { + return static_cast(bytes[offset]) | + static_cast(bytes[offset + 1] << 8); +} + +} // namespace + +int main(int argc, char** argv) { + assert(argc == 2); + const std::filesystem::path fixtureDirectory = argv[1]; + std::filesystem::remove_all(fixtureDirectory); + std::filesystem::create_directories(fixtureDirectory); + + const std::filesystem::path imagePath = fixtureDirectory / "image.png"; + { + const std::uint8_t png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, + }; + std::ofstream image(imagePath, std::ios::binary); + image.write(reinterpret_cast(png), sizeof(png)); + } + + const std::filesystem::path chapterPath = + fixtureDirectory / "chapter.html"; + { + std::ofstream chapter(chapterPath); + chapter << "

    One & Two

    " + << "" + << "" + << ""; + } + + const std::filesystem::path outputPath = fixtureDirectory / "fixture.epub"; + const EpubArchiveResult result = exportEpubArchive( + EpubArchiveMetadata{ + "Fixture & Book", + "en", + "", + "Description", + "LNReader", + "urn:lnreader:test", + "body { line-height: 1.5; }", + "", + }, + { + EpubArchiveChapter{ + "Chapter ", + chapterPath.string(), + "1", + "2", + }, + EpubArchiveChapter{ + "Missing chapter", + (fixtureDirectory / "missing.html").string(), + "1", + "3", + }, + }, + outputPath.string()); + + assert(result.chapterCount == 1); + assert(std::filesystem::is_regular_file(outputPath)); + + std::ifstream epub(outputPath, std::ios::binary); + const std::vector bytes( + (std::istreambuf_iterator(epub)), + std::istreambuf_iterator()); + assert(bytes.size() > 30); + assert(bytes[0] == 0x50 && bytes[1] == 0x4b && bytes[2] == 0x03 && + bytes[3] == 0x04); + assert(read16(bytes, 8) == 0); + const std::uint16_t nameLength = read16(bytes, 26); + const std::string firstName(bytes.begin() + 30, + bytes.begin() + 30 + nameLength); + assert(firstName == "mimetype"); + + return 0; +} diff --git a/modules/nitro-epub/cpp/tests/EpubParserTest.cpp b/modules/nitro-epub/cpp/tests/EpubParserTest.cpp new file mode 100644 index 000000000..6e85cc1cd --- /dev/null +++ b/modules/nitro-epub/cpp/tests/EpubParserTest.cpp @@ -0,0 +1,100 @@ +#include "../import/EpubParser.hpp" + +#include +#include +#include +#include +#include +#include + +namespace { + +void writeFile(const std::filesystem::path& path, const std::string& content) { + std::filesystem::create_directories(path.parent_path()); + std::ofstream file(path); + file << content; +} + +bool containsPath(const std::vector& paths, + const std::filesystem::path& expectedPath) { + return std::find(paths.begin(), paths.end(), expectedPath.string()) != + paths.end(); +} + +} // namespace + +int main(int argc, char** argv) { + assert(argc == 2); + const std::filesystem::path fixtureDirectory = argv[1]; + std::filesystem::remove_all(fixtureDirectory); + + writeFile( + fixtureDirectory / "META-INF/container.xml", + R"xml( + + + + +)xml"); + + writeFile( + fixtureDirectory / "OEBPS/content.opf", + R"xml( + + + Image fixture + + + + + + + + + + + + + + + + +)xml"); + + writeFile( + fixtureDirectory / "OEBPS/toc.ncx", + R"xml( + + + + Chapter + + + +)xml"); + writeFile(fixtureDirectory / "OEBPS/Text/chapter.xhtml", + "Chapter"); + writeFile( + fixtureDirectory / "OEBPS/Text/cover.xhtml", + R"xml()xml"); + + const EpubMetadata metadata = parseEpub(fixtureDirectory.string()); + const std::filesystem::path imageDirectory = fixtureDirectory / "OEBPS/Images"; + + assert(metadata.imagePaths.size() == 5); + assert(containsPath(metadata.imagePaths, imageDirectory / "cover.jpg")); + assert(containsPath(metadata.imagePaths, + imageDirectory / "illustration.png")); + assert(containsPath(metadata.imagePaths, + imageDirectory / "illustration.webp")); + assert(containsPath(metadata.imagePaths, + imageDirectory / "illustration.gif")); + assert(containsPath(metadata.imagePaths, + imageDirectory / "illustration.svg")); + assert(metadata.cover == (imageDirectory / "cover.jpg").string()); + assert(metadata.chapters.size() == 2); + assert(metadata.chapters.front().path == + (fixtureDirectory / "OEBPS/Text/cover.xhtml").string()); + + return 0; +} diff --git a/modules/nitro-epub/nitro.json b/modules/nitro-epub/nitro.json new file mode 100644 index 000000000..d8a0d38b1 --- /dev/null +++ b/modules/nitro-epub/nitro.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://nitro.margelo.com/nitro.schema.json", + "cxxNamespace": [ + "nitroepub" + ], + "ios": { + "iosModuleName": "NitroEpub" + }, + "android": { + "androidNamespace": [ + "nitroepub" + ], + "androidCxxLibName": "NitroEpub" + }, + "autolinking": { + "Epub": { + "all": { + "language": "c++", + "implementationClassName": "HybridEpub" + } + } + }, + "ignorePaths": [ + "**/node_modules" + ] +} diff --git a/modules/nitro-epub/nitrogen/generated/.gitattributes b/modules/nitro-epub/nitrogen/generated/.gitattributes new file mode 100644 index 000000000..fb7a0d5a3 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/.gitattributes @@ -0,0 +1 @@ +** linguist-generated=true diff --git a/modules/nitro-epub/nitrogen/generated/android/NitroEpub+autolinking.cmake b/modules/nitro-epub/nitrogen/generated/android/NitroEpub+autolinking.cmake new file mode 100644 index 000000000..60d825f85 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/android/NitroEpub+autolinking.cmake @@ -0,0 +1,81 @@ +# +# NitroEpub+autolinking.cmake +# This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +# https://github.com/mrousavy/nitro +# Copyright © Marc Rousavy @ Margelo +# + +# This is a CMake file that adds all files generated by Nitrogen +# to the current CMake project. +# +# To use it, add this to your CMakeLists.txt: +# ```cmake +# include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroEpub+autolinking.cmake) +# ``` + +# Define a flag to check if we are building properly +add_definitions(-DBUILDING_NITROEPUB_WITH_GENERATED_CMAKE_PROJECT) + +# Enable Raw Props parsing in react-native (for Nitro Views) +add_definitions(-DRN_SERIALIZABLE_STATE) + +# Add all headers that were generated by Nitrogen +include_directories( + "../nitrogen/generated/shared/c++" + "../nitrogen/generated/android/c++" + "../nitrogen/generated/android/" +) + +# Add all .cpp sources that were generated by Nitrogen +target_sources( + # CMake project name (Android C++ library name) + NitroEpub PRIVATE + # Autolinking Setup + ../nitrogen/generated/android/NitroEpubOnLoad.cpp + # Shared Nitrogen C++ sources + ../nitrogen/generated/shared/c++/HybridEpubSpec.cpp + # Android-specific Nitrogen C++ sources + +) + +# From node_modules/react-native/ReactAndroid/cmake-utils/folly-flags.cmake +# Used in node_modules/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake +target_compile_definitions( + NitroEpub PRIVATE + -DFOLLY_NO_CONFIG=1 + -DFOLLY_HAVE_CLOCK_GETTIME=1 + -DFOLLY_USE_LIBCPP=1 + -DFOLLY_CFG_NO_COROUTINES=1 + -DFOLLY_MOBILE=1 + -DFOLLY_HAVE_RECVMMSG=1 + -DFOLLY_HAVE_PTHREAD=1 + # Once we target android-23 above, we can comment + # the following line. NDK uses GNU style stderror_r() after API 23. + -DFOLLY_HAVE_XSI_STRERROR_R=1 +) + +# Add all libraries required by the generated specs +find_package(fbjni REQUIRED) # <-- Used for communication between Java <-> C++ +find_package(ReactAndroid REQUIRED) # <-- Used to set up React Native bindings (e.g. CallInvoker/TurboModule) +find_package(react-native-nitro-modules REQUIRED) # <-- Used to create all HybridObjects and use the Nitro core library + +# Link all libraries together +target_link_libraries( + NitroEpub + fbjni::fbjni # <-- Facebook C++ JNI helpers + ReactAndroid::jsi # <-- RN: JSI + react-native-nitro-modules::NitroModules # <-- NitroModules Core :) +) + +# Link react-native (different prefab between RN 0.75 and RN 0.76) +if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76) + target_link_libraries( + NitroEpub + ReactAndroid::reactnative # <-- RN: Native Modules umbrella prefab + ) +else() + target_link_libraries( + NitroEpub + ReactAndroid::react_nativemodule_core # <-- RN: TurboModules Core + ) +endif() diff --git a/modules/nitro-epub/nitrogen/generated/android/NitroEpub+autolinking.gradle b/modules/nitro-epub/nitrogen/generated/android/NitroEpub+autolinking.gradle new file mode 100644 index 000000000..29e7f4535 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/android/NitroEpub+autolinking.gradle @@ -0,0 +1,27 @@ +/// +/// NitroEpub+autolinking.gradle +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +/// This is a Gradle file that adds all files generated by Nitrogen +/// to the current Gradle project. +/// +/// To use it, add this to your build.gradle: +/// ```gradle +/// apply from: '../nitrogen/generated/android/NitroEpub+autolinking.gradle' +/// ``` + +logger.warn("[NitroModules] 🔥 NitroEpub is boosted by nitro!") + +android { + sourceSets { + main { + java.srcDirs += [ + // Nitrogen files + "${project.projectDir}/../nitrogen/generated/android/kotlin" + ] + } + } +} diff --git a/modules/nitro-epub/nitrogen/generated/android/NitroEpubOnLoad.cpp b/modules/nitro-epub/nitrogen/generated/android/NitroEpubOnLoad.cpp new file mode 100644 index 000000000..256f97670 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/android/NitroEpubOnLoad.cpp @@ -0,0 +1,49 @@ +/// +/// NitroEpubOnLoad.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#ifndef BUILDING_NITROEPUB_WITH_GENERATED_CMAKE_PROJECT +#error NitroEpubOnLoad.cpp is not being built with the autogenerated CMakeLists.txt project. Is a different CMakeLists.txt building this? +#endif + +#include "NitroEpubOnLoad.hpp" + +#include +#include +#include + +#include "HybridEpub.hpp" + +namespace margelo::nitro::nitroepub { + +int initialize(JavaVM* vm) { + return facebook::jni::initialize(vm, []() { + ::margelo::nitro::nitroepub::registerAllNatives(); + }); +} + + + +void registerAllNatives() { + using namespace margelo::nitro; + using namespace margelo::nitro::nitroepub; + + // Register native JNI methods + + + // Register Nitro Hybrid Objects + HybridObjectRegistry::registerHybridObjectConstructor( + "Epub", + []() -> std::shared_ptr { + static_assert(std::is_default_constructible_v, + "The HybridObject \"HybridEpub\" is not default-constructible! " + "Create a public constructor that takes zero arguments to be able to autolink this HybridObject."); + return std::make_shared(); + } + ); +} + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/nitrogen/generated/android/NitroEpubOnLoad.hpp b/modules/nitro-epub/nitrogen/generated/android/NitroEpubOnLoad.hpp new file mode 100644 index 000000000..538505597 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/android/NitroEpubOnLoad.hpp @@ -0,0 +1,34 @@ +/// +/// NitroEpubOnLoad.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include +#include +#include + +namespace margelo::nitro::nitroepub { + + [[deprecated("Use registerNatives() instead.")]] + int initialize(JavaVM* vm); + + /** + * Register the native (C++) part of NitroEpub, and autolinks all Hybrid Objects. + * Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`), + * inside a `facebook::jni::initialize(vm, ...)` call. + * Example: + * ```cpp (cpp-adapter.cpp) + * JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { + * return facebook::jni::initialize(vm, []() { + * // register all NitroEpub HybridObjects + * margelo::nitro::nitroepub::registerNatives(); + * // any other custom registrations go here. + * }); + * } + * ``` + */ + void registerAllNatives(); + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroepub/NitroEpubOnLoad.kt b/modules/nitro-epub/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroepub/NitroEpubOnLoad.kt new file mode 100644 index 000000000..92f896dcf --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroepub/NitroEpubOnLoad.kt @@ -0,0 +1,35 @@ +/// +/// NitroEpubOnLoad.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitroepub + +import android.util.Log + +internal class NitroEpubOnLoad { + companion object { + private const val TAG = "NitroEpubOnLoad" + private var didLoad = false + /** + * Initializes the native part of "NitroEpub". + * This method is idempotent and can be called more than once. + */ + @JvmStatic + fun initializeNative() { + if (didLoad) return + try { + Log.i(TAG, "Loading NitroEpub C++ library...") + System.loadLibrary("NitroEpub") + Log.i(TAG, "Successfully loaded NitroEpub C++ library!") + didLoad = true + } catch (e: Error) { + Log.e(TAG, "Failed to load NitroEpub C++ library! Is it properly installed and linked? " + + "Is the name correct? (see `CMakeLists.txt`, at `add_library(...)`)", e) + throw e + } + } + } +} diff --git a/modules/nitro-epub/nitrogen/generated/ios/NitroEpub+autolinking.rb b/modules/nitro-epub/nitrogen/generated/ios/NitroEpub+autolinking.rb new file mode 100644 index 000000000..a528c2f80 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/ios/NitroEpub+autolinking.rb @@ -0,0 +1,62 @@ +# +# NitroEpub+autolinking.rb +# This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +# https://github.com/mrousavy/nitro +# Copyright © Marc Rousavy @ Margelo +# + +# This is a Ruby script that adds all files generated by Nitrogen +# to the given podspec. +# +# To use it, add this to your .podspec: +# ```ruby +# Pod::Spec.new do |spec| +# # ... +# +# # Add all files generated by Nitrogen +# load 'nitrogen/generated/ios/NitroEpub+autolinking.rb' +# add_nitrogen_files(spec) +# end +# ``` + +def add_nitrogen_files(spec) + Pod::UI.puts "[NitroModules] 🔥 NitroEpub is boosted by nitro!" + + spec.dependency "NitroModules" + + current_source_files = Array(spec.attributes_hash['source_files']) + spec.source_files = current_source_files + [ + # Generated cross-platform specs + "nitrogen/generated/shared/**/*.{h,hpp,c,cpp,swift}", + # Generated bridges for the cross-platform specs + "nitrogen/generated/ios/**/*.{h,hpp,c,cpp,mm,swift}", + ] + + current_public_header_files = Array(spec.attributes_hash['public_header_files']) + spec.public_header_files = current_public_header_files + [ + # Generated specs + "nitrogen/generated/shared/**/*.{h,hpp}", + # Swift to C++ bridging helpers + "nitrogen/generated/ios/NitroEpub-Swift-Cxx-Bridge.hpp" + ] + + current_private_header_files = Array(spec.attributes_hash['private_header_files']) + spec.private_header_files = current_private_header_files + [ + # iOS specific specs + "nitrogen/generated/ios/c++/**/*.{h,hpp}", + # Views are framework-specific and should be private + "nitrogen/generated/shared/**/views/**/*" + ] + + current_pod_target_xcconfig = spec.attributes_hash['pod_target_xcconfig'] || {} + spec.pod_target_xcconfig = current_pod_target_xcconfig.merge({ + # Use C++ 20 + "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", + # Enables C++ <-> Swift interop (by default it's only ObjC) + "SWIFT_OBJC_INTEROP_MODE" => "objcxx", + # Enables stricter modular headers + "DEFINES_MODULE" => "YES", + # Disable auto-generated ObjC header for Swift (Static linkage on Xcode 26.4 breaks here) + "SWIFT_INSTALL_OBJC_HEADER" => "NO", + }) +end diff --git a/modules/nitro-epub/nitrogen/generated/ios/NitroEpub-Swift-Cxx-Bridge.cpp b/modules/nitro-epub/nitrogen/generated/ios/NitroEpub-Swift-Cxx-Bridge.cpp new file mode 100644 index 000000000..884fae4e0 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/ios/NitroEpub-Swift-Cxx-Bridge.cpp @@ -0,0 +1,17 @@ +/// +/// NitroEpub-Swift-Cxx-Bridge.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "NitroEpub-Swift-Cxx-Bridge.hpp" + +// Include C++ implementation defined types + + +namespace margelo::nitro::nitroepub::bridge::swift { + + + +} // namespace margelo::nitro::nitroepub::bridge::swift diff --git a/modules/nitro-epub/nitrogen/generated/ios/NitroEpub-Swift-Cxx-Bridge.hpp b/modules/nitro-epub/nitrogen/generated/ios/NitroEpub-Swift-Cxx-Bridge.hpp new file mode 100644 index 000000000..2f1918ae0 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/ios/NitroEpub-Swift-Cxx-Bridge.hpp @@ -0,0 +1,27 @@ +/// +/// NitroEpub-Swift-Cxx-Bridge.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +// Forward declarations of C++ defined types + + +// Forward declarations of Swift defined types + + +// Include C++ defined types + + +/** + * Contains specialized versions of C++ templated types so they can be accessed from Swift, + * as well as helper functions to interact with those C++ types from Swift. + */ +namespace margelo::nitro::nitroepub::bridge::swift { + + + +} // namespace margelo::nitro::nitroepub::bridge::swift diff --git a/modules/nitro-epub/nitrogen/generated/ios/NitroEpub-Swift-Cxx-Umbrella.hpp b/modules/nitro-epub/nitrogen/generated/ios/NitroEpub-Swift-Cxx-Umbrella.hpp new file mode 100644 index 000000000..629c5e7e0 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/ios/NitroEpub-Swift-Cxx-Umbrella.hpp @@ -0,0 +1,38 @@ +/// +/// NitroEpub-Swift-Cxx-Umbrella.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +// Forward declarations of C++ defined types + + +// Include C++ defined types + + +// C++ helpers for Swift +#include "NitroEpub-Swift-Cxx-Bridge.hpp" + +// Common C++ types used in Swift +#include +#include +#include +#include + +// Forward declarations of Swift defined types + + +// Include Swift defined types +#if __has_include("NitroEpub-Swift.h") +// This header is generated by Xcode/Swift on every app build. +// If it cannot be found, make sure the Swift module's name (= podspec name) is actually "NitroEpub". +#include "NitroEpub-Swift.h" +// Same as above, but used when building with frameworks (`use_frameworks`) +#elif __has_include() +#include +#else +#error NitroEpub's autogenerated Swift header cannot be found! Make sure the Swift module's name (= podspec name) is actually "NitroEpub", and try building the app first. +#endif diff --git a/modules/nitro-epub/nitrogen/generated/ios/NitroEpubAutolinking.mm b/modules/nitro-epub/nitrogen/generated/ios/NitroEpubAutolinking.mm new file mode 100644 index 000000000..d713e68cf --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/ios/NitroEpubAutolinking.mm @@ -0,0 +1,35 @@ +/// +/// NitroEpubAutolinking.mm +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#import +#import + +#import + +#include "HybridEpub.hpp" + +@interface NitroEpubAutolinking : NSObject +@end + +@implementation NitroEpubAutolinking + ++ (void) load { + using namespace margelo::nitro; + using namespace margelo::nitro::nitroepub; + + HybridObjectRegistry::registerHybridObjectConstructor( + "Epub", + []() -> std::shared_ptr { + static_assert(std::is_default_constructible_v, + "The HybridObject \"HybridEpub\" is not default-constructible! " + "Create a public constructor that takes zero arguments to be able to autolink this HybridObject."); + return std::make_shared(); + } + ); +} + +@end diff --git a/modules/nitro-epub/nitrogen/generated/ios/NitroEpubAutolinking.swift b/modules/nitro-epub/nitrogen/generated/ios/NitroEpubAutolinking.swift new file mode 100644 index 000000000..ffd50dc99 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/ios/NitroEpubAutolinking.swift @@ -0,0 +1,16 @@ +/// +/// NitroEpubAutolinking.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +// TODO: Use empty enums once Swift supports exporting them as namespaces +// See: https://github.com/swiftlang/swift/pull/83616 +public final class NitroEpubAutolinking { + public typealias bridge = margelo.nitro.nitroepub.bridge.swift + + +} diff --git a/modules/nitro-epub/nitrogen/generated/shared/c++/EpubChapter.hpp b/modules/nitro-epub/nitrogen/generated/shared/c++/EpubChapter.hpp new file mode 100644 index 000000000..4b109009b --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/shared/c++/EpubChapter.hpp @@ -0,0 +1,87 @@ +/// +/// EpubChapter.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include + +namespace margelo::nitro::nitroepub { + + /** + * A struct which can be represented as a JavaScript object (EpubChapter). + */ + struct EpubChapter final { + public: + std::string name SWIFT_PRIVATE; + std::string path SWIFT_PRIVATE; + + public: + EpubChapter() = default; + explicit EpubChapter(std::string name, std::string path): name(name), path(path) {} + + public: + friend bool operator==(const EpubChapter& lhs, const EpubChapter& rhs) = default; + }; + +} // namespace margelo::nitro::nitroepub + +namespace margelo::nitro { + + // C++ EpubChapter <> JS EpubChapter (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitroepub::EpubChapter fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::nitroepub::EpubChapter( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "name"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "path"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitroepub::EpubChapter& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "name"), JSIConverter::toJSI(runtime, arg.name)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "path"), JSIConverter::toJSI(runtime, arg.path)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "name")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "path")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-epub/nitrogen/generated/shared/c++/EpubExportChapter.hpp b/modules/nitro-epub/nitrogen/generated/shared/c++/EpubExportChapter.hpp new file mode 100644 index 000000000..898e4df4d --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/shared/c++/EpubExportChapter.hpp @@ -0,0 +1,95 @@ +/// +/// EpubExportChapter.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include + +namespace margelo::nitro::nitroepub { + + /** + * A struct which can be represented as a JavaScript object (EpubExportChapter). + */ + struct EpubExportChapter final { + public: + std::string title SWIFT_PRIVATE; + std::string htmlPath SWIFT_PRIVATE; + std::string novelId SWIFT_PRIVATE; + std::string chapterId SWIFT_PRIVATE; + + public: + EpubExportChapter() = default; + explicit EpubExportChapter(std::string title, std::string htmlPath, std::string novelId, std::string chapterId): title(title), htmlPath(htmlPath), novelId(novelId), chapterId(chapterId) {} + + public: + friend bool operator==(const EpubExportChapter& lhs, const EpubExportChapter& rhs) = default; + }; + +} // namespace margelo::nitro::nitroepub + +namespace margelo::nitro { + + // C++ EpubExportChapter <> JS EpubExportChapter (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitroepub::EpubExportChapter fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::nitroepub::EpubExportChapter( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "title"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "htmlPath"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "novelId"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "chapterId"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitroepub::EpubExportChapter& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "title"), JSIConverter::toJSI(runtime, arg.title)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "htmlPath"), JSIConverter::toJSI(runtime, arg.htmlPath)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "novelId"), JSIConverter::toJSI(runtime, arg.novelId)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "chapterId"), JSIConverter::toJSI(runtime, arg.chapterId)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "title")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "htmlPath")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "novelId")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "chapterId")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-epub/nitrogen/generated/shared/c++/EpubExportMetadata.hpp b/modules/nitro-epub/nitrogen/generated/shared/c++/EpubExportMetadata.hpp new file mode 100644 index 000000000..88d249647 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/shared/c++/EpubExportMetadata.hpp @@ -0,0 +1,111 @@ +/// +/// EpubExportMetadata.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include + +namespace margelo::nitro::nitroepub { + + /** + * A struct which can be represented as a JavaScript object (EpubExportMetadata). + */ + struct EpubExportMetadata final { + public: + std::string title SWIFT_PRIVATE; + std::string language SWIFT_PRIVATE; + std::string coverPath SWIFT_PRIVATE; + std::string description SWIFT_PRIVATE; + std::string author SWIFT_PRIVATE; + std::string bookId SWIFT_PRIVATE; + std::string stylesheet SWIFT_PRIVATE; + std::string javascript SWIFT_PRIVATE; + + public: + EpubExportMetadata() = default; + explicit EpubExportMetadata(std::string title, std::string language, std::string coverPath, std::string description, std::string author, std::string bookId, std::string stylesheet, std::string javascript): title(title), language(language), coverPath(coverPath), description(description), author(author), bookId(bookId), stylesheet(stylesheet), javascript(javascript) {} + + public: + friend bool operator==(const EpubExportMetadata& lhs, const EpubExportMetadata& rhs) = default; + }; + +} // namespace margelo::nitro::nitroepub + +namespace margelo::nitro { + + // C++ EpubExportMetadata <> JS EpubExportMetadata (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitroepub::EpubExportMetadata fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::nitroepub::EpubExportMetadata( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "title"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "language"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "coverPath"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "description"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "author"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bookId"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "stylesheet"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "javascript"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitroepub::EpubExportMetadata& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "title"), JSIConverter::toJSI(runtime, arg.title)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "language"), JSIConverter::toJSI(runtime, arg.language)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "coverPath"), JSIConverter::toJSI(runtime, arg.coverPath)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "description"), JSIConverter::toJSI(runtime, arg.description)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "author"), JSIConverter::toJSI(runtime, arg.author)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "bookId"), JSIConverter::toJSI(runtime, arg.bookId)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "stylesheet"), JSIConverter::toJSI(runtime, arg.stylesheet)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "javascript"), JSIConverter::toJSI(runtime, arg.javascript)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "title")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "language")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "coverPath")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "description")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "author")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "bookId")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "stylesheet")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "javascript")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-epub/nitrogen/generated/shared/c++/EpubExportResult.hpp b/modules/nitro-epub/nitrogen/generated/shared/c++/EpubExportResult.hpp new file mode 100644 index 000000000..e536a7295 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/shared/c++/EpubExportResult.hpp @@ -0,0 +1,87 @@ +/// +/// EpubExportResult.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include + +namespace margelo::nitro::nitroepub { + + /** + * A struct which can be represented as a JavaScript object (EpubExportResult). + */ + struct EpubExportResult final { + public: + std::string outputPath SWIFT_PRIVATE; + double chapterCount SWIFT_PRIVATE; + + public: + EpubExportResult() = default; + explicit EpubExportResult(std::string outputPath, double chapterCount): outputPath(outputPath), chapterCount(chapterCount) {} + + public: + friend bool operator==(const EpubExportResult& lhs, const EpubExportResult& rhs) = default; + }; + +} // namespace margelo::nitro::nitroepub + +namespace margelo::nitro { + + // C++ EpubExportResult <> JS EpubExportResult (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitroepub::EpubExportResult fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::nitroepub::EpubExportResult( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "outputPath"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "chapterCount"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitroepub::EpubExportResult& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "outputPath"), JSIConverter::toJSI(runtime, arg.outputPath)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "chapterCount"), JSIConverter::toJSI(runtime, arg.chapterCount)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "outputPath")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "chapterCount")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-epub/nitrogen/generated/shared/c++/EpubNovel.hpp b/modules/nitro-epub/nitrogen/generated/shared/c++/EpubNovel.hpp new file mode 100644 index 000000000..5b79d9bd2 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/shared/c++/EpubNovel.hpp @@ -0,0 +1,115 @@ +/// +/// EpubNovel.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `EpubChapter` to properly resolve imports. +namespace margelo::nitro::nitroepub { struct EpubChapter; } + +#include +#include +#include "EpubChapter.hpp" +#include + +namespace margelo::nitro::nitroepub { + + /** + * A struct which can be represented as a JavaScript object (EpubNovel). + */ + struct EpubNovel final { + public: + std::string name SWIFT_PRIVATE; + std::optional cover SWIFT_PRIVATE; + std::optional summary SWIFT_PRIVATE; + std::optional author SWIFT_PRIVATE; + std::optional artist SWIFT_PRIVATE; + std::vector chapters SWIFT_PRIVATE; + std::vector cssPaths SWIFT_PRIVATE; + std::vector imagePaths SWIFT_PRIVATE; + + public: + EpubNovel() = default; + explicit EpubNovel(std::string name, std::optional cover, std::optional summary, std::optional author, std::optional artist, std::vector chapters, std::vector cssPaths, std::vector imagePaths): name(name), cover(cover), summary(summary), author(author), artist(artist), chapters(chapters), cssPaths(cssPaths), imagePaths(imagePaths) {} + + public: + friend bool operator==(const EpubNovel& lhs, const EpubNovel& rhs) = default; + }; + +} // namespace margelo::nitro::nitroepub + +namespace margelo::nitro { + + // C++ EpubNovel <> JS EpubNovel (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitroepub::EpubNovel fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::nitroepub::EpubNovel( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "name"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cover"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "summary"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "author"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artist"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "chapters"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cssPaths"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "imagePaths"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitroepub::EpubNovel& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "name"), JSIConverter::toJSI(runtime, arg.name)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "cover"), JSIConverter>::toJSI(runtime, arg.cover)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "summary"), JSIConverter>::toJSI(runtime, arg.summary)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "author"), JSIConverter>::toJSI(runtime, arg.author)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "artist"), JSIConverter>::toJSI(runtime, arg.artist)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "chapters"), JSIConverter>::toJSI(runtime, arg.chapters)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "cssPaths"), JSIConverter>::toJSI(runtime, arg.cssPaths)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "imagePaths"), JSIConverter>::toJSI(runtime, arg.imagePaths)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "name")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cover")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "summary")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "author")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "artist")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "chapters")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cssPaths")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "imagePaths")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-epub/nitrogen/generated/shared/c++/HybridEpubSpec.cpp b/modules/nitro-epub/nitrogen/generated/shared/c++/HybridEpubSpec.cpp new file mode 100644 index 000000000..1d7fe42a2 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/shared/c++/HybridEpubSpec.cpp @@ -0,0 +1,22 @@ +/// +/// HybridEpubSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridEpubSpec.hpp" + +namespace margelo::nitro::nitroepub { + + void HybridEpubSpec::loadHybridMethods() { + // load base methods/properties + HybridObject::loadHybridMethods(); + // load custom methods/properties + registerHybrids(this, [](Prototype& prototype) { + prototype.registerHybridMethod("parseNovelAndChapters", &HybridEpubSpec::parseNovelAndChapters); + prototype.registerHybridMethod("exportEpub", &HybridEpubSpec::exportEpub); + }); + } + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/nitrogen/generated/shared/c++/HybridEpubSpec.hpp b/modules/nitro-epub/nitrogen/generated/shared/c++/HybridEpubSpec.hpp new file mode 100644 index 000000000..0bb4e0577 --- /dev/null +++ b/modules/nitro-epub/nitrogen/generated/shared/c++/HybridEpubSpec.hpp @@ -0,0 +1,77 @@ +/// +/// HybridEpubSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `EpubNovel` to properly resolve imports. +namespace margelo::nitro::nitroepub { struct EpubNovel; } +// Forward declaration of `EpubExportResult` to properly resolve imports. +namespace margelo::nitro::nitroepub { struct EpubExportResult; } +// Forward declaration of `EpubExportMetadata` to properly resolve imports. +namespace margelo::nitro::nitroepub { struct EpubExportMetadata; } +// Forward declaration of `EpubExportChapter` to properly resolve imports. +namespace margelo::nitro::nitroepub { struct EpubExportChapter; } + +#include "EpubNovel.hpp" +#include +#include +#include "EpubExportResult.hpp" +#include "EpubExportMetadata.hpp" +#include "EpubExportChapter.hpp" +#include +#include + +namespace margelo::nitro::nitroepub { + + using namespace margelo::nitro; + + /** + * An abstract base class for `Epub` + * Inherit this class to create instances of `HybridEpubSpec` in C++. + * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual. + * @example + * ```cpp + * class HybridEpub: public HybridEpubSpec { + * public: + * HybridEpub(...): HybridObject(TAG) { ... } + * // ... + * }; + * ``` + */ + class HybridEpubSpec: public virtual HybridObject { + public: + // Constructor + explicit HybridEpubSpec(): HybridObject(TAG) { } + + // Destructor + ~HybridEpubSpec() override = default; + + public: + // Properties + + + public: + // Methods + virtual std::shared_ptr> parseNovelAndChapters(const std::string& epubDirPath) = 0; + virtual std::shared_ptr> exportEpub(const EpubExportMetadata& metadata, const std::vector& chapters, const std::string& outputPath, const std::function>>>(double /* completedChapters */, double /* totalChapters */, const std::string& /* chapterTitle */)>& onProgress) = 0; + + protected: + // Hybrid Setup + void loadHybridMethods() override; + + protected: + // Tag for logging + static constexpr auto TAG = "Epub"; + }; + +} // namespace margelo::nitro::nitroepub diff --git a/modules/nitro-epub/package.json b/modules/nitro-epub/package.json new file mode 100644 index 000000000..21cf730f7 --- /dev/null +++ b/modules/nitro-epub/package.json @@ -0,0 +1,108 @@ +{ + "name": "nitro-epub", + "version": "0.0.1", + "description": "Nitro module for importing and exporting EPUB files", + "main": "lib/index", + "module": "lib/index", + "types": "lib/index.d.ts", + "react-native": "src/index", + "source": "src/index", + "files": [ + "src", + "react-native.config.js", + "lib", + "nitrogen", + "android/build.gradle", + "android/gradle.properties", + "android/fix-prefab.gradle", + "android/CMakeLists.txt", + "android/src", + "cpp", + "ios/**/*.h", + "ios/**/*.m", + "ios/**/*.mm", + "ios/**/*.cpp", + "ios/**/*.swift", + "app.plugin.js", + "nitro.json", + "*.podspec", + "README.md" + ], + "scripts": { + "typecheck": "tsc --noEmit", + "clean": "rm -rf android/build node_modules/**/android/build lib", + "lint": "eslint \"**/*.{js,ts,tsx}\" --fix", + "lint-ci": "eslint \"**/*.{js,ts,tsx}\" -f @jamesacarr/github-actions --max-warnings 0", + "typescript": "tsc", + "specs": "tsc --noEmit false && nitrogen --logLevel=\"debug\"" + }, + "keywords": [ + "react-native", + "nitro" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/LNReader/lnreader" + }, + "author": "LNReader", + "license": "MIT", + "bugs": { + "url": "https://github.com/LNReader/lnreader/issues" + }, + "homepage": "https://github.com/LNReader/lnreader#readme", + "publishConfig": { + "registry": "https://registry.npmjs.org/" + }, + "devDependencies": { + "@react-native/eslint-config": "0.85.3", + "@types/react": "^19.2.15", + "eslint": "^9.39.4", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.5", + "nitrogen": "*", + "prettier": "^3.8.3", + "react": "19.2.3", + "react-native": "0.85.3", + "react-native-nitro-modules": "*", + "typescript": "^6.0.3" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-nitro-modules": "*" + }, + "eslintConfig": { + "root": true, + "extends": [ + "@react-native", + "prettier" + ], + "plugins": [ + "prettier" + ], + "rules": { + "prettier/prettier": [ + "warn", + { + "quoteProps": "consistent", + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "useTabs": false + } + ] + } + }, + "eslintIgnore": [ + "node_modules/", + "lib/" + ], + "prettier": { + "quoteProps": "consistent", + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "useTabs": false, + "semi": false + } +} diff --git a/modules/nitro-epub/react-native.config.js b/modules/nitro-epub/react-native.config.js new file mode 100644 index 000000000..3fdf8eaad --- /dev/null +++ b/modules/nitro-epub/react-native.config.js @@ -0,0 +1,16 @@ +// https://github.com/react-native-community/cli/blob/main/docs/dependencies.md + +module.exports = { + dependency: { + platforms: { + /** + * @type {import('@react-native-community/cli-types').IOSDependencyParams} + */ + ios: {}, + /** + * @type {import('@react-native-community/cli-types').AndroidDependencyParams} + */ + android: {}, + }, + }, +} diff --git a/modules/nitro-epub/src/index.ts b/modules/nitro-epub/src/index.ts new file mode 100644 index 000000000..c2fa21b25 --- /dev/null +++ b/modules/nitro-epub/src/index.ts @@ -0,0 +1,11 @@ +import { NitroModules } from 'react-native-nitro-modules' +import type { Epub } from './specs/Epub.nitro' + +export const epub = NitroModules.createHybridObject('Epub') + +export type { Epub } from './specs/Epub.nitro' +export type { EpubChapter } from './types/EpubChapter' +export type { EpubExportChapter } from './types/EpubExportChapter' +export type { EpubExportMetadata } from './types/EpubExportMetadata' +export type { EpubExportResult } from './types/EpubExportResult' +export type { EpubNovel } from './types/EpubNovel' diff --git a/modules/nitro-epub/src/specs/Epub.nitro.ts b/modules/nitro-epub/src/specs/Epub.nitro.ts new file mode 100644 index 000000000..de67f76eb --- /dev/null +++ b/modules/nitro-epub/src/specs/Epub.nitro.ts @@ -0,0 +1,32 @@ +import type { HybridObject } from 'react-native-nitro-modules' +import type { EpubExportChapter } from '../types/EpubExportChapter' +import type { EpubExportMetadata } from '../types/EpubExportMetadata' +import type { EpubExportResult } from '../types/EpubExportResult' +import type { EpubNovel } from '../types/EpubNovel' + +/** + * Imports and exports EPUB publications using shared native C++ code. + * + * @see {@linkcode Epub.parseNovelAndChapters} + * @see {@linkcode Epub.exportEpub} + */ +export interface Epub extends HybridObject<{ android: 'c++'; ios: 'c++' }> { + /** + * Parses metadata and chapter paths from an extracted EPUB directory. + */ + parseNovelAndChapters(epubDirPath: string): Promise + + /** + * Creates an EPUB archive from downloaded chapter files. + */ + exportEpub( + metadata: EpubExportMetadata, + chapters: EpubExportChapter[], + outputPath: string, + onProgress: ( + completedChapters: number, + totalChapters: number, + chapterTitle: string + ) => Promise + ): Promise +} diff --git a/modules/nitro-epub/src/types/EpubChapter.ts b/modules/nitro-epub/src/types/EpubChapter.ts new file mode 100644 index 000000000..65e3bbd0e --- /dev/null +++ b/modules/nitro-epub/src/types/EpubChapter.ts @@ -0,0 +1,11 @@ +/** + * Identifies a chapter discovered while parsing an EPUB publication. + * + * @see {@linkcode EpubChapter.path} + */ +export interface EpubChapter { + /** Chapter title presented to the reader. */ + name: string + /** Absolute path to the extracted chapter document. */ + path: string +} diff --git a/modules/nitro-epub/src/types/EpubExportChapter.ts b/modules/nitro-epub/src/types/EpubExportChapter.ts new file mode 100644 index 000000000..05f4fd1f2 --- /dev/null +++ b/modules/nitro-epub/src/types/EpubExportChapter.ts @@ -0,0 +1,15 @@ +/** + * A downloaded chapter included in an exported EPUB publication. + * + * @see {@linkcode EpubExportChapter.htmlPath} + */ +export interface EpubExportChapter { + /** Chapter title displayed in the table of contents. */ + title: string + /** Absolute path to the downloaded chapter HTML file. */ + htmlPath: string + /** LNReader novel identifier exposed to optional chapter JavaScript. */ + novelId: string + /** LNReader chapter identifier exposed to optional chapter JavaScript. */ + chapterId: string +} diff --git a/modules/nitro-epub/src/types/EpubExportMetadata.ts b/modules/nitro-epub/src/types/EpubExportMetadata.ts new file mode 100644 index 000000000..77a54d668 --- /dev/null +++ b/modules/nitro-epub/src/types/EpubExportMetadata.ts @@ -0,0 +1,23 @@ +/** + * Publication metadata and optional reader assets used to create an EPUB. + * + * @see {@linkcode EpubExportMetadata.bookId} + */ +export interface EpubExportMetadata { + /** Publication title. */ + title: string + /** BCP 47 language tag written to the EPUB package. */ + language: string + /** Local cover image path or file URI, or an empty string for no cover. */ + coverPath: string + /** Publication description. */ + description: string + /** Publication author. */ + author: string + /** Stable unique identifier for the publication. */ + bookId: string + /** CSS included as the publication stylesheet. */ + stylesheet: string + /** JavaScript executed when each chapter loads, or an empty string. */ + javascript: string +} diff --git a/modules/nitro-epub/src/types/EpubExportResult.ts b/modules/nitro-epub/src/types/EpubExportResult.ts new file mode 100644 index 000000000..c78f3d21e --- /dev/null +++ b/modules/nitro-epub/src/types/EpubExportResult.ts @@ -0,0 +1,11 @@ +/** + * Describes a successfully created EPUB archive. + * + * @see {@linkcode EpubExportResult.outputPath} + */ +export interface EpubExportResult { + /** Absolute path to the completed EPUB archive. */ + outputPath: string + /** Number of downloaded chapters written to the archive. */ + chapterCount: number +} diff --git a/modules/nitro-epub/src/types/EpubNovel.ts b/modules/nitro-epub/src/types/EpubNovel.ts new file mode 100644 index 000000000..7b0448340 --- /dev/null +++ b/modules/nitro-epub/src/types/EpubNovel.ts @@ -0,0 +1,25 @@ +import type { EpubChapter } from './EpubChapter' + +/** + * Metadata and local resources discovered in an extracted EPUB publication. + * + * @see {@linkcode EpubChapter} + */ +export interface EpubNovel { + /** Publication title. */ + name: string + /** Absolute path to the extracted cover image, when present. */ + cover?: string + /** Publication description, when present. */ + summary?: string + /** Publication author, when present. */ + author?: string + /** Publication illustrator or artist, when present. */ + artist?: string + /** Chapters in reading order. */ + chapters: EpubChapter[] + /** Absolute paths to extracted stylesheets. */ + cssPaths: string[] + /** Absolute paths to extracted images. */ + imagePaths: string[] +} diff --git a/modules/nitro-epub/tsconfig.json b/modules/nitro-epub/tsconfig.json new file mode 100644 index 000000000..0f5d47020 --- /dev/null +++ b/modules/nitro-epub/tsconfig.json @@ -0,0 +1,29 @@ +{ + "include": [ + "src" + ], + "compilerOptions": { + "composite": true, + "outDir": "lib", + "rootDir": "src", + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "lib": ["esnext"], + "module": "esnext", + "moduleResolution": "bundler", + "noEmit": false, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "esnext", + "verbatimModuleSyntax": true + } +} diff --git a/modules/nitro-tts/.watchmanconfig b/modules/nitro-tts/.watchmanconfig new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/modules/nitro-tts/.watchmanconfig @@ -0,0 +1 @@ +{} diff --git a/modules/nitro-tts/NitroTts.podspec b/modules/nitro-tts/NitroTts.podspec new file mode 100644 index 000000000..11a891c99 --- /dev/null +++ b/modules/nitro-tts/NitroTts.podspec @@ -0,0 +1,21 @@ +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "package.json"))) + +Pod::Spec.new do |s| + s.name = "NitroTts" + s.version = package["version"] + s.summary = package["description"] + s.homepage = package["homepage"] + s.license = package["license"] + s.authors = package["author"] + + s.platforms = { :ios => min_ios_version_supported } + s.source = { :git => "https://github.com/LNReader/lnreader.git", :tag => "#{s.version}" } + s.source_files = ["ios/**/*.{swift}"] + + load "nitrogen/generated/ios/NitroTts+autolinking.rb" + add_nitrogen_files(s) + + install_modules_dependencies(s) +end diff --git a/modules/nitro-tts/android/CMakeLists.txt b/modules/nitro-tts/android/CMakeLists.txt new file mode 100644 index 000000000..f87cb9195 --- /dev/null +++ b/modules/nitro-tts/android/CMakeLists.txt @@ -0,0 +1,24 @@ +project(NitroTts) +cmake_minimum_required(VERSION 3.9.0) + +set(PACKAGE_NAME NitroTts) +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_CXX_STANDARD 20) + +add_library(${PACKAGE_NAME} SHARED + src/main/cpp/cpp-adapter.cpp +) + +include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroTts+autolinking.cmake) + +include_directories( + "src/main/cpp" +) + +find_library(LOG_LIB log) + +target_link_libraries( + ${PACKAGE_NAME} + ${LOG_LIB} + android +) diff --git a/modules/nitro-tts/android/build.gradle b/modules/nitro-tts/android/build.gradle new file mode 100644 index 000000000..1d330abef --- /dev/null +++ b/modules/nitro-tts/android/build.gradle @@ -0,0 +1,97 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath "com.android.tools.build:gradle:9.2.1" + } +} + +def reactNativeArchitectures() { + def value = rootProject.getProperties().get("reactNativeArchitectures") + return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] +} + +def isNewArchitectureEnabled() { + return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" +} + +apply plugin: "com.android.library" +apply plugin: "org.jetbrains.kotlin.android" +apply from: "../nitrogen/generated/android/NitroTts+autolinking.gradle" +apply from: "./fix-prefab.gradle" + +if (isNewArchitectureEnabled()) { + apply plugin: "com.facebook.react" +} + +def getExtOrDefault(name) { + return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["NitroTts_" + name] +} + +def getExtOrIntegerDefault(name) { + return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroTts_" + name]).toInteger() +} + +android { + namespace "com.margelo.nitro.nitrotts" + + ndkVersion getExtOrDefault("ndkVersion") + compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") + + defaultConfig { + minSdkVersion getExtOrIntegerDefault("minSdkVersion") + targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() + + externalNativeBuild { + cmake { + cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all" + arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON" + abiFilters (*reactNativeArchitectures()) + } + } + } + + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } + + packagingOptions { + excludes = [ + "META-INF", + "META-INF/**", + "**/libc++_shared.so", + "**/libNitroModules.so", + "**/libfbjni.so", + "**/libjsi.so", + "**/libreactnative.so" + ] + } + + buildFeatures { + buildConfig true + prefab true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +repositories { + google() + mavenCentral() +} + +dependencies { + implementation "com.facebook.react:react-native:+" + implementation project(":react-native-nitro-modules") + implementation "androidx.core:core-ktx:1.17.0" + implementation "androidx.media:media:1.7.1" +} diff --git a/modules/nitro-tts/android/fix-prefab.gradle b/modules/nitro-tts/android/fix-prefab.gradle new file mode 100644 index 000000000..6f845a359 --- /dev/null +++ b/modules/nitro-tts/android/fix-prefab.gradle @@ -0,0 +1,44 @@ +tasks.configureEach { task -> + def prefabConfigurePattern = ~/^prefab(.+)ConfigurePackage$/ + def matcher = task.name =~ prefabConfigurePattern + if (matcher.matches()) { + def variantName = matcher[0][1] + task.outputs.upToDateWhen { false } + task.dependsOn("externalNativeBuild${variantName}") + } +} + +afterEvaluate { + def abis = reactNativeArchitectures() + rootProject.allprojects.each { proj -> + if (proj === rootProject) return + + def dependsOnThisLib = proj.configurations.findAll { it.canBeResolved }.any { config -> + config.dependencies.any { dep -> + dep.group == project.group && dep.name == project.name + } + } + if (!dependsOnThisLib && proj != project) return + + if (!proj.plugins.hasPlugin('com.android.application') && !proj.plugins.hasPlugin('com.android.library')) { + return + } + + def variants = proj.android.hasProperty('applicationVariants') ? proj.android.applicationVariants : proj.android.libraryVariants + variants.all { variant -> + def variantName = variant.name + abis.each { abi -> + def searchDir = new File(proj.projectDir, ".cxx/${variantName}") + if (!searchDir.exists()) return + def matches = [] + searchDir.eachDir { randomDir -> + def prefabFile = new File(randomDir, "${abi}/prefab_config.json") + if (prefabFile.exists()) matches << prefabFile + } + matches.each { prefabConfig -> + prefabConfig.setLastModified(System.currentTimeMillis()) + } + } + } + } +} diff --git a/modules/nitro-tts/android/gradle.properties b/modules/nitro-tts/android/gradle.properties new file mode 100644 index 000000000..c2eb7daaf --- /dev/null +++ b/modules/nitro-tts/android/gradle.properties @@ -0,0 +1,5 @@ +NitroTts_kotlinVersion=2.1.21 +NitroTts_minSdkVersion=23 +NitroTts_targetSdkVersion=36 +NitroTts_compileSdkVersion=36 +NitroTts_ndkVersion=29.0.14206865 diff --git a/modules/nitro-tts/android/src/main/AndroidManifest.xml b/modules/nitro-tts/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..351ac0dcc --- /dev/null +++ b/modules/nitro-tts/android/src/main/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/modules/nitro-tts/android/src/main/cpp/cpp-adapter.cpp b/modules/nitro-tts/android/src/main/cpp/cpp-adapter.cpp new file mode 100644 index 000000000..10d6afb00 --- /dev/null +++ b/modules/nitro-tts/android/src/main/cpp/cpp-adapter.cpp @@ -0,0 +1,9 @@ +#include +#include +#include "NitroTtsOnLoad.hpp" + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { + return facebook::jni::initialize(vm, []() { + margelo::nitro::nitrotts::registerAllNatives(); + }); +} diff --git a/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/HybridTtsFactory.kt b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/HybridTtsFactory.kt new file mode 100644 index 000000000..ac7ea37f6 --- /dev/null +++ b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/HybridTtsFactory.kt @@ -0,0 +1,42 @@ +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.NitroModules +import com.margelo.nitro.core.Promise + +@Keep +@DoNotStrip +final class HybridTtsFactory : HybridTtsFactorySpec() { + private val context + get() = NitroModules.applicationContext + ?: error("Nitro Modules has no Android application context.") + + override fun createSession(): Promise { + val promise = Promise() + TtsPlaybackStore.prepare(context) { result -> + result.fold( + onSuccess = { promise.resolve(HybridTtsSession()) }, + onFailure = { promise.reject(it) }, + ) + } + return promise + } + + override fun getEngines(): Promise> { + val promise = Promise>() + promise.resolve(TtsPlaybackStore.listEngines(context).toTypedArray()) + return promise + } + + override fun getVoices(engineName: String?): Promise> { + val promise = Promise>() + TtsPlaybackStore.listVoices(context, engineName) { result -> + result.fold( + onSuccess = { promise.resolve(it.toTypedArray()) }, + onFailure = { promise.reject(it) }, + ) + } + return promise + } +} diff --git a/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/HybridTtsSession.kt b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/HybridTtsSession.kt new file mode 100644 index 000000000..5904457f7 --- /dev/null +++ b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/HybridTtsSession.kt @@ -0,0 +1,67 @@ +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.core.Promise + +@Keep +@DoNotStrip +final class HybridTtsSession : HybridTtsSessionSpec() { + override fun load( + paragraphs: Array, + initialIndex: Double, + metadata: TtsMetadata, + settings: TtsSettings, + ): Promise { + return MainThreadPromise.run { + TtsPlaybackStore.load( + paragraphs, + initialIndex.toInt(), + metadata, + settings, + ) + } + } + + override fun play(): Promise = + MainThreadPromise.run(TtsPlaybackStore::play) + + override fun pause(): Promise = + MainThreadPromise.run(TtsPlaybackStore::pause) + + override fun stop(): Promise = + MainThreadPromise.run(TtsPlaybackStore::stop) + + override fun skipPrevious(): Promise = + MainThreadPromise.run(TtsPlaybackStore::skipPrevious) + + override fun skipNext(): Promise = + MainThreadPromise.run(TtsPlaybackStore::skipNext) + + override fun replayCurrent(): Promise = + MainThreadPromise.run(TtsPlaybackStore::replayCurrent) + + override fun seekTo(index: Double): Promise = + MainThreadPromise.run { TtsPlaybackStore.seekTo(index.toInt()) } + + override fun updateSettings(settings: TtsSettings): Promise = + MainThreadPromise.run { TtsPlaybackStore.updateSettings(settings) } + + override fun addOnStateChangedListener( + listener: (state: TtsPlaybackState) -> Unit, + ): ListenerSubscription { + return ListenerSubscription(TtsPlaybackStore.addStateListener(listener)) + } + + override fun addOnProgressChangedListener( + listener: (progress: TtsProgress) -> Unit, + ): ListenerSubscription { + return ListenerSubscription(TtsPlaybackStore.addProgressListener(listener)) + } + + override fun addOnErrorListener( + listener: (message: String) -> Unit, + ): ListenerSubscription { + return ListenerSubscription(TtsPlaybackStore.addErrorListener(listener)) + } +} diff --git a/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/MainThreadPromise.kt b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/MainThreadPromise.kt new file mode 100644 index 000000000..16c9836b2 --- /dev/null +++ b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/MainThreadPromise.kt @@ -0,0 +1,22 @@ +package com.margelo.nitro.nitrotts + +import android.os.Handler +import android.os.Looper +import com.margelo.nitro.core.Promise + +internal object MainThreadPromise { + private val handler = Handler(Looper.getMainLooper()) + + fun run(operation: () -> Unit): Promise { + val promise = Promise() + handler.post { + try { + operation() + promise.resolve(Unit) + } catch (error: Throwable) { + promise.reject(error) + } + } + return promise + } +} diff --git a/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/NitroTtsPackage.kt b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/NitroTtsPackage.kt new file mode 100644 index 000000000..12624c053 --- /dev/null +++ b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/NitroTtsPackage.kt @@ -0,0 +1,22 @@ +package com.margelo.nitro.nitrotts + +import com.facebook.react.BaseReactPackage +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.module.model.ReactModuleInfoProvider + +class NitroTtsPackage : BaseReactPackage() { + override fun getModule( + name: String, + reactContext: ReactApplicationContext, + ): NativeModule? = null + + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = + ReactModuleInfoProvider { HashMap() } + + companion object { + init { + NitroTtsOnLoad.initializeNative() + } + } +} diff --git a/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsListenerRegistry.kt b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsListenerRegistry.kt new file mode 100644 index 000000000..b8fed1b80 --- /dev/null +++ b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsListenerRegistry.kt @@ -0,0 +1,19 @@ +package com.margelo.nitro.nitrotts + +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.atomic.AtomicLong + +internal class TtsListenerRegistry { + private val nextId = AtomicLong(0L) + private val listeners = ConcurrentHashMap Unit>() + + fun add(listener: (Value) -> Unit): () -> Unit { + val id = nextId.incrementAndGet() + listeners[id] = listener + return { listeners.remove(id) } + } + + fun emit(value: Value) { + listeners.values.toList().forEach { it(value) } + } +} diff --git a/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsMediaNotification.kt b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsMediaNotification.kt new file mode 100644 index 000000000..00885e98e --- /dev/null +++ b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsMediaNotification.kt @@ -0,0 +1,193 @@ +package com.margelo.nitro.nitrotts + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.os.Build +import android.support.v4.media.MediaMetadataCompat +import android.support.v4.media.session.MediaSessionCompat +import android.support.v4.media.session.PlaybackStateCompat +import androidx.core.app.NotificationCompat +import androidx.media.app.NotificationCompat.MediaStyle + +internal class TtsMediaNotification( + private val context: Context, +) { + private val manager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + private val mediaSession = MediaSessionCompat(context, "LNReaderTTS") + + init { + ensureChannel() + mediaSession.setCallback(TtsMediaSessionCallback()) + mediaSession.isActive = true + } + + fun build(snapshot: TtsPlaybackSnapshot): Notification { + updateMediaSession(snapshot) + + val isPlaying = snapshot.state == TtsPlaybackState.PLAYING + val progressLabel = paragraphProgressLabel(snapshot.progress) + val playPauseAction = if (isPlaying) { + action( + android.R.drawable.ic_media_pause, + "Pause", + TtsPlaybackService.ACTION_PAUSE, + ) + } else { + action( + android.R.drawable.ic_media_play, + "Play", + TtsPlaybackService.ACTION_PLAY, + ) + } + + return NotificationCompat.Builder(context, CHANNEL_ID) + .setContentTitle(snapshot.metadata?.chapterName ?: "Text to speech") + .setContentText(progressLabel ?: snapshot.metadata?.novelName ?: "LNReader") + .setSubText(snapshot.metadata?.novelName) + .setSmallIcon(context.applicationInfo.icon) + .setContentIntent(contentIntent()) + .setDeleteIntent(serviceIntent(TtsPlaybackService.ACTION_STOP)) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setOnlyAlertOnce(true) + .setOngoing(isPlaying) + .addAction( + action( + android.R.drawable.ic_media_previous, + "Previous paragraph", + TtsPlaybackService.ACTION_PREVIOUS, + ), + ) + .addAction(playPauseAction) + .addAction( + action( + android.R.drawable.ic_media_next, + "Next paragraph", + TtsPlaybackService.ACTION_NEXT, + ), + ) + .setStyle( + MediaStyle() + .setMediaSession(mediaSession.sessionToken) + .setShowActionsInCompactView(0, 1, 2), + ) + .build() + } + + fun notify(snapshot: TtsPlaybackSnapshot) { + manager.notify(TtsPlaybackService.NOTIFICATION_ID, build(snapshot)) + } + + fun release() { + mediaSession.isActive = false + mediaSession.release() + manager.cancel(TtsPlaybackService.NOTIFICATION_ID) + } + + private fun updateMediaSession(snapshot: TtsPlaybackSnapshot) { + val progress = snapshot.progress + val playbackState = when (snapshot.state) { + TtsPlaybackState.PLAYING -> PlaybackStateCompat.STATE_PLAYING + TtsPlaybackState.PAUSED -> PlaybackStateCompat.STATE_PAUSED + TtsPlaybackState.ERROR -> PlaybackStateCompat.STATE_ERROR + TtsPlaybackState.COMPLETED -> PlaybackStateCompat.STATE_STOPPED + TtsPlaybackState.LOADING -> PlaybackStateCompat.STATE_BUFFERING + TtsPlaybackState.IDLE -> PlaybackStateCompat.STATE_NONE + } + + mediaSession.setPlaybackState( + PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_PLAY or + PlaybackStateCompat.ACTION_PAUSE or + PlaybackStateCompat.ACTION_STOP or + PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or + PlaybackStateCompat.ACTION_SKIP_TO_NEXT, + ) + .setState( + playbackState, + PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, + if (snapshot.state == TtsPlaybackState.PLAYING) 1f else 0f, + ) + .build(), + ) + + mediaSession.setMetadata( + MediaMetadataCompat.Builder() + .putString( + MediaMetadataCompat.METADATA_KEY_TITLE, + snapshot.metadata?.chapterName ?: "", + ) + .putString( + MediaMetadataCompat.METADATA_KEY_ARTIST, + snapshot.metadata?.novelName ?: "", + ) + .putString( + MediaMetadataCompat.METADATA_KEY_ALBUM, + paragraphProgressLabel(progress) ?: "", + ) + .build(), + ) + } + + private fun paragraphProgressLabel(progress: TtsProgress?): String? { + val total = progress?.total?.toInt() ?: return null + if (total <= 0) { + return null + } + val current = progress.index.toInt().coerceIn(0, total - 1) + 1 + return "Paragraph $current of $total" + } + + private fun action(icon: Int, title: String, action: String): NotificationCompat.Action { + return NotificationCompat.Action.Builder( + icon, + title, + serviceIntent(action), + ).build() + } + + private fun serviceIntent(action: String): PendingIntent { + val intent = Intent(context, TtsPlaybackService::class.java).setAction(action) + return PendingIntent.getService( + context, + action.hashCode(), + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, + ) + } + + private fun contentIntent(): PendingIntent { + val intent = context.packageManager.getLaunchIntentForPackage(context.packageName) + ?: Intent() + return PendingIntent.getActivity( + context, + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, + ) + } + + private fun ensureChannel() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + return + } + val channel = NotificationChannel( + CHANNEL_ID, + "TTS Media Controls", + NotificationManager.IMPORTANCE_LOW, + ).apply { + description = "Text-to-speech playback controls" + setShowBadge(false) + } + manager.createNotificationChannel(channel) + } + + companion object { + private const val CHANNEL_ID = "tts-media-controls" + } +} diff --git a/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsMediaSessionCallback.kt b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsMediaSessionCallback.kt new file mode 100644 index 000000000..12c1271c5 --- /dev/null +++ b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsMediaSessionCallback.kt @@ -0,0 +1,25 @@ +package com.margelo.nitro.nitrotts + +import android.support.v4.media.session.MediaSessionCompat + +internal class TtsMediaSessionCallback : MediaSessionCompat.Callback() { + override fun onPlay() { + TtsPlaybackStore.play() + } + + override fun onPause() { + TtsPlaybackStore.pause() + } + + override fun onStop() { + TtsPlaybackStore.stop() + } + + override fun onSkipToPrevious() { + TtsPlaybackStore.skipPrevious() + } + + override fun onSkipToNext() { + TtsPlaybackStore.skipNext() + } +} diff --git a/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsPlaybackService.kt b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsPlaybackService.kt new file mode 100644 index 000000000..7f41c2628 --- /dev/null +++ b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsPlaybackService.kt @@ -0,0 +1,71 @@ +package com.margelo.nitro.nitrotts + +import android.app.Service +import android.content.Context +import android.content.Intent +import android.content.pm.ServiceInfo +import android.os.Build +import android.os.IBinder +import androidx.core.content.ContextCompat + +internal class TtsPlaybackService : Service() { + private lateinit var mediaNotification: TtsMediaNotification + private var removeSnapshotListener: (() -> Unit)? = null + + override fun onCreate() { + super.onCreate() + mediaNotification = TtsMediaNotification(this) + val snapshot = TtsPlaybackStore.snapshot() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + startForeground( + NOTIFICATION_ID, + mediaNotification.build(snapshot), + ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, + ) + } else { + startForeground(NOTIFICATION_ID, mediaNotification.build(snapshot)) + } + removeSnapshotListener = TtsPlaybackStore.addSnapshotListener( + mediaNotification::notify, + ) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + when (intent?.action) { + ACTION_PLAY -> TtsPlaybackStore.play() + ACTION_PAUSE -> TtsPlaybackStore.pause() + ACTION_STOP -> TtsPlaybackStore.stop() + ACTION_PREVIOUS -> TtsPlaybackStore.skipPrevious() + ACTION_NEXT -> TtsPlaybackStore.skipNext() + } + return START_NOT_STICKY + } + + override fun onDestroy() { + removeSnapshotListener?.invoke() + removeSnapshotListener = null + mediaNotification.release() + super.onDestroy() + } + + override fun onBind(intent: Intent?): IBinder? = null + + companion object { + internal const val NOTIFICATION_ID = 1001 + internal const val ACTION_PLAY = "com.lnreader.TTS_PLAY" + internal const val ACTION_PAUSE = "com.lnreader.TTS_PAUSE" + internal const val ACTION_STOP = "com.lnreader.TTS_STOP" + internal const val ACTION_PREVIOUS = "com.lnreader.TTS_PREVIOUS" + internal const val ACTION_NEXT = "com.lnreader.TTS_NEXT" + + fun start(context: Context) { + val intent = Intent(context, TtsPlaybackService::class.java) + ContextCompat.startForegroundService(context, intent) + } + + fun stop(context: Context) { + context.stopService(Intent(context, TtsPlaybackService::class.java)) + } + + } +} diff --git a/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsPlaybackSnapshot.kt b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsPlaybackSnapshot.kt new file mode 100644 index 000000000..6b78eb063 --- /dev/null +++ b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsPlaybackSnapshot.kt @@ -0,0 +1,7 @@ +package com.margelo.nitro.nitrotts + +internal data class TtsPlaybackSnapshot( + val state: TtsPlaybackState, + val metadata: TtsMetadata?, + val progress: TtsProgress?, +) diff --git a/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsPlaybackStore.kt b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsPlaybackStore.kt new file mode 100644 index 000000000..816f3124e --- /dev/null +++ b/modules/nitro-tts/android/src/main/java/com/margelo/nitro/nitrotts/TtsPlaybackStore.kt @@ -0,0 +1,395 @@ +package com.margelo.nitro.nitrotts + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.speech.tts.TextToSpeech +import android.speech.tts.UtteranceProgressListener +import java.util.Locale + +internal object TtsPlaybackStore { + private val ownerHandler = Handler(Looper.getMainLooper()) + private val stateListeners = TtsListenerRegistry() + private val progressListeners = TtsListenerRegistry() + private val errorListeners = TtsListenerRegistry() + private val snapshotListeners = TtsListenerRegistry() + private val pendingInitialization = mutableListOf<(Result) -> Unit>() + + private var applicationContext: Context? = null + private var engine: TextToSpeech? = null + private var boundEngineName: String? = null + private var isReady = false + private var paragraphs: List = emptyList() + private var currentIndex = 0 + private var metadata: TtsMetadata? = null + private var settings = TtsSettings(null, null, 1.0, 1.0) + private var state = TtsPlaybackState.IDLE + private var generation = 0L + + fun prepare(context: Context, completion: (Result) -> Unit) { + runOnOwner { + applicationContext = context.applicationContext + if (isReady && boundEngineName == settings.engineName) { + completion(Result.success(Unit)) + return@runOnOwner + } + + pendingInitialization.add(completion) + bindEngine(settings.engineName) + } + } + + /** Lists text-to-speech engines installed on the device. */ + fun listEngines(context: Context): List { + val packageManager = context.packageManager + val intent = Intent(TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE) + val resolveInfos = packageManager.queryIntentServices( + intent, + PackageManager.GET_META_DATA, + ) + return resolveInfos + .map { resolveInfo -> + TtsEngine( + name = resolveInfo.serviceInfo.packageName, + label = resolveInfo.serviceInfo.loadLabel(packageManager).toString(), + ) + } + .distinctBy { it.name } + .sortedBy { it.label.lowercase() } + } + + /** Lists voices offered by `engineName`, probing it independently of the active engine. */ + fun listVoices( + context: Context, + engineName: String?, + completion: (Result>) -> Unit, + ) { + runOnOwner { + var probe: TextToSpeech? = null + val listener = TextToSpeech.OnInitListener { status -> + runOnOwner { + val result = if (status == TextToSpeech.SUCCESS) { + val voices = probe?.voices + ?.map { voice -> + TtsVoice( + identifier = voice.name, + name = voice.name, + language = voice.locale?.toLanguageTag(), + ) + } + ?.sortedBy { it.name } + ?: emptyList() + Result.success(voices) + } else { + Result.failure( + IllegalStateException("The selected text-to-speech engine failed to initialize."), + ) + } + probe?.shutdown() + probe = null + completion(result) + } + } + probe = if (engineName != null) { + TextToSpeech(context.applicationContext, listener, engineName) + } else { + TextToSpeech(context.applicationContext, listener) + } + } + } + + /** (Re)binds [engine] to [engineName], shutting down any previously bound engine. */ + private fun bindEngine(engineName: String?) { + val context = checkNotNull(applicationContext) + isReady = false + engine?.stop() + engine?.shutdown() + engine = null + + val listener = TextToSpeech.OnInitListener { status -> + runOnOwner { + if (status == TextToSpeech.SUCCESS) { + isReady = true + boundEngineName = engineName + engine?.setOnUtteranceProgressListener(progressListener) + completeInitialization(Result.success(Unit)) + } else { + engine = null + completeInitialization( + Result.failure( + IllegalStateException("The selected text-to-speech engine failed to initialize."), + ), + ) + } + } + } + engine = if (engineName != null) { + TextToSpeech(context, listener, engineName) + } else { + TextToSpeech(context, listener) + } + } + + fun load( + nextParagraphs: Array, + initialIndex: Int, + nextMetadata: TtsMetadata, + nextSettings: TtsSettings, + ) { + requireReady() + require(nextParagraphs.isNotEmpty()) { "The TTS queue cannot be empty." } + + generation += 1 + engine?.stop() + paragraphs = nextParagraphs.filter { it.text.isNotBlank() } + require(paragraphs.isNotEmpty()) { "The TTS queue contains no readable paragraphs." } + currentIndex = initialIndex.coerceIn(paragraphs.indices) + metadata = nextMetadata + settings = nextSettings + state = TtsPlaybackState.PAUSED + emitProgress() + emitState() + + val context = checkNotNull(applicationContext) + TtsPlaybackService.start(context) + } + + fun play() { + requireReady() + check(paragraphs.isNotEmpty()) { "Load a paragraph queue before starting TTS." } + speakCurrent() + } + + fun pause() { + if (state != TtsPlaybackState.PLAYING) { + return + } + generation += 1 + engine?.stop() + state = TtsPlaybackState.PAUSED + emitState() + } + + fun stop() { + generation += 1 + engine?.stop() + paragraphs = emptyList() + currentIndex = 0 + metadata = null + state = TtsPlaybackState.IDLE + emitState() + applicationContext?.let { TtsPlaybackService.stop(it) } + } + + fun skipPrevious() { + check(paragraphs.isNotEmpty()) { "Load a paragraph queue before seeking." } + currentIndex = (currentIndex - 1).coerceAtLeast(0) + speakCurrent() + } + + fun skipNext() { + check(paragraphs.isNotEmpty()) { "Load a paragraph queue before seeking." } + if (currentIndex >= paragraphs.lastIndex) { + completeQueue() + return + } + currentIndex += 1 + speakCurrent() + } + + fun replayCurrent() { + check(paragraphs.isNotEmpty()) { "Load a paragraph queue before replaying." } + speakCurrent() + } + + fun seekTo(index: Int) { + check(paragraphs.isNotEmpty()) { "Load a paragraph queue before seeking." } + currentIndex = index.coerceIn(paragraphs.indices) + speakCurrent() + } + + fun updateSettings(nextSettings: TtsSettings) { + requireReady() + val shouldResume = state == TtsPlaybackState.PLAYING + settings = nextSettings + if (shouldResume) { + speakCurrent() + } + } + + fun addStateListener(listener: (TtsPlaybackState) -> Unit): () -> Unit { + runOnOwner { listener(state) } + return stateListeners.add(listener) + } + + fun addProgressListener(listener: (TtsProgress) -> Unit): () -> Unit { + runOnOwner { currentProgress()?.let(listener) } + return progressListeners.add(listener) + } + + fun addErrorListener(listener: (String) -> Unit): () -> Unit { + return errorListeners.add(listener) + } + + fun addSnapshotListener(listener: (TtsPlaybackSnapshot) -> Unit): () -> Unit { + listener(snapshot()) + return snapshotListeners.add(listener) + } + + fun snapshot(): TtsPlaybackSnapshot { + return TtsPlaybackSnapshot(state, metadata, currentProgress()) + } + + private fun speakCurrent() { + if (engine == null || settings.engineName != boundEngineName) { + pendingInitialization.add { result -> + result.fold( + onSuccess = { speakCurrent() }, + onFailure = { fail("The selected text-to-speech engine failed to initialize.") }, + ) + } + bindEngine(settings.engineName) + return + } + + val activeEngine = checkNotNull(engine) + val paragraph = paragraphs[currentIndex] + generation += 1 + val utteranceId = utteranceId(generation, currentIndex) + + activeEngine.setSpeechRate(settings.rate.toFloat().coerceIn(0.1f, 4.0f)) + activeEngine.setPitch(settings.pitch.toFloat().coerceIn(0.1f, 2.0f)) + applyVoice(activeEngine) + + val result = activeEngine.speak( + paragraph.text, + TextToSpeech.QUEUE_FLUSH, + Bundle(), + utteranceId, + ) + if (result == TextToSpeech.ERROR) { + fail("The text-to-speech engine rejected the current paragraph.") + return + } + + state = TtsPlaybackState.PLAYING + emitProgress() + emitState() + } + + private fun applyVoice(activeEngine: TextToSpeech) { + val voiceIdentifier = settings.voiceIdentifier + if (voiceIdentifier.isNullOrBlank()) { + activeEngine.setLanguage(Locale.getDefault()) + return + } + + val selectedVoice = activeEngine.voices?.firstOrNull { + it.name == voiceIdentifier + } + if (selectedVoice != null) { + activeEngine.voice = selectedVoice + } + } + + private fun advance(utteranceId: String) { + if (utteranceId != utteranceId(generation, currentIndex)) { + return + } + if (currentIndex >= paragraphs.lastIndex) { + completeQueue() + return + } + currentIndex += 1 + speakCurrent() + } + + private fun completeQueue() { + state = TtsPlaybackState.COMPLETED + emitState() + applicationContext?.let { TtsPlaybackService.stop(it) } + } + + private fun fail(message: String) { + state = TtsPlaybackState.ERROR + errorListeners.emit(message) + emitState() + } + + private fun emitState() { + stateListeners.emit(state) + snapshotListeners.emit(snapshot()) + } + + private fun emitProgress() { + val progress = currentProgress() ?: return + progressListeners.emit(progress) + snapshotListeners.emit(snapshot()) + } + + private fun currentProgress(): TtsProgress? { + val paragraph = paragraphs.getOrNull(currentIndex) ?: return null + return TtsProgress( + index = currentIndex.toDouble(), + total = paragraphs.size.toDouble(), + paragraphId = paragraph.id, + ) + } + + private fun utteranceId(queueGeneration: Long, index: Int): String { + return "lnreader-$queueGeneration-$index" + } + + private fun completeInitialization(result: Result) { + val callbacks = pendingInitialization.toList() + pendingInitialization.clear() + callbacks.forEach { it(result) } + } + + private fun requireReady() { + check(isReady) { "The text-to-speech engine is not ready." } + } + + private fun runOnOwner(operation: () -> Unit) { + if (Looper.myLooper() == Looper.getMainLooper()) { + operation() + } else { + ownerHandler.post(operation) + } + } + + private val progressListener = object : UtteranceProgressListener() { + override fun onStart(utteranceId: String) { + runOnOwner { + if (utteranceId == utteranceId(generation, currentIndex)) { + state = TtsPlaybackState.PLAYING + emitState() + } + } + } + + override fun onDone(utteranceId: String) { + runOnOwner { advance(utteranceId) } + } + + @Deprecated("Deprecated by Android") + override fun onError(utteranceId: String) { + runOnOwner { + if (utteranceId == utteranceId(generation, currentIndex)) { + fail("The text-to-speech engine failed while speaking.") + } + } + } + + override fun onError(utteranceId: String, errorCode: Int) { + runOnOwner { + if (utteranceId == utteranceId(generation, currentIndex)) { + fail("The text-to-speech engine failed with error code $errorCode.") + } + } + } + } +} diff --git a/modules/nitro-tts/babel.config.js b/modules/nitro-tts/babel.config.js new file mode 100644 index 000000000..f7b3da3b3 --- /dev/null +++ b/modules/nitro-tts/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['module:@react-native/babel-preset'], +}; diff --git a/modules/nitro-tts/ios/Float+clamped.swift b/modules/nitro-tts/ios/Float+clamped.swift new file mode 100644 index 000000000..212f54ae9 --- /dev/null +++ b/modules/nitro-tts/ios/Float+clamped.swift @@ -0,0 +1,7 @@ +import Foundation + +extension Float { + func clamped(to range: ClosedRange) -> Float { + return min(max(self, range.lowerBound), range.upperBound) + } +} diff --git a/modules/nitro-tts/ios/HybridTtsFactory.swift b/modules/nitro-tts/ios/HybridTtsFactory.swift new file mode 100644 index 000000000..c5732fa69 --- /dev/null +++ b/modules/nitro-tts/ios/HybridTtsFactory.swift @@ -0,0 +1,28 @@ +import AVFAudio +import NitroModules + +final class HybridTtsFactory: HybridTtsFactorySpec { + func createSession() throws -> Promise { + return Promise.resolved(withResult: HybridTtsSession()) + } + + /// iOS has no concept of swappable synthesis engines, so this always + /// resolves to an empty array; the reader UI hides engine selection there. + func getEngines() throws -> Promise<[TtsEngine]> { + return Promise.resolved(withResult: []) + } + + /// iOS ignores `engineName` and lists the system's speech-synthesis voices. + func getVoices(engineName: String?) throws -> Promise<[TtsVoice]> { + let voices = AVSpeechSynthesisVoice.speechVoices() + .map { voice in + TtsVoice( + identifier: voice.identifier, + name: voice.name, + language: voice.language + ) + } + .sorted { $0.name < $1.name } + return Promise.resolved(withResult: voices) + } +} diff --git a/modules/nitro-tts/ios/HybridTtsSession.swift b/modules/nitro-tts/ios/HybridTtsSession.swift new file mode 100644 index 000000000..71d9425a7 --- /dev/null +++ b/modules/nitro-tts/ios/HybridTtsSession.swift @@ -0,0 +1,75 @@ +import NitroModules + +final class HybridTtsSession: HybridTtsSessionSpec { + private let coordinator = TtsPlaybackCoordinator.shared + + func load( + paragraphs: [TtsParagraph], + initialIndex: Double, + metadata: TtsMetadata, + settings: TtsSettings + ) throws -> Promise { + return MainQueuePromise.run { + try self.coordinator.load( + paragraphs: paragraphs, + initialIndex: Int(initialIndex), + metadata: metadata, + settings: settings + ) + } + } + + func play() throws -> Promise { + return MainQueuePromise.run(coordinator.play) + } + + func pause() throws -> Promise { + return MainQueuePromise.run(coordinator.pause) + } + + func stop() throws -> Promise { + return MainQueuePromise.run(coordinator.stop) + } + + func skipPrevious() throws -> Promise { + return MainQueuePromise.run(coordinator.skipPrevious) + } + + func skipNext() throws -> Promise { + return MainQueuePromise.run(coordinator.skipNext) + } + + func replayCurrent() throws -> Promise { + return MainQueuePromise.run(coordinator.replayCurrent) + } + + func seekTo(index: Double) throws -> Promise { + return MainQueuePromise.run { + self.coordinator.seekTo(index: Int(index)) + } + } + + func updateSettings(settings: TtsSettings) throws -> Promise { + return MainQueuePromise.run { + self.coordinator.updateSettings(settings) + } + } + + func addOnStateChangedListener( + listener: @escaping (TtsPlaybackState) -> Void + ) throws -> ListenerSubscription { + return ListenerSubscription(remove: coordinator.addStateListener(listener)) + } + + func addOnProgressChangedListener( + listener: @escaping (TtsProgress) -> Void + ) throws -> ListenerSubscription { + return ListenerSubscription(remove: coordinator.addProgressListener(listener)) + } + + func addOnErrorListener( + listener: @escaping (String) -> Void + ) throws -> ListenerSubscription { + return ListenerSubscription(remove: coordinator.addErrorListener(listener)) + } +} diff --git a/modules/nitro-tts/ios/MainQueuePromise.swift b/modules/nitro-tts/ios/MainQueuePromise.swift new file mode 100644 index 000000000..3b0e82050 --- /dev/null +++ b/modules/nitro-tts/ios/MainQueuePromise.swift @@ -0,0 +1,17 @@ +import Foundation +import NitroModules + +enum MainQueuePromise { + static func run(_ operation: @escaping () throws -> Void) -> Promise { + let promise = Promise() + DispatchQueue.main.async { + do { + try operation() + promise.resolve() + } catch { + promise.reject(withError: error) + } + } + return promise + } +} diff --git a/modules/nitro-tts/ios/TtsListenerRegistry.swift b/modules/nitro-tts/ios/TtsListenerRegistry.swift new file mode 100644 index 000000000..c15d09df9 --- /dev/null +++ b/modules/nitro-tts/ios/TtsListenerRegistry.swift @@ -0,0 +1,26 @@ +import Foundation + +final class TtsListenerRegistry { + private let lock = NSLock() + private var listeners: [UUID: (Value) -> Void] = [:] + + func add(_ listener: @escaping (Value) -> Void) -> () -> Void { + let id = UUID() + lock.lock() + listeners[id] = listener + lock.unlock() + + return { [weak self] in + self?.lock.lock() + self?.listeners.removeValue(forKey: id) + self?.lock.unlock() + } + } + + func emit(_ value: Value) { + lock.lock() + let snapshot = Array(listeners.values) + lock.unlock() + snapshot.forEach { $0(value) } + } +} diff --git a/modules/nitro-tts/ios/TtsNowPlayingController.swift b/modules/nitro-tts/ios/TtsNowPlayingController.swift new file mode 100644 index 000000000..15c51dbd2 --- /dev/null +++ b/modules/nitro-tts/ios/TtsNowPlayingController.swift @@ -0,0 +1,31 @@ +import MediaPlayer + +final class TtsNowPlayingController { + func update( + metadata: TtsMetadata?, + state: TtsPlaybackState, + progress: TtsProgress? + ) { + guard let metadata else { + MPNowPlayingInfoCenter.default().nowPlayingInfo = nil + return + } + + var nowPlayingInfo: [String: Any] = [ + MPMediaItemPropertyTitle: metadata.chapterName, + MPMediaItemPropertyArtist: metadata.novelName, + MPNowPlayingInfoPropertyPlaybackRate: state == .playing ? 1.0 : 0.0, + ] + if let progress, progress.total > 0 { + let total = Int(progress.total) + let current = min(max(Int(progress.index), 0), total - 1) + 1 + nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = + "Paragraph \(current) of \(total)" + } + MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo + } + + func clear() { + MPNowPlayingInfoCenter.default().nowPlayingInfo = nil + } +} diff --git a/modules/nitro-tts/ios/TtsPlaybackCoordinator.swift b/modules/nitro-tts/ios/TtsPlaybackCoordinator.swift new file mode 100644 index 000000000..bb7ef6ec0 --- /dev/null +++ b/modules/nitro-tts/ios/TtsPlaybackCoordinator.swift @@ -0,0 +1,256 @@ +import AVFAudio +import Foundation +import NitroModules + +final class TtsPlaybackCoordinator: NSObject, AVSpeechSynthesizerDelegate { + static let shared = TtsPlaybackCoordinator() + + private let synthesizer = AVSpeechSynthesizer() + private let stateListeners = TtsListenerRegistry() + private let progressListeners = TtsListenerRegistry() + private let errorListeners = TtsListenerRegistry() + private let nowPlaying = TtsNowPlayingController() + + private var paragraphs: [TtsParagraph] = [] + private var currentIndex = 0 + private var metadata: TtsMetadata? + private var settings = TtsSettings(engineName: nil, voiceIdentifier: nil, rate: 1, pitch: 1) + private var state: TtsPlaybackState = .idle + private var activeUtterance: AVSpeechUtterance? + + private lazy var remoteCommands = TtsRemoteCommandController( + onPlay: { [weak self] in self?.play() }, + onPause: { [weak self] in self?.pause() }, + onStop: { [weak self] in self?.stop() }, + onPrevious: { [weak self] in self?.skipPrevious() }, + onNext: { [weak self] in self?.skipNext() } + ) + + override private init() { + super.init() + synthesizer.delegate = self + _ = remoteCommands + } + + func load( + paragraphs nextParagraphs: [TtsParagraph], + initialIndex: Int, + metadata nextMetadata: TtsMetadata, + settings nextSettings: TtsSettings + ) throws { + precondition(Thread.isMainThread) + let readableParagraphs = nextParagraphs.filter { !$0.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } + guard !readableParagraphs.isEmpty else { + throw RuntimeError.error(withMessage: "The TTS queue contains no readable paragraphs.") + } + + interruptSpeech() + paragraphs = readableParagraphs + currentIndex = min(max(initialIndex, 0), readableParagraphs.count - 1) + metadata = nextMetadata + settings = nextSettings + state = .paused + emitProgress() + emitState() + } + + func play() { + precondition(Thread.isMainThread) + guard !paragraphs.isEmpty else { + fail("Load a paragraph queue before starting TTS.") + return + } + if synthesizer.isPaused { + synthesizer.continueSpeaking() + state = .playing + emitState() + return + } + speakCurrent() + } + + func pause() { + precondition(Thread.isMainThread) + guard state == .playing else { return } + synthesizer.pauseSpeaking(at: .immediate) + state = .paused + emitState() + } + + func stop() { + precondition(Thread.isMainThread) + interruptSpeech() + paragraphs = [] + currentIndex = 0 + metadata = nil + state = .idle + nowPlaying.clear() + emitState() + try? AVAudioSession.sharedInstance().setActive( + false, + options: .notifyOthersOnDeactivation + ) + } + + func skipPrevious() { + precondition(Thread.isMainThread) + guard !paragraphs.isEmpty else { return } + currentIndex = max(currentIndex - 1, 0) + speakCurrent() + } + + func skipNext() { + precondition(Thread.isMainThread) + guard !paragraphs.isEmpty else { return } + guard currentIndex < paragraphs.count - 1 else { + completeQueue() + return + } + currentIndex += 1 + speakCurrent() + } + + func replayCurrent() { + precondition(Thread.isMainThread) + guard !paragraphs.isEmpty else { return } + speakCurrent() + } + + func seekTo(index: Int) { + precondition(Thread.isMainThread) + guard !paragraphs.isEmpty else { return } + currentIndex = min(max(index, 0), paragraphs.count - 1) + speakCurrent() + } + + func updateSettings(_ nextSettings: TtsSettings) { + precondition(Thread.isMainThread) + let shouldResume = state == .playing + settings = nextSettings + if shouldResume { + speakCurrent() + } + } + + func addStateListener( + _ listener: @escaping (TtsPlaybackState) -> Void + ) -> () -> Void { + DispatchQueue.main.async { [weak self] in + guard let self else { return } + listener(self.state) + } + return stateListeners.add(listener) + } + + func addProgressListener( + _ listener: @escaping (TtsProgress) -> Void + ) -> () -> Void { + DispatchQueue.main.async { [weak self] in + guard let progress = self?.currentProgress() else { return } + listener(progress) + } + return progressListeners.add(listener) + } + + func addErrorListener(_ listener: @escaping (String) -> Void) -> () -> Void { + return errorListeners.add(listener) + } + + func speechSynthesizer( + _ synthesizer: AVSpeechSynthesizer, + didFinish utterance: AVSpeechUtterance + ) { + guard utterance === activeUtterance else { return } + activeUtterance = nil + if currentIndex >= paragraphs.count - 1 { + completeQueue() + } else { + currentIndex += 1 + speakCurrent() + } + } + + func speechSynthesizer( + _ synthesizer: AVSpeechSynthesizer, + didCancel utterance: AVSpeechUtterance + ) { + if utterance === activeUtterance { + activeUtterance = nil + } + } + + private func speakCurrent() { + interruptSpeech() + do { + let audioSession = AVAudioSession.sharedInstance() + try audioSession.setCategory(.playback, mode: .spokenAudio) + try audioSession.setActive(true) + } catch { + fail("Unable to activate the audio session: \(error.localizedDescription)") + return + } + + let paragraph = paragraphs[currentIndex] + let utterance = AVSpeechUtterance(string: paragraph.text) + utterance.rate = ( + Float(settings.rate) * AVSpeechUtteranceDefaultSpeechRate + ).clamped( + to: AVSpeechUtteranceMinimumSpeechRate...AVSpeechUtteranceMaximumSpeechRate + ) + utterance.pitchMultiplier = Float(settings.pitch).clamped(to: 0.5...2.0) + if let identifier = settings.voiceIdentifier { + utterance.voice = AVSpeechSynthesisVoice(identifier: identifier) + } + activeUtterance = utterance + synthesizer.speak(utterance) + state = .playing + emitProgress() + emitState() + } + + private func interruptSpeech() { + if synthesizer.isSpeaking || synthesizer.isPaused { + synthesizer.stopSpeaking(at: .immediate) + } + activeUtterance = nil + } + + private func completeQueue() { + state = .completed + emitState() + try? AVAudioSession.sharedInstance().setActive( + false, + options: .notifyOthersOnDeactivation + ) + } + + private func fail(_ message: String) { + state = .error + errorListeners.emit(message) + emitState() + } + + private func emitState() { + stateListeners.emit(state) + nowPlaying.update( + metadata: metadata, + state: state, + progress: currentProgress() + ) + } + + private func emitProgress() { + guard let progress = currentProgress() else { return } + progressListeners.emit(progress) + nowPlaying.update(metadata: metadata, state: state, progress: progress) + } + + private func currentProgress() -> TtsProgress? { + guard paragraphs.indices.contains(currentIndex) else { return nil } + return TtsProgress( + index: Double(currentIndex), + total: Double(paragraphs.count), + paragraphId: paragraphs[currentIndex].id + ) + } +} diff --git a/modules/nitro-tts/ios/TtsRemoteCommandController.swift b/modules/nitro-tts/ios/TtsRemoteCommandController.swift new file mode 100644 index 000000000..1854638e5 --- /dev/null +++ b/modules/nitro-tts/ios/TtsRemoteCommandController.swift @@ -0,0 +1,37 @@ +import MediaPlayer + +final class TtsRemoteCommandController { + private let commandCenter = MPRemoteCommandCenter.shared() + private var targets: [(MPRemoteCommand, Any)] = [] + + init( + onPlay: @escaping () -> Void, + onPause: @escaping () -> Void, + onStop: @escaping () -> Void, + onPrevious: @escaping () -> Void, + onNext: @escaping () -> Void + ) { + register(commandCenter.playCommand, action: onPlay) + register(commandCenter.pauseCommand, action: onPause) + register(commandCenter.stopCommand, action: onStop) + register(commandCenter.previousTrackCommand, action: onPrevious) + register(commandCenter.nextTrackCommand, action: onNext) + } + + deinit { + targets.forEach { command, target in + command.removeTarget(target) + } + } + + private func register(_ command: MPRemoteCommand, action: @escaping () -> Void) { + command.isEnabled = true + let target = command.addTarget { _ in + DispatchQueue.main.async { + action() + } + return .success + } + targets.append((command, target)) + } +} diff --git a/modules/nitro-tts/nitro.json b/modules/nitro-tts/nitro.json new file mode 100644 index 000000000..9ebe54119 --- /dev/null +++ b/modules/nitro-tts/nitro.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://nitro.margelo.com/nitro.schema.json", + "cxxNamespace": ["nitrotts"], + "ios": { + "iosModuleName": "NitroTts" + }, + "android": { + "androidNamespace": ["nitrotts"], + "androidCxxLibName": "NitroTts" + }, + "autolinking": { + "TtsFactory": { + "ios": { + "language": "swift", + "implementationClassName": "HybridTtsFactory" + }, + "android": { + "language": "kotlin", + "implementationClassName": "HybridTtsFactory" + } + } + }, + "ignorePaths": ["**/node_modules"] +} diff --git a/modules/nitro-tts/nitrogen/generated/.gitattributes b/modules/nitro-tts/nitrogen/generated/.gitattributes new file mode 100644 index 000000000..fb7a0d5a3 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/.gitattributes @@ -0,0 +1 @@ +** linguist-generated=true diff --git a/modules/nitro-tts/nitrogen/generated/android/NitroTts+autolinking.cmake b/modules/nitro-tts/nitrogen/generated/android/NitroTts+autolinking.cmake new file mode 100644 index 000000000..628ab25c1 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/NitroTts+autolinking.cmake @@ -0,0 +1,83 @@ +# +# NitroTts+autolinking.cmake +# This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +# https://github.com/mrousavy/nitro +# Copyright © Marc Rousavy @ Margelo +# + +# This is a CMake file that adds all files generated by Nitrogen +# to the current CMake project. +# +# To use it, add this to your CMakeLists.txt: +# ```cmake +# include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroTts+autolinking.cmake) +# ``` + +# Define a flag to check if we are building properly +add_definitions(-DBUILDING_NITROTTS_WITH_GENERATED_CMAKE_PROJECT) + +# Enable Raw Props parsing in react-native (for Nitro Views) +add_definitions(-DRN_SERIALIZABLE_STATE) + +# Add all headers that were generated by Nitrogen +include_directories( + "../nitrogen/generated/shared/c++" + "../nitrogen/generated/android/c++" + "../nitrogen/generated/android/" +) + +# Add all .cpp sources that were generated by Nitrogen +target_sources( + # CMake project name (Android C++ library name) + NitroTts PRIVATE + # Autolinking Setup + ../nitrogen/generated/android/NitroTtsOnLoad.cpp + # Shared Nitrogen C++ sources + ../nitrogen/generated/shared/c++/HybridTtsFactorySpec.cpp + ../nitrogen/generated/shared/c++/HybridTtsSessionSpec.cpp + # Android-specific Nitrogen C++ sources + ../nitrogen/generated/android/c++/JHybridTtsFactorySpec.cpp + ../nitrogen/generated/android/c++/JHybridTtsSessionSpec.cpp +) + +# From node_modules/react-native/ReactAndroid/cmake-utils/folly-flags.cmake +# Used in node_modules/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake +target_compile_definitions( + NitroTts PRIVATE + -DFOLLY_NO_CONFIG=1 + -DFOLLY_HAVE_CLOCK_GETTIME=1 + -DFOLLY_USE_LIBCPP=1 + -DFOLLY_CFG_NO_COROUTINES=1 + -DFOLLY_MOBILE=1 + -DFOLLY_HAVE_RECVMMSG=1 + -DFOLLY_HAVE_PTHREAD=1 + # Once we target android-23 above, we can comment + # the following line. NDK uses GNU style stderror_r() after API 23. + -DFOLLY_HAVE_XSI_STRERROR_R=1 +) + +# Add all libraries required by the generated specs +find_package(fbjni REQUIRED) # <-- Used for communication between Java <-> C++ +find_package(ReactAndroid REQUIRED) # <-- Used to set up React Native bindings (e.g. CallInvoker/TurboModule) +find_package(react-native-nitro-modules REQUIRED) # <-- Used to create all HybridObjects and use the Nitro core library + +# Link all libraries together +target_link_libraries( + NitroTts + fbjni::fbjni # <-- Facebook C++ JNI helpers + ReactAndroid::jsi # <-- RN: JSI + react-native-nitro-modules::NitroModules # <-- NitroModules Core :) +) + +# Link react-native (different prefab between RN 0.75 and RN 0.76) +if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76) + target_link_libraries( + NitroTts + ReactAndroid::reactnative # <-- RN: Native Modules umbrella prefab + ) +else() + target_link_libraries( + NitroTts + ReactAndroid::react_nativemodule_core # <-- RN: TurboModules Core + ) +endif() diff --git a/modules/nitro-tts/nitrogen/generated/android/NitroTts+autolinking.gradle b/modules/nitro-tts/nitrogen/generated/android/NitroTts+autolinking.gradle new file mode 100644 index 000000000..ec609e57d --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/NitroTts+autolinking.gradle @@ -0,0 +1,27 @@ +/// +/// NitroTts+autolinking.gradle +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +/// This is a Gradle file that adds all files generated by Nitrogen +/// to the current Gradle project. +/// +/// To use it, add this to your build.gradle: +/// ```gradle +/// apply from: '../nitrogen/generated/android/NitroTts+autolinking.gradle' +/// ``` + +logger.warn("[NitroModules] 🔥 NitroTts is boosted by nitro!") + +android { + sourceSets { + main { + java.srcDirs += [ + // Nitrogen files + "${project.projectDir}/../nitrogen/generated/android/kotlin" + ] + } + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/NitroTtsOnLoad.cpp b/modules/nitro-tts/nitrogen/generated/android/NitroTtsOnLoad.cpp new file mode 100644 index 000000000..6276e4031 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/NitroTtsOnLoad.cpp @@ -0,0 +1,64 @@ +/// +/// NitroTtsOnLoad.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#ifndef BUILDING_NITROTTS_WITH_GENERATED_CMAKE_PROJECT +#error NitroTtsOnLoad.cpp is not being built with the autogenerated CMakeLists.txt project. Is a different CMakeLists.txt building this? +#endif + +#include "NitroTtsOnLoad.hpp" + +#include +#include +#include + +#include "JHybridTtsFactorySpec.hpp" +#include "JHybridTtsSessionSpec.hpp" +#include "JFunc_void.hpp" +#include "JFunc_void_TtsPlaybackState.hpp" +#include "JFunc_void_TtsProgress.hpp" +#include "JFunc_void_std__string.hpp" +#include + +namespace margelo::nitro::nitrotts { + +int initialize(JavaVM* vm) { + return facebook::jni::initialize(vm, []() { + ::margelo::nitro::nitrotts::registerAllNatives(); + }); +} + +struct JHybridTtsFactorySpecImpl: public jni::JavaClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/HybridTtsFactory;"; + static std::shared_ptr create() { + static const auto constructorFn = javaClassStatic()->getConstructor(); + jni::local_ref javaPart = javaClassStatic()->newObject(constructorFn); + return javaPart->getJHybridTtsFactorySpec(); + } +}; + +void registerAllNatives() { + using namespace margelo::nitro; + using namespace margelo::nitro::nitrotts; + + // Register native JNI methods + margelo::nitro::nitrotts::JHybridTtsFactorySpec::CxxPart::registerNatives(); + margelo::nitro::nitrotts::JHybridTtsSessionSpec::CxxPart::registerNatives(); + margelo::nitro::nitrotts::JFunc_void_cxx::registerNatives(); + margelo::nitro::nitrotts::JFunc_void_TtsPlaybackState_cxx::registerNatives(); + margelo::nitro::nitrotts::JFunc_void_TtsProgress_cxx::registerNatives(); + margelo::nitro::nitrotts::JFunc_void_std__string_cxx::registerNatives(); + + // Register Nitro Hybrid Objects + HybridObjectRegistry::registerHybridObjectConstructor( + "TtsFactory", + []() -> std::shared_ptr { + return JHybridTtsFactorySpecImpl::create(); + } + ); +} + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/NitroTtsOnLoad.hpp b/modules/nitro-tts/nitrogen/generated/android/NitroTtsOnLoad.hpp new file mode 100644 index 000000000..dda0ce69f --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/NitroTtsOnLoad.hpp @@ -0,0 +1,34 @@ +/// +/// NitroTtsOnLoad.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include +#include +#include + +namespace margelo::nitro::nitrotts { + + [[deprecated("Use registerNatives() instead.")]] + int initialize(JavaVM* vm); + + /** + * Register the native (C++) part of NitroTts, and autolinks all Hybrid Objects. + * Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`), + * inside a `facebook::jni::initialize(vm, ...)` call. + * Example: + * ```cpp (cpp-adapter.cpp) + * JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { + * return facebook::jni::initialize(vm, []() { + * // register all NitroTts HybridObjects + * margelo::nitro::nitrotts::registerNatives(); + * // any other custom registrations go here. + * }); + * } + * ``` + */ + void registerAllNatives(); + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JFunc_void.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JFunc_void.hpp new file mode 100644 index 000000000..30dbda86d --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JFunc_void.hpp @@ -0,0 +1,75 @@ +/// +/// JFunc_void.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include + +#include +#include + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + /** + * Represents the Java/Kotlin callback `() -> Unit`. + * This can be passed around between C++ and Java/Kotlin. + */ + struct JFunc_void: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/Func_void;"; + + public: + /** + * Invokes the function this `JFunc_void` instance holds through JNI. + */ + void invoke() const { + static const auto method = javaClassStatic()->getMethod("invoke"); + method(self()); + } + }; + + /** + * An implementation of Func_void that is backed by a C++ implementation (using `std::function<...>`) + */ + class JFunc_void_cxx final: public jni::HybridClass { + public: + static jni::local_ref fromCpp(const std::function& func) { + return JFunc_void_cxx::newObjectCxxArgs(func); + } + + public: + /** + * Invokes the C++ `std::function<...>` this `JFunc_void_cxx` instance holds. + */ + void invoke_cxx() { + _func(); + } + + public: + [[nodiscard]] + inline const std::function& getFunction() const { + return _func; + } + + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/Func_void_cxx;"; + static void registerNatives() { + registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_cxx::invoke_cxx)}); + } + + private: + explicit JFunc_void_cxx(const std::function& func): _func(func) { } + + private: + friend HybridBase; + std::function _func; + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JFunc_void_TtsPlaybackState.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JFunc_void_TtsPlaybackState.hpp new file mode 100644 index 000000000..6ee89c321 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JFunc_void_TtsPlaybackState.hpp @@ -0,0 +1,77 @@ +/// +/// JFunc_void_TtsPlaybackState.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include + +#include "TtsPlaybackState.hpp" +#include +#include +#include "JTtsPlaybackState.hpp" + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + /** + * Represents the Java/Kotlin callback `(state: TtsPlaybackState) -> Unit`. + * This can be passed around between C++ and Java/Kotlin. + */ + struct JFunc_void_TtsPlaybackState: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/Func_void_TtsPlaybackState;"; + + public: + /** + * Invokes the function this `JFunc_void_TtsPlaybackState` instance holds through JNI. + */ + void invoke(TtsPlaybackState state) const { + static const auto method = javaClassStatic()->getMethod /* state */)>("invoke"); + method(self(), JTtsPlaybackState::fromCpp(state)); + } + }; + + /** + * An implementation of Func_void_TtsPlaybackState that is backed by a C++ implementation (using `std::function<...>`) + */ + class JFunc_void_TtsPlaybackState_cxx final: public jni::HybridClass { + public: + static jni::local_ref fromCpp(const std::function& func) { + return JFunc_void_TtsPlaybackState_cxx::newObjectCxxArgs(func); + } + + public: + /** + * Invokes the C++ `std::function<...>` this `JFunc_void_TtsPlaybackState_cxx` instance holds. + */ + void invoke_cxx(jni::alias_ref state) { + _func(state->toCpp()); + } + + public: + [[nodiscard]] + inline const std::function& getFunction() const { + return _func; + } + + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/Func_void_TtsPlaybackState_cxx;"; + static void registerNatives() { + registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_TtsPlaybackState_cxx::invoke_cxx)}); + } + + private: + explicit JFunc_void_TtsPlaybackState_cxx(const std::function& func): _func(func) { } + + private: + friend HybridBase; + std::function _func; + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JFunc_void_TtsProgress.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JFunc_void_TtsProgress.hpp new file mode 100644 index 000000000..f44d0b520 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JFunc_void_TtsProgress.hpp @@ -0,0 +1,78 @@ +/// +/// JFunc_void_TtsProgress.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include + +#include "TtsProgress.hpp" +#include +#include +#include "JTtsProgress.hpp" +#include + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + /** + * Represents the Java/Kotlin callback `(progress: TtsProgress) -> Unit`. + * This can be passed around between C++ and Java/Kotlin. + */ + struct JFunc_void_TtsProgress: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/Func_void_TtsProgress;"; + + public: + /** + * Invokes the function this `JFunc_void_TtsProgress` instance holds through JNI. + */ + void invoke(const TtsProgress& progress) const { + static const auto method = javaClassStatic()->getMethod /* progress */)>("invoke"); + method(self(), JTtsProgress::fromCpp(progress)); + } + }; + + /** + * An implementation of Func_void_TtsProgress that is backed by a C++ implementation (using `std::function<...>`) + */ + class JFunc_void_TtsProgress_cxx final: public jni::HybridClass { + public: + static jni::local_ref fromCpp(const std::function& func) { + return JFunc_void_TtsProgress_cxx::newObjectCxxArgs(func); + } + + public: + /** + * Invokes the C++ `std::function<...>` this `JFunc_void_TtsProgress_cxx` instance holds. + */ + void invoke_cxx(jni::alias_ref progress) { + _func(progress->toCpp()); + } + + public: + [[nodiscard]] + inline const std::function& getFunction() const { + return _func; + } + + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/Func_void_TtsProgress_cxx;"; + static void registerNatives() { + registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_TtsProgress_cxx::invoke_cxx)}); + } + + private: + explicit JFunc_void_TtsProgress_cxx(const std::function& func): _func(func) { } + + private: + friend HybridBase; + std::function _func; + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JFunc_void_std__string.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JFunc_void_std__string.hpp new file mode 100644 index 000000000..baa5849fc --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JFunc_void_std__string.hpp @@ -0,0 +1,76 @@ +/// +/// JFunc_void_std__string.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include + +#include +#include +#include + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + /** + * Represents the Java/Kotlin callback `(message: String) -> Unit`. + * This can be passed around between C++ and Java/Kotlin. + */ + struct JFunc_void_std__string: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/Func_void_std__string;"; + + public: + /** + * Invokes the function this `JFunc_void_std__string` instance holds through JNI. + */ + void invoke(const std::string& message) const { + static const auto method = javaClassStatic()->getMethod /* message */)>("invoke"); + method(self(), jni::make_jstring(message)); + } + }; + + /** + * An implementation of Func_void_std__string that is backed by a C++ implementation (using `std::function<...>`) + */ + class JFunc_void_std__string_cxx final: public jni::HybridClass { + public: + static jni::local_ref fromCpp(const std::function& func) { + return JFunc_void_std__string_cxx::newObjectCxxArgs(func); + } + + public: + /** + * Invokes the C++ `std::function<...>` this `JFunc_void_std__string_cxx` instance holds. + */ + void invoke_cxx(jni::alias_ref message) { + _func(message->toStdString()); + } + + public: + [[nodiscard]] + inline const std::function& getFunction() const { + return _func; + } + + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/Func_void_std__string_cxx;"; + static void registerNatives() { + registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_std__string_cxx::invoke_cxx)}); + } + + private: + explicit JFunc_void_std__string_cxx(const std::function& func): _func(func) { } + + private: + friend HybridBase; + std::function _func; + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JHybridTtsFactorySpec.cpp b/modules/nitro-tts/nitrogen/generated/android/c++/JHybridTtsFactorySpec.cpp new file mode 100644 index 000000000..dcb73b140 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JHybridTtsFactorySpec.cpp @@ -0,0 +1,130 @@ +/// +/// JHybridTtsFactorySpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "JHybridTtsFactorySpec.hpp" + +// Forward declaration of `HybridTtsSessionSpec` to properly resolve imports. +namespace margelo::nitro::nitrotts { class HybridTtsSessionSpec; } +// Forward declaration of `TtsEngine` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsEngine; } +// Forward declaration of `TtsVoice` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsVoice; } + +#include +#include "HybridTtsSessionSpec.hpp" +#include +#include +#include "JHybridTtsSessionSpec.hpp" +#include "TtsEngine.hpp" +#include +#include "JTtsEngine.hpp" +#include +#include "TtsVoice.hpp" +#include "JTtsVoice.hpp" +#include + +namespace margelo::nitro::nitrotts { + + std::shared_ptr JHybridTtsFactorySpec::JavaPart::getJHybridTtsFactorySpec() { + auto hybridObject = JHybridObject::JavaPart::getJHybridObject(); + auto castHybridObject = std::dynamic_pointer_cast(hybridObject); + if (castHybridObject == nullptr) [[unlikely]] { + throw std::runtime_error("Failed to downcast JHybridObject to JHybridTtsFactorySpec!"); + } + return castHybridObject; + } + + jni::local_ref JHybridTtsFactorySpec::CxxPart::initHybrid(jni::alias_ref jThis) { + return makeCxxInstance(jThis); + } + + std::shared_ptr JHybridTtsFactorySpec::CxxPart::createHybridObject(const jni::local_ref& javaPart) { + auto castJavaPart = jni::dynamic_ref_cast(javaPart); + if (castJavaPart == nullptr) [[unlikely]] { + throw std::runtime_error("Failed to cast JHybridObject::JavaPart to JHybridTtsFactorySpec::JavaPart!"); + } + return std::make_shared(castJavaPart); + } + + void JHybridTtsFactorySpec::CxxPart::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", JHybridTtsFactorySpec::CxxPart::initHybrid), + }); + } + + // Properties + + + // Methods + std::shared_ptr>> JHybridTtsFactorySpec::createSession() { + static const auto method = _javaPart->javaClassStatic()->getMethod()>("createSession"); + auto __result = method(_javaPart); + return [&]() { + auto __promise = Promise>::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& __boxedResult) { + auto __result = jni::static_ref_cast(__boxedResult); + __promise->resolve(__result->getJHybridTtsSessionSpec()); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr>> JHybridTtsFactorySpec::getEngines() { + static const auto method = _javaPart->javaClassStatic()->getMethod()>("getEngines"); + auto __result = method(_javaPart); + return [&]() { + auto __promise = Promise>::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& __boxedResult) { + auto __result = jni::static_ref_cast>(__boxedResult); + __promise->resolve([&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(__result)); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr>> JHybridTtsFactorySpec::getVoices(const std::optional& engineName) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* engineName */)>("getVoices"); + auto __result = method(_javaPart, engineName.has_value() ? jni::make_jstring(engineName.value()) : nullptr); + return [&]() { + auto __promise = Promise>::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& __boxedResult) { + auto __result = jni::static_ref_cast>(__boxedResult); + __promise->resolve([&](auto&& __input) { + size_t __size = __input->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = __input->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(__result)); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JHybridTtsFactorySpec.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JHybridTtsFactorySpec.hpp new file mode 100644 index 000000000..7e8094136 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JHybridTtsFactorySpec.hpp @@ -0,0 +1,65 @@ +/// +/// HybridTtsFactorySpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include +#include "HybridTtsFactorySpec.hpp" + + + + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + class JHybridTtsFactorySpec: public virtual HybridTtsFactorySpec, public virtual JHybridObject { + public: + struct JavaPart: public jni::JavaClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/HybridTtsFactorySpec;"; + std::shared_ptr getJHybridTtsFactorySpec(); + }; + struct CxxPart: public jni::HybridClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/HybridTtsFactorySpec$CxxPart;"; + static jni::local_ref initHybrid(jni::alias_ref jThis); + static void registerNatives(); + using HybridBase::HybridBase; + protected: + std::shared_ptr createHybridObject(const jni::local_ref& javaPart) override; + }; + + public: + explicit JHybridTtsFactorySpec(const jni::local_ref& javaPart): + HybridObject(HybridTtsFactorySpec::TAG), + JHybridObject(javaPart), + _javaPart(jni::make_global(javaPart)) {} + ~JHybridTtsFactorySpec() override { + // Hermes GC can destroy JS objects on a non-JNI Thread. + jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); }); + } + + public: + inline const jni::global_ref& getJavaPart() const noexcept { + return _javaPart; + } + + public: + // Properties + + + public: + // Methods + std::shared_ptr>> createSession() override; + std::shared_ptr>> getEngines() override; + std::shared_ptr>> getVoices(const std::optional& engineName) override; + + private: + jni::global_ref _javaPart; + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JHybridTtsSessionSpec.cpp b/modules/nitro-tts/nitrogen/generated/android/c++/JHybridTtsSessionSpec.cpp new file mode 100644 index 000000000..86c4b7036 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JHybridTtsSessionSpec.cpp @@ -0,0 +1,241 @@ +/// +/// JHybridTtsSessionSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "JHybridTtsSessionSpec.hpp" + +// Forward declaration of `ListenerSubscription` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct ListenerSubscription; } +// Forward declaration of `TtsParagraph` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsParagraph; } +// Forward declaration of `TtsMetadata` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsMetadata; } +// Forward declaration of `TtsSettings` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsSettings; } +// Forward declaration of `TtsPlaybackState` to properly resolve imports. +namespace margelo::nitro::nitrotts { enum class TtsPlaybackState; } +// Forward declaration of `TtsProgress` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsProgress; } + +#include +#include +#include +#include "ListenerSubscription.hpp" +#include "JListenerSubscription.hpp" +#include +#include "JFunc_void.hpp" +#include +#include "TtsParagraph.hpp" +#include +#include "JTtsParagraph.hpp" +#include +#include "TtsMetadata.hpp" +#include "JTtsMetadata.hpp" +#include +#include "TtsSettings.hpp" +#include "JTtsSettings.hpp" +#include "TtsPlaybackState.hpp" +#include "JFunc_void_TtsPlaybackState.hpp" +#include "JTtsPlaybackState.hpp" +#include "TtsProgress.hpp" +#include "JFunc_void_TtsProgress.hpp" +#include "JTtsProgress.hpp" +#include "JFunc_void_std__string.hpp" + +namespace margelo::nitro::nitrotts { + + std::shared_ptr JHybridTtsSessionSpec::JavaPart::getJHybridTtsSessionSpec() { + auto hybridObject = JHybridObject::JavaPart::getJHybridObject(); + auto castHybridObject = std::dynamic_pointer_cast(hybridObject); + if (castHybridObject == nullptr) [[unlikely]] { + throw std::runtime_error("Failed to downcast JHybridObject to JHybridTtsSessionSpec!"); + } + return castHybridObject; + } + + jni::local_ref JHybridTtsSessionSpec::CxxPart::initHybrid(jni::alias_ref jThis) { + return makeCxxInstance(jThis); + } + + std::shared_ptr JHybridTtsSessionSpec::CxxPart::createHybridObject(const jni::local_ref& javaPart) { + auto castJavaPart = jni::dynamic_ref_cast(javaPart); + if (castJavaPart == nullptr) [[unlikely]] { + throw std::runtime_error("Failed to cast JHybridObject::JavaPart to JHybridTtsSessionSpec::JavaPart!"); + } + return std::make_shared(castJavaPart); + } + + void JHybridTtsSessionSpec::CxxPart::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", JHybridTtsSessionSpec::CxxPart::initHybrid), + }); + } + + // Properties + + + // Methods + std::shared_ptr> JHybridTtsSessionSpec::load(const std::vector& paragraphs, double initialIndex, const TtsMetadata& metadata, const TtsSettings& settings) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref> /* paragraphs */, double /* initialIndex */, jni::alias_ref /* metadata */, jni::alias_ref /* settings */)>("load"); + auto __result = method(_javaPart, [&](auto&& __input) { + size_t __size = __input.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = __input[__i]; + auto __elementJni = JTtsParagraph::fromCpp(__element); + __array->setElement(__i, *__elementJni); + } + return __array; + }(paragraphs), initialIndex, JTtsMetadata::fromCpp(metadata), JTtsSettings::fromCpp(settings)); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr> JHybridTtsSessionSpec::play() { + static const auto method = _javaPart->javaClassStatic()->getMethod()>("play"); + auto __result = method(_javaPart); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr> JHybridTtsSessionSpec::pause() { + static const auto method = _javaPart->javaClassStatic()->getMethod()>("pause"); + auto __result = method(_javaPart); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr> JHybridTtsSessionSpec::stop() { + static const auto method = _javaPart->javaClassStatic()->getMethod()>("stop"); + auto __result = method(_javaPart); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr> JHybridTtsSessionSpec::skipPrevious() { + static const auto method = _javaPart->javaClassStatic()->getMethod()>("skipPrevious"); + auto __result = method(_javaPart); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr> JHybridTtsSessionSpec::skipNext() { + static const auto method = _javaPart->javaClassStatic()->getMethod()>("skipNext"); + auto __result = method(_javaPart); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr> JHybridTtsSessionSpec::replayCurrent() { + static const auto method = _javaPart->javaClassStatic()->getMethod()>("replayCurrent"); + auto __result = method(_javaPart); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr> JHybridTtsSessionSpec::seekTo(double index) { + static const auto method = _javaPart->javaClassStatic()->getMethod(double /* index */)>("seekTo"); + auto __result = method(_javaPart, index); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr> JHybridTtsSessionSpec::updateSettings(const TtsSettings& settings) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* settings */)>("updateSettings"); + auto __result = method(_javaPart, JTtsSettings::fromCpp(settings)); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + ListenerSubscription JHybridTtsSessionSpec::addOnStateChangedListener(const std::function& listener) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* listener */)>("addOnStateChangedListener_cxx"); + auto __result = method(_javaPart, JFunc_void_TtsPlaybackState_cxx::fromCpp(listener)); + return __result->toCpp(); + } + ListenerSubscription JHybridTtsSessionSpec::addOnProgressChangedListener(const std::function& listener) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* listener */)>("addOnProgressChangedListener_cxx"); + auto __result = method(_javaPart, JFunc_void_TtsProgress_cxx::fromCpp(listener)); + return __result->toCpp(); + } + ListenerSubscription JHybridTtsSessionSpec::addOnErrorListener(const std::function& listener) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* listener */)>("addOnErrorListener_cxx"); + auto __result = method(_javaPart, JFunc_void_std__string_cxx::fromCpp(listener)); + return __result->toCpp(); + } + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JHybridTtsSessionSpec.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JHybridTtsSessionSpec.hpp new file mode 100644 index 000000000..757eb65fb --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JHybridTtsSessionSpec.hpp @@ -0,0 +1,74 @@ +/// +/// HybridTtsSessionSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include +#include "HybridTtsSessionSpec.hpp" + + + + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + class JHybridTtsSessionSpec: public virtual HybridTtsSessionSpec, public virtual JHybridObject { + public: + struct JavaPart: public jni::JavaClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/HybridTtsSessionSpec;"; + std::shared_ptr getJHybridTtsSessionSpec(); + }; + struct CxxPart: public jni::HybridClass { + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/HybridTtsSessionSpec$CxxPart;"; + static jni::local_ref initHybrid(jni::alias_ref jThis); + static void registerNatives(); + using HybridBase::HybridBase; + protected: + std::shared_ptr createHybridObject(const jni::local_ref& javaPart) override; + }; + + public: + explicit JHybridTtsSessionSpec(const jni::local_ref& javaPart): + HybridObject(HybridTtsSessionSpec::TAG), + JHybridObject(javaPart), + _javaPart(jni::make_global(javaPart)) {} + ~JHybridTtsSessionSpec() override { + // Hermes GC can destroy JS objects on a non-JNI Thread. + jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); }); + } + + public: + inline const jni::global_ref& getJavaPart() const noexcept { + return _javaPart; + } + + public: + // Properties + + + public: + // Methods + std::shared_ptr> load(const std::vector& paragraphs, double initialIndex, const TtsMetadata& metadata, const TtsSettings& settings) override; + std::shared_ptr> play() override; + std::shared_ptr> pause() override; + std::shared_ptr> stop() override; + std::shared_ptr> skipPrevious() override; + std::shared_ptr> skipNext() override; + std::shared_ptr> replayCurrent() override; + std::shared_ptr> seekTo(double index) override; + std::shared_ptr> updateSettings(const TtsSettings& settings) override; + ListenerSubscription addOnStateChangedListener(const std::function& listener) override; + ListenerSubscription addOnProgressChangedListener(const std::function& listener) override; + ListenerSubscription addOnErrorListener(const std::function& listener) override; + + private: + jni::global_ref _javaPart; + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JListenerSubscription.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JListenerSubscription.hpp new file mode 100644 index 000000000..08806eb10 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JListenerSubscription.hpp @@ -0,0 +1,67 @@ +/// +/// JListenerSubscription.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "ListenerSubscription.hpp" + +#include "JFunc_void.hpp" +#include +#include + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "ListenerSubscription" and the Kotlin data class "ListenerSubscription". + */ + struct JListenerSubscription final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/ListenerSubscription;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct ListenerSubscription by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + ListenerSubscription toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldRemove = clazz->getField("remove"); + jni::local_ref remove = this->getFieldValue(fieldRemove); + return ListenerSubscription( + [&]() -> std::function { + if (remove->isInstanceOf(JFunc_void_cxx::javaClassStatic())) [[likely]] { + auto downcast = jni::static_ref_cast(remove); + return downcast->cthis()->getFunction(); + } else { + auto removeRef = jni::make_global(remove); + return JNICallable(std::move(removeRef)); + } + }() + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const ListenerSubscription& value) { + using JSignature = JListenerSubscription(jni::alias_ref); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + JFunc_void_cxx::fromCpp(value.remove) + ); + } + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JTtsEngine.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsEngine.hpp new file mode 100644 index 000000000..925f8844e --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsEngine.hpp @@ -0,0 +1,61 @@ +/// +/// JTtsEngine.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TtsEngine.hpp" + +#include + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "TtsEngine" and the Kotlin data class "TtsEngine". + */ + struct JTtsEngine final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/TtsEngine;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct TtsEngine by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + TtsEngine toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldName = clazz->getField("name"); + jni::local_ref name = this->getFieldValue(fieldName); + static const auto fieldLabel = clazz->getField("label"); + jni::local_ref label = this->getFieldValue(fieldLabel); + return TtsEngine( + name->toStdString(), + label->toStdString() + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const TtsEngine& value) { + using JSignature = JTtsEngine(jni::alias_ref, jni::alias_ref); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + jni::make_jstring(value.name), + jni::make_jstring(value.label) + ); + } + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JTtsMetadata.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsMetadata.hpp new file mode 100644 index 000000000..39c928b1c --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsMetadata.hpp @@ -0,0 +1,66 @@ +/// +/// JTtsMetadata.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TtsMetadata.hpp" + +#include +#include + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "TtsMetadata" and the Kotlin data class "TtsMetadata". + */ + struct JTtsMetadata final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/TtsMetadata;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct TtsMetadata by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + TtsMetadata toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldNovelName = clazz->getField("novelName"); + jni::local_ref novelName = this->getFieldValue(fieldNovelName); + static const auto fieldChapterName = clazz->getField("chapterName"); + jni::local_ref chapterName = this->getFieldValue(fieldChapterName); + static const auto fieldCoverUri = clazz->getField("coverUri"); + jni::local_ref coverUri = this->getFieldValue(fieldCoverUri); + return TtsMetadata( + novelName->toStdString(), + chapterName->toStdString(), + coverUri != nullptr ? std::make_optional(coverUri->toStdString()) : std::nullopt + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const TtsMetadata& value) { + using JSignature = JTtsMetadata(jni::alias_ref, jni::alias_ref, jni::alias_ref); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + jni::make_jstring(value.novelName), + jni::make_jstring(value.chapterName), + value.coverUri.has_value() ? jni::make_jstring(value.coverUri.value()) : nullptr + ); + } + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JTtsParagraph.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsParagraph.hpp new file mode 100644 index 000000000..4452592f4 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsParagraph.hpp @@ -0,0 +1,61 @@ +/// +/// JTtsParagraph.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TtsParagraph.hpp" + +#include + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "TtsParagraph" and the Kotlin data class "TtsParagraph". + */ + struct JTtsParagraph final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/TtsParagraph;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct TtsParagraph by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + TtsParagraph toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldId = clazz->getField("id"); + jni::local_ref id = this->getFieldValue(fieldId); + static const auto fieldText = clazz->getField("text"); + jni::local_ref text = this->getFieldValue(fieldText); + return TtsParagraph( + id->toStdString(), + text->toStdString() + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const TtsParagraph& value) { + using JSignature = JTtsParagraph(jni::alias_ref, jni::alias_ref); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + jni::make_jstring(value.id), + jni::make_jstring(value.text) + ); + } + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JTtsPlaybackState.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsPlaybackState.hpp new file mode 100644 index 000000000..b323ea9c8 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsPlaybackState.hpp @@ -0,0 +1,70 @@ +/// +/// JTtsPlaybackState.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TtsPlaybackState.hpp" + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ enum "TtsPlaybackState" and the Kotlin enum "TtsPlaybackState". + */ + struct JTtsPlaybackState final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/TtsPlaybackState;"; + + public: + /** + * Convert this Java/Kotlin-based enum to the C++ enum TtsPlaybackState. + */ + [[maybe_unused]] + [[nodiscard]] + TtsPlaybackState toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldOrdinal = clazz->getField("value"); + int ordinal = this->getFieldValue(fieldOrdinal); + return static_cast(ordinal); + } + + public: + /** + * Create a Java/Kotlin-based enum with the given C++ enum's value. + */ + [[maybe_unused]] + static jni::alias_ref fromCpp(TtsPlaybackState value) { + static const auto clazz = javaClassStatic(); + switch (value) { + case TtsPlaybackState::IDLE: + static const auto fieldIDLE = clazz->getStaticField("IDLE"); + return clazz->getStaticFieldValue(fieldIDLE); + case TtsPlaybackState::LOADING: + static const auto fieldLOADING = clazz->getStaticField("LOADING"); + return clazz->getStaticFieldValue(fieldLOADING); + case TtsPlaybackState::PLAYING: + static const auto fieldPLAYING = clazz->getStaticField("PLAYING"); + return clazz->getStaticFieldValue(fieldPLAYING); + case TtsPlaybackState::PAUSED: + static const auto fieldPAUSED = clazz->getStaticField("PAUSED"); + return clazz->getStaticFieldValue(fieldPAUSED); + case TtsPlaybackState::COMPLETED: + static const auto fieldCOMPLETED = clazz->getStaticField("COMPLETED"); + return clazz->getStaticFieldValue(fieldCOMPLETED); + case TtsPlaybackState::ERROR: + static const auto fieldERROR = clazz->getStaticField("ERROR"); + return clazz->getStaticFieldValue(fieldERROR); + default: + std::string stringValue = std::to_string(static_cast(value)); + throw std::invalid_argument("Invalid enum value (" + stringValue + "!"); + } + } + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JTtsProgress.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsProgress.hpp new file mode 100644 index 000000000..86585c61e --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsProgress.hpp @@ -0,0 +1,65 @@ +/// +/// JTtsProgress.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TtsProgress.hpp" + +#include + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "TtsProgress" and the Kotlin data class "TtsProgress". + */ + struct JTtsProgress final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/TtsProgress;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct TtsProgress by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + TtsProgress toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldIndex = clazz->getField("index"); + double index = this->getFieldValue(fieldIndex); + static const auto fieldTotal = clazz->getField("total"); + double total = this->getFieldValue(fieldTotal); + static const auto fieldParagraphId = clazz->getField("paragraphId"); + jni::local_ref paragraphId = this->getFieldValue(fieldParagraphId); + return TtsProgress( + index, + total, + paragraphId->toStdString() + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const TtsProgress& value) { + using JSignature = JTtsProgress(double, double, jni::alias_ref); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + value.index, + value.total, + jni::make_jstring(value.paragraphId) + ); + } + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JTtsSettings.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsSettings.hpp new file mode 100644 index 000000000..61c40e8c6 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsSettings.hpp @@ -0,0 +1,70 @@ +/// +/// JTtsSettings.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TtsSettings.hpp" + +#include +#include + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "TtsSettings" and the Kotlin data class "TtsSettings". + */ + struct JTtsSettings final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/TtsSettings;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct TtsSettings by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + TtsSettings toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldEngineName = clazz->getField("engineName"); + jni::local_ref engineName = this->getFieldValue(fieldEngineName); + static const auto fieldVoiceIdentifier = clazz->getField("voiceIdentifier"); + jni::local_ref voiceIdentifier = this->getFieldValue(fieldVoiceIdentifier); + static const auto fieldRate = clazz->getField("rate"); + double rate = this->getFieldValue(fieldRate); + static const auto fieldPitch = clazz->getField("pitch"); + double pitch = this->getFieldValue(fieldPitch); + return TtsSettings( + engineName != nullptr ? std::make_optional(engineName->toStdString()) : std::nullopt, + voiceIdentifier != nullptr ? std::make_optional(voiceIdentifier->toStdString()) : std::nullopt, + rate, + pitch + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const TtsSettings& value) { + using JSignature = JTtsSettings(jni::alias_ref, jni::alias_ref, double, double); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + value.engineName.has_value() ? jni::make_jstring(value.engineName.value()) : nullptr, + value.voiceIdentifier.has_value() ? jni::make_jstring(value.voiceIdentifier.value()) : nullptr, + value.rate, + value.pitch + ); + } + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/c++/JTtsVoice.hpp b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsVoice.hpp new file mode 100644 index 000000000..79b603b6c --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/c++/JTtsVoice.hpp @@ -0,0 +1,66 @@ +/// +/// JTtsVoice.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "TtsVoice.hpp" + +#include +#include + +namespace margelo::nitro::nitrotts { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "TtsVoice" and the Kotlin data class "TtsVoice". + */ + struct JTtsVoice final: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitrotts/TtsVoice;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct TtsVoice by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + TtsVoice toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldIdentifier = clazz->getField("identifier"); + jni::local_ref identifier = this->getFieldValue(fieldIdentifier); + static const auto fieldName = clazz->getField("name"); + jni::local_ref name = this->getFieldValue(fieldName); + static const auto fieldLanguage = clazz->getField("language"); + jni::local_ref language = this->getFieldValue(fieldLanguage); + return TtsVoice( + identifier->toStdString(), + name->toStdString(), + language != nullptr ? std::make_optional(language->toStdString()) : std::nullopt + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const TtsVoice& value) { + using JSignature = JTtsVoice(jni::alias_ref, jni::alias_ref, jni::alias_ref); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + jni::make_jstring(value.identifier), + jni::make_jstring(value.name), + value.language.has_value() ? jni::make_jstring(value.language.value()) : nullptr + ); + } + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/Func_void.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/Func_void.kt new file mode 100644 index 000000000..1a2ebfb7e --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/Func_void.kt @@ -0,0 +1,80 @@ +/// +/// Func_void.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import dalvik.annotation.optimization.FastNative + + +/** + * Represents the JavaScript callback `() => void`. + * This can be either implemented in C++ (in which case it might be a callback coming from JS), + * or in Kotlin/Java (in which case it is a native callback). + */ +@DoNotStrip +@Keep +@Suppress("ClassName", "RedundantUnitReturnType") +fun interface Func_void: () -> Unit { + /** + * Call the given JS callback. + * @throws Throwable if the JS function itself throws an error, or if the JS function/runtime has already been deleted. + */ + @DoNotStrip + @Keep + override fun invoke(): Unit +} + +/** + * Represents the JavaScript callback `() => void`. + * This is implemented in C++, via a `std::function<...>`. + * The callback might be coming from JS. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "FunctionName", + "ConvertSecondaryConstructorToPrimary", "ClassName", "LocalVariableName", +) +class Func_void_cxx: Func_void { + @DoNotStrip + @Keep + private val mHybridData: HybridData + + @DoNotStrip + @Keep + private constructor(hybridData: HybridData) { + mHybridData = hybridData + } + + @DoNotStrip + @Keep + override fun invoke(): Unit + = invoke_cxx() + + @FastNative + private external fun invoke_cxx(): Unit +} + +/** + * Represents the JavaScript callback `() => void`. + * This is implemented in Java/Kotlin, via a `() -> Unit`. + * The callback is always coming from native. + */ +@DoNotStrip +@Keep +@Suppress("ClassName", "RedundantUnitReturnType", "unused") +class Func_void_java(private val function: () -> Unit): Func_void { + @DoNotStrip + @Keep + override fun invoke(): Unit { + return this.function() + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/Func_void_TtsPlaybackState.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/Func_void_TtsPlaybackState.kt new file mode 100644 index 000000000..65ef56dfe --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/Func_void_TtsPlaybackState.kt @@ -0,0 +1,80 @@ +/// +/// Func_void_TtsPlaybackState.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import dalvik.annotation.optimization.FastNative + + +/** + * Represents the JavaScript callback `(state: enum) => void`. + * This can be either implemented in C++ (in which case it might be a callback coming from JS), + * or in Kotlin/Java (in which case it is a native callback). + */ +@DoNotStrip +@Keep +@Suppress("ClassName", "RedundantUnitReturnType") +fun interface Func_void_TtsPlaybackState: (TtsPlaybackState) -> Unit { + /** + * Call the given JS callback. + * @throws Throwable if the JS function itself throws an error, or if the JS function/runtime has already been deleted. + */ + @DoNotStrip + @Keep + override fun invoke(state: TtsPlaybackState): Unit +} + +/** + * Represents the JavaScript callback `(state: enum) => void`. + * This is implemented in C++, via a `std::function<...>`. + * The callback might be coming from JS. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "FunctionName", + "ConvertSecondaryConstructorToPrimary", "ClassName", "LocalVariableName", +) +class Func_void_TtsPlaybackState_cxx: Func_void_TtsPlaybackState { + @DoNotStrip + @Keep + private val mHybridData: HybridData + + @DoNotStrip + @Keep + private constructor(hybridData: HybridData) { + mHybridData = hybridData + } + + @DoNotStrip + @Keep + override fun invoke(state: TtsPlaybackState): Unit + = invoke_cxx(state) + + @FastNative + private external fun invoke_cxx(state: TtsPlaybackState): Unit +} + +/** + * Represents the JavaScript callback `(state: enum) => void`. + * This is implemented in Java/Kotlin, via a `(TtsPlaybackState) -> Unit`. + * The callback is always coming from native. + */ +@DoNotStrip +@Keep +@Suppress("ClassName", "RedundantUnitReturnType", "unused") +class Func_void_TtsPlaybackState_java(private val function: (TtsPlaybackState) -> Unit): Func_void_TtsPlaybackState { + @DoNotStrip + @Keep + override fun invoke(state: TtsPlaybackState): Unit { + return this.function(state) + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/Func_void_TtsProgress.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/Func_void_TtsProgress.kt new file mode 100644 index 000000000..6a1b44018 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/Func_void_TtsProgress.kt @@ -0,0 +1,80 @@ +/// +/// Func_void_TtsProgress.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import dalvik.annotation.optimization.FastNative + + +/** + * Represents the JavaScript callback `(progress: struct) => void`. + * This can be either implemented in C++ (in which case it might be a callback coming from JS), + * or in Kotlin/Java (in which case it is a native callback). + */ +@DoNotStrip +@Keep +@Suppress("ClassName", "RedundantUnitReturnType") +fun interface Func_void_TtsProgress: (TtsProgress) -> Unit { + /** + * Call the given JS callback. + * @throws Throwable if the JS function itself throws an error, or if the JS function/runtime has already been deleted. + */ + @DoNotStrip + @Keep + override fun invoke(progress: TtsProgress): Unit +} + +/** + * Represents the JavaScript callback `(progress: struct) => void`. + * This is implemented in C++, via a `std::function<...>`. + * The callback might be coming from JS. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "FunctionName", + "ConvertSecondaryConstructorToPrimary", "ClassName", "LocalVariableName", +) +class Func_void_TtsProgress_cxx: Func_void_TtsProgress { + @DoNotStrip + @Keep + private val mHybridData: HybridData + + @DoNotStrip + @Keep + private constructor(hybridData: HybridData) { + mHybridData = hybridData + } + + @DoNotStrip + @Keep + override fun invoke(progress: TtsProgress): Unit + = invoke_cxx(progress) + + @FastNative + private external fun invoke_cxx(progress: TtsProgress): Unit +} + +/** + * Represents the JavaScript callback `(progress: struct) => void`. + * This is implemented in Java/Kotlin, via a `(TtsProgress) -> Unit`. + * The callback is always coming from native. + */ +@DoNotStrip +@Keep +@Suppress("ClassName", "RedundantUnitReturnType", "unused") +class Func_void_TtsProgress_java(private val function: (TtsProgress) -> Unit): Func_void_TtsProgress { + @DoNotStrip + @Keep + override fun invoke(progress: TtsProgress): Unit { + return this.function(progress) + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/Func_void_std__string.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/Func_void_std__string.kt new file mode 100644 index 000000000..aa4d9d7da --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/Func_void_std__string.kt @@ -0,0 +1,80 @@ +/// +/// Func_void_std__string.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import dalvik.annotation.optimization.FastNative + + +/** + * Represents the JavaScript callback `(message: string) => void`. + * This can be either implemented in C++ (in which case it might be a callback coming from JS), + * or in Kotlin/Java (in which case it is a native callback). + */ +@DoNotStrip +@Keep +@Suppress("ClassName", "RedundantUnitReturnType") +fun interface Func_void_std__string: (String) -> Unit { + /** + * Call the given JS callback. + * @throws Throwable if the JS function itself throws an error, or if the JS function/runtime has already been deleted. + */ + @DoNotStrip + @Keep + override fun invoke(message: String): Unit +} + +/** + * Represents the JavaScript callback `(message: string) => void`. + * This is implemented in C++, via a `std::function<...>`. + * The callback might be coming from JS. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "FunctionName", + "ConvertSecondaryConstructorToPrimary", "ClassName", "LocalVariableName", +) +class Func_void_std__string_cxx: Func_void_std__string { + @DoNotStrip + @Keep + private val mHybridData: HybridData + + @DoNotStrip + @Keep + private constructor(hybridData: HybridData) { + mHybridData = hybridData + } + + @DoNotStrip + @Keep + override fun invoke(message: String): Unit + = invoke_cxx(message) + + @FastNative + private external fun invoke_cxx(message: String): Unit +} + +/** + * Represents the JavaScript callback `(message: string) => void`. + * This is implemented in Java/Kotlin, via a `(String) -> Unit`. + * The callback is always coming from native. + */ +@DoNotStrip +@Keep +@Suppress("ClassName", "RedundantUnitReturnType", "unused") +class Func_void_std__string_java(private val function: (String) -> Unit): Func_void_std__string { + @DoNotStrip + @Keep + override fun invoke(message: String): Unit { + return this.function(message) + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/HybridTtsFactorySpec.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/HybridTtsFactorySpec.kt new file mode 100644 index 000000000..b143c15ac --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/HybridTtsFactorySpec.kt @@ -0,0 +1,63 @@ +/// +/// HybridTtsFactorySpec.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.core.Promise +import com.margelo.nitro.core.HybridObject + +/** + * A Kotlin class representing the TtsFactory HybridObject. + * Implement this abstract class to create Kotlin-based instances of TtsFactory. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet", + "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName" +) +abstract class HybridTtsFactorySpec: HybridObject() { + // Properties + + + // Methods + @DoNotStrip + @Keep + abstract fun createSession(): Promise + + @DoNotStrip + @Keep + abstract fun getEngines(): Promise> + + @DoNotStrip + @Keep + abstract fun getVoices(engineName: String?): Promise> + + // Default implementation of `HybridObject.toString()` + override fun toString(): String { + return "[HybridObject TtsFactory]" + } + + // C++ backing class + @DoNotStrip + @Keep + protected open class CxxPart(javaPart: HybridTtsFactorySpec): HybridObject.CxxPart(javaPart) { + // C++ JHybridTtsFactorySpec::CxxPart::initHybrid(...) + external override fun initHybrid(): HybridData + } + override fun createCxxPart(): CxxPart { + return CxxPart(this) + } + + companion object { + protected const val TAG = "HybridTtsFactorySpec" + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/HybridTtsSessionSpec.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/HybridTtsSessionSpec.kt new file mode 100644 index 000000000..80abd3022 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/HybridTtsSessionSpec.kt @@ -0,0 +1,114 @@ +/// +/// HybridTtsSessionSpec.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.core.Promise +import com.margelo.nitro.core.HybridObject + +/** + * A Kotlin class representing the TtsSession HybridObject. + * Implement this abstract class to create Kotlin-based instances of TtsSession. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet", + "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName" +) +abstract class HybridTtsSessionSpec: HybridObject() { + // Properties + + + // Methods + @DoNotStrip + @Keep + abstract fun load(paragraphs: Array, initialIndex: Double, metadata: TtsMetadata, settings: TtsSettings): Promise + + @DoNotStrip + @Keep + abstract fun play(): Promise + + @DoNotStrip + @Keep + abstract fun pause(): Promise + + @DoNotStrip + @Keep + abstract fun stop(): Promise + + @DoNotStrip + @Keep + abstract fun skipPrevious(): Promise + + @DoNotStrip + @Keep + abstract fun skipNext(): Promise + + @DoNotStrip + @Keep + abstract fun replayCurrent(): Promise + + @DoNotStrip + @Keep + abstract fun seekTo(index: Double): Promise + + @DoNotStrip + @Keep + abstract fun updateSettings(settings: TtsSettings): Promise + + abstract fun addOnStateChangedListener(listener: (state: TtsPlaybackState) -> Unit): ListenerSubscription + + @DoNotStrip + @Keep + private fun addOnStateChangedListener_cxx(listener: Func_void_TtsPlaybackState): ListenerSubscription { + val __result = addOnStateChangedListener(listener) + return __result + } + + abstract fun addOnProgressChangedListener(listener: (progress: TtsProgress) -> Unit): ListenerSubscription + + @DoNotStrip + @Keep + private fun addOnProgressChangedListener_cxx(listener: Func_void_TtsProgress): ListenerSubscription { + val __result = addOnProgressChangedListener(listener) + return __result + } + + abstract fun addOnErrorListener(listener: (message: String) -> Unit): ListenerSubscription + + @DoNotStrip + @Keep + private fun addOnErrorListener_cxx(listener: Func_void_std__string): ListenerSubscription { + val __result = addOnErrorListener(listener) + return __result + } + + // Default implementation of `HybridObject.toString()` + override fun toString(): String { + return "[HybridObject TtsSession]" + } + + // C++ backing class + @DoNotStrip + @Keep + protected open class CxxPart(javaPart: HybridTtsSessionSpec): HybridObject.CxxPart(javaPart) { + // C++ JHybridTtsSessionSpec::CxxPart::initHybrid(...) + external override fun initHybrid(): HybridData + } + override fun createCxxPart(): CxxPart { + return CxxPart(this) + } + + companion object { + protected const val TAG = "HybridTtsSessionSpec" + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/ListenerSubscription.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/ListenerSubscription.kt new file mode 100644 index 000000000..c8510ff4f --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/ListenerSubscription.kt @@ -0,0 +1,55 @@ +/// +/// ListenerSubscription.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "ListenerSubscription". + */ +@DoNotStrip +@Keep +data class ListenerSubscription( + @DoNotStrip + @Keep + val remove: Func_void +) { + /** + * Create a new instance of ListenerSubscription from Kotlin + */ + constructor(remove: () -> Unit): + this(Func_void_java(remove)) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is ListenerSubscription) return false + return Objects.deepEquals(this.remove, other.remove) + } + + override fun hashCode(): Int { + return arrayOf( + remove + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(remove: Func_void): ListenerSubscription { + return ListenerSubscription(remove) + } + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/NitroTtsOnLoad.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/NitroTtsOnLoad.kt new file mode 100644 index 000000000..bae46fd1b --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/NitroTtsOnLoad.kt @@ -0,0 +1,35 @@ +/// +/// NitroTtsOnLoad.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import android.util.Log + +internal class NitroTtsOnLoad { + companion object { + private const val TAG = "NitroTtsOnLoad" + private var didLoad = false + /** + * Initializes the native part of "NitroTts". + * This method is idempotent and can be called more than once. + */ + @JvmStatic + fun initializeNative() { + if (didLoad) return + try { + Log.i(TAG, "Loading NitroTts C++ library...") + System.loadLibrary("NitroTts") + Log.i(TAG, "Successfully loaded NitroTts C++ library!") + didLoad = true + } catch (e: Error) { + Log.e(TAG, "Failed to load NitroTts C++ library! Is it properly installed and linked? " + + "Is the name correct? (see `CMakeLists.txt`, at `add_library(...)`)", e) + throw e + } + } + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsEngine.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsEngine.kt new file mode 100644 index 000000000..808d1ed4b --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsEngine.kt @@ -0,0 +1,56 @@ +/// +/// TtsEngine.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "TtsEngine". + */ +@DoNotStrip +@Keep +data class TtsEngine( + @DoNotStrip + @Keep + val name: String, + @DoNotStrip + @Keep + val label: String +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TtsEngine) return false + return Objects.deepEquals(this.name, other.name) + && Objects.deepEquals(this.label, other.label) + } + + override fun hashCode(): Int { + return arrayOf( + name, + label + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(name: String, label: String): TtsEngine { + return TtsEngine(name, label) + } + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsMetadata.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsMetadata.kt new file mode 100644 index 000000000..0783875f3 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsMetadata.kt @@ -0,0 +1,61 @@ +/// +/// TtsMetadata.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "TtsMetadata". + */ +@DoNotStrip +@Keep +data class TtsMetadata( + @DoNotStrip + @Keep + val novelName: String, + @DoNotStrip + @Keep + val chapterName: String, + @DoNotStrip + @Keep + val coverUri: String? +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TtsMetadata) return false + return Objects.deepEquals(this.novelName, other.novelName) + && Objects.deepEquals(this.chapterName, other.chapterName) + && Objects.deepEquals(this.coverUri, other.coverUri) + } + + override fun hashCode(): Int { + return arrayOf( + novelName, + chapterName, + coverUri + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(novelName: String, chapterName: String, coverUri: String?): TtsMetadata { + return TtsMetadata(novelName, chapterName, coverUri) + } + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsParagraph.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsParagraph.kt new file mode 100644 index 000000000..f8fef34b0 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsParagraph.kt @@ -0,0 +1,56 @@ +/// +/// TtsParagraph.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "TtsParagraph". + */ +@DoNotStrip +@Keep +data class TtsParagraph( + @DoNotStrip + @Keep + val id: String, + @DoNotStrip + @Keep + val text: String +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TtsParagraph) return false + return Objects.deepEquals(this.id, other.id) + && Objects.deepEquals(this.text, other.text) + } + + override fun hashCode(): Int { + return arrayOf( + id, + text + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(id: String, text: String): TtsParagraph { + return TtsParagraph(id, text) + } + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsPlaybackState.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsPlaybackState.kt new file mode 100644 index 000000000..89594b49d --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsPlaybackState.kt @@ -0,0 +1,27 @@ +/// +/// TtsPlaybackState.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip + +/** + * Represents the JavaScript enum/union "TtsPlaybackState". + */ +@DoNotStrip +@Keep +enum class TtsPlaybackState(@DoNotStrip @Keep val value: Int) { + IDLE(0), + LOADING(1), + PLAYING(2), + PAUSED(3), + COMPLETED(4), + ERROR(5); + + companion object +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsProgress.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsProgress.kt new file mode 100644 index 000000000..a54bb9c15 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsProgress.kt @@ -0,0 +1,61 @@ +/// +/// TtsProgress.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "TtsProgress". + */ +@DoNotStrip +@Keep +data class TtsProgress( + @DoNotStrip + @Keep + val index: Double, + @DoNotStrip + @Keep + val total: Double, + @DoNotStrip + @Keep + val paragraphId: String +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TtsProgress) return false + return Objects.deepEquals(this.index, other.index) + && Objects.deepEquals(this.total, other.total) + && Objects.deepEquals(this.paragraphId, other.paragraphId) + } + + override fun hashCode(): Int { + return arrayOf( + index, + total, + paragraphId + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(index: Double, total: Double, paragraphId: String): TtsProgress { + return TtsProgress(index, total, paragraphId) + } + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsSettings.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsSettings.kt new file mode 100644 index 000000000..3ae742b66 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsSettings.kt @@ -0,0 +1,66 @@ +/// +/// TtsSettings.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "TtsSettings". + */ +@DoNotStrip +@Keep +data class TtsSettings( + @DoNotStrip + @Keep + val engineName: String?, + @DoNotStrip + @Keep + val voiceIdentifier: String?, + @DoNotStrip + @Keep + val rate: Double, + @DoNotStrip + @Keep + val pitch: Double +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TtsSettings) return false + return Objects.deepEquals(this.engineName, other.engineName) + && Objects.deepEquals(this.voiceIdentifier, other.voiceIdentifier) + && Objects.deepEquals(this.rate, other.rate) + && Objects.deepEquals(this.pitch, other.pitch) + } + + override fun hashCode(): Int { + return arrayOf( + engineName, + voiceIdentifier, + rate, + pitch + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(engineName: String?, voiceIdentifier: String?, rate: Double, pitch: Double): TtsSettings { + return TtsSettings(engineName, voiceIdentifier, rate, pitch) + } + } +} diff --git a/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsVoice.kt b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsVoice.kt new file mode 100644 index 000000000..3f74dd2d3 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotts/TtsVoice.kt @@ -0,0 +1,61 @@ +/// +/// TtsVoice.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nitrotts + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import java.util.Objects + + +/** + * Represents the JavaScript object/struct "TtsVoice". + */ +@DoNotStrip +@Keep +data class TtsVoice( + @DoNotStrip + @Keep + val identifier: String, + @DoNotStrip + @Keep + val name: String, + @DoNotStrip + @Keep + val language: String? +) { + /* primary constructor */ + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TtsVoice) return false + return Objects.deepEquals(this.identifier, other.identifier) + && Objects.deepEquals(this.name, other.name) + && Objects.deepEquals(this.language, other.language) + } + + override fun hashCode(): Int { + return arrayOf( + identifier, + name, + language + ).contentDeepHashCode() + } + + companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(identifier: String, name: String, language: String?): TtsVoice { + return TtsVoice(identifier, name, language) + } + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/NitroTts+autolinking.rb b/modules/nitro-tts/nitrogen/generated/ios/NitroTts+autolinking.rb new file mode 100644 index 000000000..4b8660872 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/NitroTts+autolinking.rb @@ -0,0 +1,62 @@ +# +# NitroTts+autolinking.rb +# This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +# https://github.com/mrousavy/nitro +# Copyright © Marc Rousavy @ Margelo +# + +# This is a Ruby script that adds all files generated by Nitrogen +# to the given podspec. +# +# To use it, add this to your .podspec: +# ```ruby +# Pod::Spec.new do |spec| +# # ... +# +# # Add all files generated by Nitrogen +# load 'nitrogen/generated/ios/NitroTts+autolinking.rb' +# add_nitrogen_files(spec) +# end +# ``` + +def add_nitrogen_files(spec) + Pod::UI.puts "[NitroModules] 🔥 NitroTts is boosted by nitro!" + + spec.dependency "NitroModules" + + current_source_files = Array(spec.attributes_hash['source_files']) + spec.source_files = current_source_files + [ + # Generated cross-platform specs + "nitrogen/generated/shared/**/*.{h,hpp,c,cpp,swift}", + # Generated bridges for the cross-platform specs + "nitrogen/generated/ios/**/*.{h,hpp,c,cpp,mm,swift}", + ] + + current_public_header_files = Array(spec.attributes_hash['public_header_files']) + spec.public_header_files = current_public_header_files + [ + # Generated specs + "nitrogen/generated/shared/**/*.{h,hpp}", + # Swift to C++ bridging helpers + "nitrogen/generated/ios/NitroTts-Swift-Cxx-Bridge.hpp" + ] + + current_private_header_files = Array(spec.attributes_hash['private_header_files']) + spec.private_header_files = current_private_header_files + [ + # iOS specific specs + "nitrogen/generated/ios/c++/**/*.{h,hpp}", + # Views are framework-specific and should be private + "nitrogen/generated/shared/**/views/**/*" + ] + + current_pod_target_xcconfig = spec.attributes_hash['pod_target_xcconfig'] || {} + spec.pod_target_xcconfig = current_pod_target_xcconfig.merge({ + # Use C++ 20 + "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", + # Enables C++ <-> Swift interop (by default it's only ObjC) + "SWIFT_OBJC_INTEROP_MODE" => "objcxx", + # Enables stricter modular headers + "DEFINES_MODULE" => "YES", + # Disable auto-generated ObjC header for Swift (Static linkage on Xcode 26.4 breaks here) + "SWIFT_INSTALL_OBJC_HEADER" => "NO", + }) +end diff --git a/modules/nitro-tts/nitrogen/generated/ios/NitroTts-Swift-Cxx-Bridge.cpp b/modules/nitro-tts/nitrogen/generated/ios/NitroTts-Swift-Cxx-Bridge.cpp new file mode 100644 index 000000000..cc4e8ddab --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/NitroTts-Swift-Cxx-Bridge.cpp @@ -0,0 +1,114 @@ +/// +/// NitroTts-Swift-Cxx-Bridge.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "NitroTts-Swift-Cxx-Bridge.hpp" + +// Include C++ implementation defined types +#include "HybridTtsFactorySpecSwift.hpp" +#include "HybridTtsSessionSpecSwift.hpp" +#include "NitroTts-Swift-Cxx-Umbrella.hpp" +#include + +namespace margelo::nitro::nitrotts::bridge::swift { + + // pragma MARK: std::shared_ptr + std::shared_ptr create_std__shared_ptr_HybridTtsSessionSpec_(void* NON_NULL swiftUnsafePointer) noexcept { + NitroTts::HybridTtsSessionSpec_cxx swiftPart = NitroTts::HybridTtsSessionSpec_cxx::fromUnsafe(swiftUnsafePointer); + return std::make_shared(swiftPart); + } + void* NON_NULL get_std__shared_ptr_HybridTtsSessionSpec_(std__shared_ptr_HybridTtsSessionSpec_ cppType) { + std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType); + #ifdef NITRO_DEBUG + if (swiftWrapper == nullptr) [[unlikely]] { + throw std::runtime_error("Class \"HybridTtsSessionSpec\" is not implemented in Swift!"); + } + #endif + NitroTts::HybridTtsSessionSpec_cxx& swiftPart = swiftWrapper->getSwiftPart(); + return swiftPart.toUnsafe(); + } + + // pragma MARK: std::function& /* result */)> + Func_void_std__shared_ptr_HybridTtsSessionSpec_ create_Func_void_std__shared_ptr_HybridTtsSessionSpec_(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = NitroTts::Func_void_std__shared_ptr_HybridTtsSessionSpec_::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](const std::shared_ptr& result) mutable -> void { + swiftClosure.call(result); + }; + } + + // pragma MARK: std::function + Func_void_std__exception_ptr create_Func_void_std__exception_ptr(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = NitroTts::Func_void_std__exception_ptr::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](const std::exception_ptr& error) mutable -> void { + swiftClosure.call(error); + }; + } + + // pragma MARK: std::function& /* result */)> + Func_void_std__vector_TtsEngine_ create_Func_void_std__vector_TtsEngine_(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = NitroTts::Func_void_std__vector_TtsEngine_::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](const std::vector& result) mutable -> void { + swiftClosure.call(result); + }; + } + + // pragma MARK: std::function& /* result */)> + Func_void_std__vector_TtsVoice_ create_Func_void_std__vector_TtsVoice_(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = NitroTts::Func_void_std__vector_TtsVoice_::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](const std::vector& result) mutable -> void { + swiftClosure.call(result); + }; + } + + // pragma MARK: std::shared_ptr + std::shared_ptr create_std__shared_ptr_HybridTtsFactorySpec_(void* NON_NULL swiftUnsafePointer) noexcept { + NitroTts::HybridTtsFactorySpec_cxx swiftPart = NitroTts::HybridTtsFactorySpec_cxx::fromUnsafe(swiftUnsafePointer); + return std::make_shared(swiftPart); + } + void* NON_NULL get_std__shared_ptr_HybridTtsFactorySpec_(std__shared_ptr_HybridTtsFactorySpec_ cppType) { + std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType); + #ifdef NITRO_DEBUG + if (swiftWrapper == nullptr) [[unlikely]] { + throw std::runtime_error("Class \"HybridTtsFactorySpec\" is not implemented in Swift!"); + } + #endif + NitroTts::HybridTtsFactorySpec_cxx& swiftPart = swiftWrapper->getSwiftPart(); + return swiftPart.toUnsafe(); + } + + // pragma MARK: std::function + Func_void create_Func_void(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = NitroTts::Func_void::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)]() mutable -> void { + swiftClosure.call(); + }; + } + + // pragma MARK: std::function + Func_void_TtsPlaybackState create_Func_void_TtsPlaybackState(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = NitroTts::Func_void_TtsPlaybackState::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](TtsPlaybackState state) mutable -> void { + swiftClosure.call(static_cast(state)); + }; + } + + // pragma MARK: std::function + Func_void_TtsProgress create_Func_void_TtsProgress(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = NitroTts::Func_void_TtsProgress::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](const TtsProgress& progress) mutable -> void { + swiftClosure.call(progress); + }; + } + + // pragma MARK: std::function + Func_void_std__string create_Func_void_std__string(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = NitroTts::Func_void_std__string::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](const std::string& message) mutable -> void { + swiftClosure.call(message); + }; + } + +} // namespace margelo::nitro::nitrotts::bridge::swift diff --git a/modules/nitro-tts/nitrogen/generated/ios/NitroTts-Swift-Cxx-Bridge.hpp b/modules/nitro-tts/nitrogen/generated/ios/NitroTts-Swift-Cxx-Bridge.hpp new file mode 100644 index 000000000..97d801694 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/NitroTts-Swift-Cxx-Bridge.hpp @@ -0,0 +1,400 @@ +/// +/// NitroTts-Swift-Cxx-Bridge.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +// Forward declarations of C++ defined types +// Forward declaration of `HybridTtsFactorySpec` to properly resolve imports. +namespace margelo::nitro::nitrotts { class HybridTtsFactorySpec; } +// Forward declaration of `HybridTtsSessionSpec` to properly resolve imports. +namespace margelo::nitro::nitrotts { class HybridTtsSessionSpec; } +// Forward declaration of `ListenerSubscription` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct ListenerSubscription; } +// Forward declaration of `TtsEngine` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsEngine; } +// Forward declaration of `TtsParagraph` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsParagraph; } +// Forward declaration of `TtsPlaybackState` to properly resolve imports. +namespace margelo::nitro::nitrotts { enum class TtsPlaybackState; } +// Forward declaration of `TtsProgress` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsProgress; } +// Forward declaration of `TtsVoice` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsVoice; } + +// Forward declarations of Swift defined types +// Forward declaration of `HybridTtsFactorySpec_cxx` to properly resolve imports. +namespace NitroTts { class HybridTtsFactorySpec_cxx; } +// Forward declaration of `HybridTtsSessionSpec_cxx` to properly resolve imports. +namespace NitroTts { class HybridTtsSessionSpec_cxx; } + +// Include C++ defined types +#include "HybridTtsFactorySpec.hpp" +#include "HybridTtsSessionSpec.hpp" +#include "ListenerSubscription.hpp" +#include "TtsEngine.hpp" +#include "TtsParagraph.hpp" +#include "TtsPlaybackState.hpp" +#include "TtsProgress.hpp" +#include "TtsVoice.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Contains specialized versions of C++ templated types so they can be accessed from Swift, + * as well as helper functions to interact with those C++ types from Swift. + */ +namespace margelo::nitro::nitrotts::bridge::swift { + + // pragma MARK: std::shared_ptr + /** + * Specialized version of `std::shared_ptr`. + */ + using std__shared_ptr_HybridTtsSessionSpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_HybridTtsSessionSpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_HybridTtsSessionSpec_(std__shared_ptr_HybridTtsSessionSpec_ cppType); + + // pragma MARK: std::weak_ptr + using std__weak_ptr_HybridTtsSessionSpec_ = std::weak_ptr; + inline std__weak_ptr_HybridTtsSessionSpec_ weakify_std__shared_ptr_HybridTtsSessionSpec_(const std::shared_ptr& strong) noexcept { return strong; } + + // pragma MARK: std::shared_ptr>> + /** + * Specialized version of `std::shared_ptr>>`. + */ + using std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec___ = std::shared_ptr>>; + inline std::shared_ptr>> create_std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec___() noexcept { + return Promise>::create(); + } + inline PromiseHolder> wrap_std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec___(std::shared_ptr>> promise) noexcept { + return PromiseHolder>(std::move(promise)); + } + + // pragma MARK: std::function& /* result */)> + /** + * Specialized version of `std::function&)>`. + */ + using Func_void_std__shared_ptr_HybridTtsSessionSpec_ = std::function& /* result */)>; + /** + * Wrapper class for a `std::function& / * result * /)>`, this can be used from Swift. + */ + class Func_void_std__shared_ptr_HybridTtsSessionSpec__Wrapper final { + public: + explicit Func_void_std__shared_ptr_HybridTtsSessionSpec__Wrapper(std::function& /* result */)>&& func): _function(std::make_unique& /* result */)>>(std::move(func))) {} + inline void call(std::shared_ptr result) const noexcept { + _function->operator()(result); + } + private: + std::unique_ptr& /* result */)>> _function; + } SWIFT_NONCOPYABLE; + Func_void_std__shared_ptr_HybridTtsSessionSpec_ create_Func_void_std__shared_ptr_HybridTtsSessionSpec_(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_std__shared_ptr_HybridTtsSessionSpec__Wrapper wrap_Func_void_std__shared_ptr_HybridTtsSessionSpec_(Func_void_std__shared_ptr_HybridTtsSessionSpec_ value) noexcept { + return Func_void_std__shared_ptr_HybridTtsSessionSpec__Wrapper(std::move(value)); + } + + // pragma MARK: std::function + /** + * Specialized version of `std::function`. + */ + using Func_void_std__exception_ptr = std::function; + /** + * Wrapper class for a `std::function`, this can be used from Swift. + */ + class Func_void_std__exception_ptr_Wrapper final { + public: + explicit Func_void_std__exception_ptr_Wrapper(std::function&& func): _function(std::make_unique>(std::move(func))) {} + inline void call(std::exception_ptr error) const noexcept { + _function->operator()(error); + } + private: + std::unique_ptr> _function; + } SWIFT_NONCOPYABLE; + Func_void_std__exception_ptr create_Func_void_std__exception_ptr(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_std__exception_ptr_Wrapper wrap_Func_void_std__exception_ptr(Func_void_std__exception_ptr value) noexcept { + return Func_void_std__exception_ptr_Wrapper(std::move(value)); + } + + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_TtsEngine_ = std::vector; + inline std::vector create_std__vector_TtsEngine_(size_t size) noexcept { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::shared_ptr>> + /** + * Specialized version of `std::shared_ptr>>`. + */ + using std__shared_ptr_Promise_std__vector_TtsEngine___ = std::shared_ptr>>; + inline std::shared_ptr>> create_std__shared_ptr_Promise_std__vector_TtsEngine___() noexcept { + return Promise>::create(); + } + inline PromiseHolder> wrap_std__shared_ptr_Promise_std__vector_TtsEngine___(std::shared_ptr>> promise) noexcept { + return PromiseHolder>(std::move(promise)); + } + + // pragma MARK: std::function& /* result */)> + /** + * Specialized version of `std::function&)>`. + */ + using Func_void_std__vector_TtsEngine_ = std::function& /* result */)>; + /** + * Wrapper class for a `std::function& / * result * /)>`, this can be used from Swift. + */ + class Func_void_std__vector_TtsEngine__Wrapper final { + public: + explicit Func_void_std__vector_TtsEngine__Wrapper(std::function& /* result */)>&& func): _function(std::make_unique& /* result */)>>(std::move(func))) {} + inline void call(std::vector result) const noexcept { + _function->operator()(result); + } + private: + std::unique_ptr& /* result */)>> _function; + } SWIFT_NONCOPYABLE; + Func_void_std__vector_TtsEngine_ create_Func_void_std__vector_TtsEngine_(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_std__vector_TtsEngine__Wrapper wrap_Func_void_std__vector_TtsEngine_(Func_void_std__vector_TtsEngine_ value) noexcept { + return Func_void_std__vector_TtsEngine__Wrapper(std::move(value)); + } + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_std__string_ = std::optional; + inline std::optional create_std__optional_std__string_(const std::string& value) noexcept { + return std::optional(value); + } + inline bool has_value_std__optional_std__string_(const std::optional& optional) noexcept { + return optional.has_value(); + } + inline std::string get_std__optional_std__string_(const std::optional& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_TtsVoice_ = std::vector; + inline std::vector create_std__vector_TtsVoice_(size_t size) noexcept { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::shared_ptr>> + /** + * Specialized version of `std::shared_ptr>>`. + */ + using std__shared_ptr_Promise_std__vector_TtsVoice___ = std::shared_ptr>>; + inline std::shared_ptr>> create_std__shared_ptr_Promise_std__vector_TtsVoice___() noexcept { + return Promise>::create(); + } + inline PromiseHolder> wrap_std__shared_ptr_Promise_std__vector_TtsVoice___(std::shared_ptr>> promise) noexcept { + return PromiseHolder>(std::move(promise)); + } + + // pragma MARK: std::function& /* result */)> + /** + * Specialized version of `std::function&)>`. + */ + using Func_void_std__vector_TtsVoice_ = std::function& /* result */)>; + /** + * Wrapper class for a `std::function& / * result * /)>`, this can be used from Swift. + */ + class Func_void_std__vector_TtsVoice__Wrapper final { + public: + explicit Func_void_std__vector_TtsVoice__Wrapper(std::function& /* result */)>&& func): _function(std::make_unique& /* result */)>>(std::move(func))) {} + inline void call(std::vector result) const noexcept { + _function->operator()(result); + } + private: + std::unique_ptr& /* result */)>> _function; + } SWIFT_NONCOPYABLE; + Func_void_std__vector_TtsVoice_ create_Func_void_std__vector_TtsVoice_(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_std__vector_TtsVoice__Wrapper wrap_Func_void_std__vector_TtsVoice_(Func_void_std__vector_TtsVoice_ value) noexcept { + return Func_void_std__vector_TtsVoice__Wrapper(std::move(value)); + } + + // pragma MARK: std::shared_ptr + /** + * Specialized version of `std::shared_ptr`. + */ + using std__shared_ptr_HybridTtsFactorySpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_HybridTtsFactorySpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_HybridTtsFactorySpec_(std__shared_ptr_HybridTtsFactorySpec_ cppType); + + // pragma MARK: std::weak_ptr + using std__weak_ptr_HybridTtsFactorySpec_ = std::weak_ptr; + inline std__weak_ptr_HybridTtsFactorySpec_ weakify_std__shared_ptr_HybridTtsFactorySpec_(const std::shared_ptr& strong) noexcept { return strong; } + + // pragma MARK: Result>>> + using Result_std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec____ = Result>>>; + inline Result_std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec____ create_Result_std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec____(const std::shared_ptr>>& value) noexcept { + return Result>>>::withValue(value); + } + inline Result_std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec____ create_Result_std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec____(const std::exception_ptr& error) noexcept { + return Result>>>::withError(error); + } + + // pragma MARK: Result>>> + using Result_std__shared_ptr_Promise_std__vector_TtsEngine____ = Result>>>; + inline Result_std__shared_ptr_Promise_std__vector_TtsEngine____ create_Result_std__shared_ptr_Promise_std__vector_TtsEngine____(const std::shared_ptr>>& value) noexcept { + return Result>>>::withValue(value); + } + inline Result_std__shared_ptr_Promise_std__vector_TtsEngine____ create_Result_std__shared_ptr_Promise_std__vector_TtsEngine____(const std::exception_ptr& error) noexcept { + return Result>>>::withError(error); + } + + // pragma MARK: Result>>> + using Result_std__shared_ptr_Promise_std__vector_TtsVoice____ = Result>>>; + inline Result_std__shared_ptr_Promise_std__vector_TtsVoice____ create_Result_std__shared_ptr_Promise_std__vector_TtsVoice____(const std::shared_ptr>>& value) noexcept { + return Result>>>::withValue(value); + } + inline Result_std__shared_ptr_Promise_std__vector_TtsVoice____ create_Result_std__shared_ptr_Promise_std__vector_TtsVoice____(const std::exception_ptr& error) noexcept { + return Result>>>::withError(error); + } + + // pragma MARK: std::shared_ptr> + /** + * Specialized version of `std::shared_ptr>`. + */ + using std__shared_ptr_Promise_void__ = std::shared_ptr>; + inline std::shared_ptr> create_std__shared_ptr_Promise_void__() noexcept { + return Promise::create(); + } + inline PromiseHolder wrap_std__shared_ptr_Promise_void__(std::shared_ptr> promise) noexcept { + return PromiseHolder(std::move(promise)); + } + + // pragma MARK: std::function + /** + * Specialized version of `std::function`. + */ + using Func_void = std::function; + /** + * Wrapper class for a `std::function`, this can be used from Swift. + */ + class Func_void_Wrapper final { + public: + explicit Func_void_Wrapper(std::function&& func): _function(std::make_unique>(std::move(func))) {} + inline void call() const noexcept { + _function->operator()(); + } + private: + std::unique_ptr> _function; + } SWIFT_NONCOPYABLE; + Func_void create_Func_void(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_Wrapper wrap_Func_void(Func_void value) noexcept { + return Func_void_Wrapper(std::move(value)); + } + + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_TtsParagraph_ = std::vector; + inline std::vector create_std__vector_TtsParagraph_(size_t size) noexcept { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::function + /** + * Specialized version of `std::function`. + */ + using Func_void_TtsPlaybackState = std::function; + /** + * Wrapper class for a `std::function`, this can be used from Swift. + */ + class Func_void_TtsPlaybackState_Wrapper final { + public: + explicit Func_void_TtsPlaybackState_Wrapper(std::function&& func): _function(std::make_unique>(std::move(func))) {} + inline void call(int state) const noexcept { + _function->operator()(static_cast(state)); + } + private: + std::unique_ptr> _function; + } SWIFT_NONCOPYABLE; + Func_void_TtsPlaybackState create_Func_void_TtsPlaybackState(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_TtsPlaybackState_Wrapper wrap_Func_void_TtsPlaybackState(Func_void_TtsPlaybackState value) noexcept { + return Func_void_TtsPlaybackState_Wrapper(std::move(value)); + } + + // pragma MARK: std::function + /** + * Specialized version of `std::function`. + */ + using Func_void_TtsProgress = std::function; + /** + * Wrapper class for a `std::function`, this can be used from Swift. + */ + class Func_void_TtsProgress_Wrapper final { + public: + explicit Func_void_TtsProgress_Wrapper(std::function&& func): _function(std::make_unique>(std::move(func))) {} + inline void call(TtsProgress progress) const noexcept { + _function->operator()(progress); + } + private: + std::unique_ptr> _function; + } SWIFT_NONCOPYABLE; + Func_void_TtsProgress create_Func_void_TtsProgress(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_TtsProgress_Wrapper wrap_Func_void_TtsProgress(Func_void_TtsProgress value) noexcept { + return Func_void_TtsProgress_Wrapper(std::move(value)); + } + + // pragma MARK: std::function + /** + * Specialized version of `std::function`. + */ + using Func_void_std__string = std::function; + /** + * Wrapper class for a `std::function`, this can be used from Swift. + */ + class Func_void_std__string_Wrapper final { + public: + explicit Func_void_std__string_Wrapper(std::function&& func): _function(std::make_unique>(std::move(func))) {} + inline void call(std::string message) const noexcept { + _function->operator()(message); + } + private: + std::unique_ptr> _function; + } SWIFT_NONCOPYABLE; + Func_void_std__string create_Func_void_std__string(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_std__string_Wrapper wrap_Func_void_std__string(Func_void_std__string value) noexcept { + return Func_void_std__string_Wrapper(std::move(value)); + } + + // pragma MARK: Result>> + using Result_std__shared_ptr_Promise_void___ = Result>>; + inline Result_std__shared_ptr_Promise_void___ create_Result_std__shared_ptr_Promise_void___(const std::shared_ptr>& value) noexcept { + return Result>>::withValue(value); + } + inline Result_std__shared_ptr_Promise_void___ create_Result_std__shared_ptr_Promise_void___(const std::exception_ptr& error) noexcept { + return Result>>::withError(error); + } + + // pragma MARK: Result + using Result_ListenerSubscription_ = Result; + inline Result_ListenerSubscription_ create_Result_ListenerSubscription_(const ListenerSubscription& value) noexcept { + return Result::withValue(value); + } + inline Result_ListenerSubscription_ create_Result_ListenerSubscription_(const std::exception_ptr& error) noexcept { + return Result::withError(error); + } + +} // namespace margelo::nitro::nitrotts::bridge::swift diff --git a/modules/nitro-tts/nitrogen/generated/ios/NitroTts-Swift-Cxx-Umbrella.hpp b/modules/nitro-tts/nitrogen/generated/ios/NitroTts-Swift-Cxx-Umbrella.hpp new file mode 100644 index 000000000..e67cf7119 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/NitroTts-Swift-Cxx-Umbrella.hpp @@ -0,0 +1,77 @@ +/// +/// NitroTts-Swift-Cxx-Umbrella.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +// Forward declarations of C++ defined types +// Forward declaration of `HybridTtsFactorySpec` to properly resolve imports. +namespace margelo::nitro::nitrotts { class HybridTtsFactorySpec; } +// Forward declaration of `HybridTtsSessionSpec` to properly resolve imports. +namespace margelo::nitro::nitrotts { class HybridTtsSessionSpec; } +// Forward declaration of `ListenerSubscription` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct ListenerSubscription; } +// Forward declaration of `TtsEngine` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsEngine; } +// Forward declaration of `TtsMetadata` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsMetadata; } +// Forward declaration of `TtsParagraph` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsParagraph; } +// Forward declaration of `TtsPlaybackState` to properly resolve imports. +namespace margelo::nitro::nitrotts { enum class TtsPlaybackState; } +// Forward declaration of `TtsProgress` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsProgress; } +// Forward declaration of `TtsSettings` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsSettings; } +// Forward declaration of `TtsVoice` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsVoice; } + +// Include C++ defined types +#include "HybridTtsFactorySpec.hpp" +#include "HybridTtsSessionSpec.hpp" +#include "ListenerSubscription.hpp" +#include "TtsEngine.hpp" +#include "TtsMetadata.hpp" +#include "TtsParagraph.hpp" +#include "TtsPlaybackState.hpp" +#include "TtsProgress.hpp" +#include "TtsSettings.hpp" +#include "TtsVoice.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +// C++ helpers for Swift +#include "NitroTts-Swift-Cxx-Bridge.hpp" + +// Common C++ types used in Swift +#include +#include +#include +#include + +// Forward declarations of Swift defined types +// Forward declaration of `HybridTtsFactorySpec_cxx` to properly resolve imports. +namespace NitroTts { class HybridTtsFactorySpec_cxx; } +// Forward declaration of `HybridTtsSessionSpec_cxx` to properly resolve imports. +namespace NitroTts { class HybridTtsSessionSpec_cxx; } + +// Include Swift defined types +#if __has_include("NitroTts-Swift.h") +// This header is generated by Xcode/Swift on every app build. +// If it cannot be found, make sure the Swift module's name (= podspec name) is actually "NitroTts". +#include "NitroTts-Swift.h" +// Same as above, but used when building with frameworks (`use_frameworks`) +#elif __has_include() +#include +#else +#error NitroTts's autogenerated Swift header cannot be found! Make sure the Swift module's name (= podspec name) is actually "NitroTts", and try building the app first. +#endif diff --git a/modules/nitro-tts/nitrogen/generated/ios/NitroTtsAutolinking.mm b/modules/nitro-tts/nitrogen/generated/ios/NitroTtsAutolinking.mm new file mode 100644 index 000000000..5c0fb245b --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/NitroTtsAutolinking.mm @@ -0,0 +1,33 @@ +/// +/// NitroTtsAutolinking.mm +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#import +#import +#import "NitroTts-Swift-Cxx-Umbrella.hpp" +#import + +#include "HybridTtsFactorySpecSwift.hpp" + +@interface NitroTtsAutolinking : NSObject +@end + +@implementation NitroTtsAutolinking + ++ (void) load { + using namespace margelo::nitro; + using namespace margelo::nitro::nitrotts; + + HybridObjectRegistry::registerHybridObjectConstructor( + "TtsFactory", + []() -> std::shared_ptr { + std::shared_ptr hybridObject = NitroTts::NitroTtsAutolinking::createTtsFactory(); + return hybridObject; + } + ); +} + +@end diff --git a/modules/nitro-tts/nitrogen/generated/ios/NitroTtsAutolinking.swift b/modules/nitro-tts/nitrogen/generated/ios/NitroTtsAutolinking.swift new file mode 100644 index 000000000..de3a24534 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/NitroTtsAutolinking.swift @@ -0,0 +1,26 @@ +/// +/// NitroTtsAutolinking.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +// TODO: Use empty enums once Swift supports exporting them as namespaces +// See: https://github.com/swiftlang/swift/pull/83616 +public final class NitroTtsAutolinking { + public typealias bridge = margelo.nitro.nitrotts.bridge.swift + + public static func createTtsFactory() -> bridge.std__shared_ptr_HybridTtsFactorySpec_ { + let hybridObject = HybridTtsFactory() + return { () -> bridge.std__shared_ptr_HybridTtsFactorySpec_ in + let __cxxWrapped = hybridObject.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }() + } + + public static func isTtsFactoryRecyclable() -> Bool { + return HybridTtsFactory.self is any RecyclableView.Type + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/c++/HybridTtsFactorySpecSwift.cpp b/modules/nitro-tts/nitrogen/generated/ios/c++/HybridTtsFactorySpecSwift.cpp new file mode 100644 index 000000000..a4785758e --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/c++/HybridTtsFactorySpecSwift.cpp @@ -0,0 +1,11 @@ +/// +/// HybridTtsFactorySpecSwift.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridTtsFactorySpecSwift.hpp" + +namespace margelo::nitro::nitrotts { +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/ios/c++/HybridTtsFactorySpecSwift.hpp b/modules/nitro-tts/nitrogen/generated/ios/c++/HybridTtsFactorySpecSwift.hpp new file mode 100644 index 000000000..d45f642f3 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/c++/HybridTtsFactorySpecSwift.hpp @@ -0,0 +1,110 @@ +/// +/// HybridTtsFactorySpecSwift.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include "HybridTtsFactorySpec.hpp" + +// Forward declaration of `HybridTtsFactorySpec_cxx` to properly resolve imports. +namespace NitroTts { class HybridTtsFactorySpec_cxx; } + +// Forward declaration of `HybridTtsSessionSpec` to properly resolve imports. +namespace margelo::nitro::nitrotts { class HybridTtsSessionSpec; } +// Forward declaration of `TtsEngine` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsEngine; } +// Forward declaration of `TtsVoice` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsVoice; } + +#include +#include "HybridTtsSessionSpec.hpp" +#include +#include "TtsEngine.hpp" +#include +#include +#include "TtsVoice.hpp" +#include + +#include "NitroTts-Swift-Cxx-Umbrella.hpp" + +namespace margelo::nitro::nitrotts { + + /** + * The C++ part of HybridTtsFactorySpec_cxx.swift. + * + * HybridTtsFactorySpecSwift (C++) accesses HybridTtsFactorySpec_cxx (Swift), and might + * contain some additional bridging code for C++ <> Swift interop. + * + * Since this obviously introduces an overhead, I hope at some point in + * the future, HybridTtsFactorySpec_cxx can directly inherit from the C++ class HybridTtsFactorySpec + * to simplify the whole structure and memory management. + */ + class HybridTtsFactorySpecSwift: public virtual HybridTtsFactorySpec { + public: + // Constructor from a Swift instance + explicit HybridTtsFactorySpecSwift(const NitroTts::HybridTtsFactorySpec_cxx& swiftPart): + HybridObject(HybridTtsFactorySpec::TAG), + _swiftPart(swiftPart) { } + + public: + // Get the Swift part + inline NitroTts::HybridTtsFactorySpec_cxx& getSwiftPart() noexcept { + return _swiftPart; + } + + public: + inline size_t getExternalMemorySize() noexcept override { + return _swiftPart.getMemorySize(); + } + bool equals(const std::shared_ptr& other) override { + if (auto otherCast = std::dynamic_pointer_cast(other)) { + return _swiftPart.equals(otherCast->_swiftPart); + } + return false; + } + void dispose() noexcept override { + _swiftPart.dispose(); + } + std::string toString() override { + return _swiftPart.toString(); + } + + public: + // Properties + + + public: + // Methods + inline std::shared_ptr>> createSession() override { + auto __result = _swiftPart.createSession(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr>> getEngines() override { + auto __result = _swiftPart.getEngines(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr>> getVoices(const std::optional& engineName) override { + auto __result = _swiftPart.getVoices(engineName); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + + private: + NitroTts::HybridTtsFactorySpec_cxx _swiftPart; + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/ios/c++/HybridTtsSessionSpecSwift.cpp b/modules/nitro-tts/nitrogen/generated/ios/c++/HybridTtsSessionSpecSwift.cpp new file mode 100644 index 000000000..fcc810f31 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/c++/HybridTtsSessionSpecSwift.cpp @@ -0,0 +1,11 @@ +/// +/// HybridTtsSessionSpecSwift.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridTtsSessionSpecSwift.hpp" + +namespace margelo::nitro::nitrotts { +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/ios/c++/HybridTtsSessionSpecSwift.hpp b/modules/nitro-tts/nitrogen/generated/ios/c++/HybridTtsSessionSpecSwift.hpp new file mode 100644 index 000000000..6e7e9826c --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/c++/HybridTtsSessionSpecSwift.hpp @@ -0,0 +1,191 @@ +/// +/// HybridTtsSessionSpecSwift.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include "HybridTtsSessionSpec.hpp" + +// Forward declaration of `HybridTtsSessionSpec_cxx` to properly resolve imports. +namespace NitroTts { class HybridTtsSessionSpec_cxx; } + +// Forward declaration of `TtsParagraph` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsParagraph; } +// Forward declaration of `TtsMetadata` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsMetadata; } +// Forward declaration of `TtsSettings` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsSettings; } +// Forward declaration of `ListenerSubscription` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct ListenerSubscription; } +// Forward declaration of `TtsPlaybackState` to properly resolve imports. +namespace margelo::nitro::nitrotts { enum class TtsPlaybackState; } +// Forward declaration of `TtsProgress` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsProgress; } + +#include +#include "TtsParagraph.hpp" +#include +#include +#include "TtsMetadata.hpp" +#include +#include "TtsSettings.hpp" +#include "ListenerSubscription.hpp" +#include +#include "TtsPlaybackState.hpp" +#include "TtsProgress.hpp" + +#include "NitroTts-Swift-Cxx-Umbrella.hpp" + +namespace margelo::nitro::nitrotts { + + /** + * The C++ part of HybridTtsSessionSpec_cxx.swift. + * + * HybridTtsSessionSpecSwift (C++) accesses HybridTtsSessionSpec_cxx (Swift), and might + * contain some additional bridging code for C++ <> Swift interop. + * + * Since this obviously introduces an overhead, I hope at some point in + * the future, HybridTtsSessionSpec_cxx can directly inherit from the C++ class HybridTtsSessionSpec + * to simplify the whole structure and memory management. + */ + class HybridTtsSessionSpecSwift: public virtual HybridTtsSessionSpec { + public: + // Constructor from a Swift instance + explicit HybridTtsSessionSpecSwift(const NitroTts::HybridTtsSessionSpec_cxx& swiftPart): + HybridObject(HybridTtsSessionSpec::TAG), + _swiftPart(swiftPart) { } + + public: + // Get the Swift part + inline NitroTts::HybridTtsSessionSpec_cxx& getSwiftPart() noexcept { + return _swiftPart; + } + + public: + inline size_t getExternalMemorySize() noexcept override { + return _swiftPart.getMemorySize(); + } + bool equals(const std::shared_ptr& other) override { + if (auto otherCast = std::dynamic_pointer_cast(other)) { + return _swiftPart.equals(otherCast->_swiftPart); + } + return false; + } + void dispose() noexcept override { + _swiftPart.dispose(); + } + std::string toString() override { + return _swiftPart.toString(); + } + + public: + // Properties + + + public: + // Methods + inline std::shared_ptr> load(const std::vector& paragraphs, double initialIndex, const TtsMetadata& metadata, const TtsSettings& settings) override { + auto __result = _swiftPart.load(paragraphs, std::forward(initialIndex), std::forward(metadata), std::forward(settings)); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> play() override { + auto __result = _swiftPart.play(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> pause() override { + auto __result = _swiftPart.pause(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> stop() override { + auto __result = _swiftPart.stop(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> skipPrevious() override { + auto __result = _swiftPart.skipPrevious(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> skipNext() override { + auto __result = _swiftPart.skipNext(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> replayCurrent() override { + auto __result = _swiftPart.replayCurrent(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> seekTo(double index) override { + auto __result = _swiftPart.seekTo(std::forward(index)); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> updateSettings(const TtsSettings& settings) override { + auto __result = _swiftPart.updateSettings(std::forward(settings)); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline ListenerSubscription addOnStateChangedListener(const std::function& listener) override { + auto __result = _swiftPart.addOnStateChangedListener(listener); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline ListenerSubscription addOnProgressChangedListener(const std::function& listener) override { + auto __result = _swiftPart.addOnProgressChangedListener(listener); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline ListenerSubscription addOnErrorListener(const std::function& listener) override { + auto __result = _swiftPart.addOnErrorListener(listener); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + + private: + NitroTts::HybridTtsSessionSpec_cxx _swiftPart; + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void.swift new file mode 100644 index 000000000..472e14f77 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void.swift @@ -0,0 +1,46 @@ +/// +/// Func_void.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Wraps a Swift `() -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void { + public typealias bridge = margelo.nitro.nitrotts.bridge.swift + + private let closure: () -> Void + + public init(_ closure: @escaping () -> Void) { + self.closure = closure + } + + @inline(__always) + public func call() -> Void { + self.closure() + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_TtsPlaybackState.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_TtsPlaybackState.swift new file mode 100644 index 000000000..3b68f067f --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_TtsPlaybackState.swift @@ -0,0 +1,46 @@ +/// +/// Func_void_TtsPlaybackState.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Wraps a Swift `(_ state: TtsPlaybackState) -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void_TtsPlaybackState { + public typealias bridge = margelo.nitro.nitrotts.bridge.swift + + private let closure: (_ state: TtsPlaybackState) -> Void + + public init(_ closure: @escaping (_ state: TtsPlaybackState) -> Void) { + self.closure = closure + } + + @inline(__always) + public func call(state: Int32) -> Void { + self.closure(margelo.nitro.nitrotts.TtsPlaybackState(rawValue: state)!) + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void_TtsPlaybackState`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_TtsPlaybackState { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_TtsProgress.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_TtsProgress.swift new file mode 100644 index 000000000..f54758430 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_TtsProgress.swift @@ -0,0 +1,46 @@ +/// +/// Func_void_TtsProgress.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Wraps a Swift `(_ progress: TtsProgress) -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void_TtsProgress { + public typealias bridge = margelo.nitro.nitrotts.bridge.swift + + private let closure: (_ progress: TtsProgress) -> Void + + public init(_ closure: @escaping (_ progress: TtsProgress) -> Void) { + self.closure = closure + } + + @inline(__always) + public func call(progress: TtsProgress) -> Void { + self.closure(progress) + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void_TtsProgress`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_TtsProgress { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift new file mode 100644 index 000000000..9f1cd9318 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift @@ -0,0 +1,46 @@ +/// +/// Func_void_std__exception_ptr.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Wraps a Swift `(_ error: Error) -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void_std__exception_ptr { + public typealias bridge = margelo.nitro.nitrotts.bridge.swift + + private let closure: (_ error: Error) -> Void + + public init(_ closure: @escaping (_ error: Error) -> Void) { + self.closure = closure + } + + @inline(__always) + public func call(error: std.exception_ptr) -> Void { + self.closure(RuntimeError.from(cppError: error)) + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void_std__exception_ptr`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_std__exception_ptr { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_HybridTtsSessionSpec_.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_HybridTtsSessionSpec_.swift new file mode 100644 index 000000000..8732aa186 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_HybridTtsSessionSpec_.swift @@ -0,0 +1,50 @@ +/// +/// Func_void_std__shared_ptr_HybridTtsSessionSpec_.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Wraps a Swift `(_ value: (any HybridTtsSessionSpec)) -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void_std__shared_ptr_HybridTtsSessionSpec_ { + public typealias bridge = margelo.nitro.nitrotts.bridge.swift + + private let closure: (_ value: (any HybridTtsSessionSpec)) -> Void + + public init(_ closure: @escaping (_ value: (any HybridTtsSessionSpec)) -> Void) { + self.closure = closure + } + + @inline(__always) + public func call(value: bridge.std__shared_ptr_HybridTtsSessionSpec_) -> Void { + self.closure({ () -> any HybridTtsSessionSpec in + let __unsafePointer = bridge.get_std__shared_ptr_HybridTtsSessionSpec_(value) + let __instance = HybridTtsSessionSpec_cxx.fromUnsafe(__unsafePointer) + return __instance.getHybridTtsSessionSpec() + }()) + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void_std__shared_ptr_HybridTtsSessionSpec_`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_std__shared_ptr_HybridTtsSessionSpec_ { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__string.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__string.swift new file mode 100644 index 000000000..dd3b78b58 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__string.swift @@ -0,0 +1,46 @@ +/// +/// Func_void_std__string.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Wraps a Swift `(_ message: String) -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void_std__string { + public typealias bridge = margelo.nitro.nitrotts.bridge.swift + + private let closure: (_ message: String) -> Void + + public init(_ closure: @escaping (_ message: String) -> Void) { + self.closure = closure + } + + @inline(__always) + public func call(message: std.string) -> Void { + self.closure(String(message)) + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void_std__string`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_std__string { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__vector_TtsEngine_.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__vector_TtsEngine_.swift new file mode 100644 index 000000000..02efabd00 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__vector_TtsEngine_.swift @@ -0,0 +1,46 @@ +/// +/// Func_void_std__vector_TtsEngine_.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Wraps a Swift `(_ value: [TtsEngine]) -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void_std__vector_TtsEngine_ { + public typealias bridge = margelo.nitro.nitrotts.bridge.swift + + private let closure: (_ value: [TtsEngine]) -> Void + + public init(_ closure: @escaping (_ value: [TtsEngine]) -> Void) { + self.closure = closure + } + + @inline(__always) + public func call(value: bridge.std__vector_TtsEngine_) -> Void { + self.closure(value.map({ __item in __item })) + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void_std__vector_TtsEngine_`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_std__vector_TtsEngine_ { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__vector_TtsVoice_.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__vector_TtsVoice_.swift new file mode 100644 index 000000000..9eaf2628a --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/Func_void_std__vector_TtsVoice_.swift @@ -0,0 +1,46 @@ +/// +/// Func_void_std__vector_TtsVoice_.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Wraps a Swift `(_ value: [TtsVoice]) -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void_std__vector_TtsVoice_ { + public typealias bridge = margelo.nitro.nitrotts.bridge.swift + + private let closure: (_ value: [TtsVoice]) -> Void + + public init(_ closure: @escaping (_ value: [TtsVoice]) -> Void) { + self.closure = closure + } + + @inline(__always) + public func call(value: bridge.std__vector_TtsVoice_) -> Void { + self.closure(value.map({ __item in __item })) + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void_std__vector_TtsVoice_`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_std__vector_TtsVoice_ { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/HybridTtsFactorySpec.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/HybridTtsFactorySpec.swift new file mode 100644 index 000000000..bb1592be9 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/HybridTtsFactorySpec.swift @@ -0,0 +1,57 @@ +/// +/// HybridTtsFactorySpec.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/// See ``HybridTtsFactorySpec`` +public protocol HybridTtsFactorySpec_protocol: HybridObject { + // Properties + + + // Methods + func createSession() throws -> Promise<(any HybridTtsSessionSpec)> + func getEngines() throws -> Promise<[TtsEngine]> + func getVoices(engineName: String?) throws -> Promise<[TtsVoice]> +} + +public extension HybridTtsFactorySpec_protocol { + /// Default implementation of ``HybridObject.toString`` + func toString() -> String { + return "[HybridObject TtsFactory]" + } +} + +/// See ``HybridTtsFactorySpec`` +open class HybridTtsFactorySpec_base { + private weak var cxxWrapper: HybridTtsFactorySpec_cxx? = nil + public init() { } + public func getCxxWrapper() -> HybridTtsFactorySpec_cxx { + #if DEBUG + guard self is any HybridTtsFactorySpec else { + fatalError("`self` is not a `HybridTtsFactorySpec`! Did you accidentally inherit from `HybridTtsFactorySpec_base` instead of `HybridTtsFactorySpec`?") + } + #endif + if let cxxWrapper = self.cxxWrapper { + return cxxWrapper + } else { + let cxxWrapper = HybridTtsFactorySpec_cxx(self as! any HybridTtsFactorySpec) + self.cxxWrapper = cxxWrapper + return cxxWrapper + } + } +} + +/** + * A Swift base-protocol representing the TtsFactory HybridObject. + * Implement this protocol to create Swift-based instances of TtsFactory. + * ```swift + * class HybridTtsFactory : HybridTtsFactorySpec { + * // ... + * } + * ``` + */ +public typealias HybridTtsFactorySpec = HybridTtsFactorySpec_protocol & HybridTtsFactorySpec_base diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/HybridTtsFactorySpec_cxx.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/HybridTtsFactorySpec_cxx.swift new file mode 100644 index 000000000..8ff6db45f --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/HybridTtsFactorySpec_cxx.swift @@ -0,0 +1,205 @@ +/// +/// HybridTtsFactorySpec_cxx.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * A class implementation that bridges HybridTtsFactorySpec over to C++. + * In C++, we cannot use Swift protocols - so we need to wrap it in a class to make it strongly defined. + * + * Also, some Swift types need to be bridged with special handling: + * - Enums need to be wrapped in Structs, otherwise they cannot be accessed bi-directionally (Swift bug: https://github.com/swiftlang/swift/issues/75330) + * - Other HybridObjects need to be wrapped/unwrapped from the Swift TCxx wrapper + * - Throwing methods need to be wrapped with a Result type, as exceptions cannot be propagated to C++ + */ +open class HybridTtsFactorySpec_cxx { + /** + * The Swift <> C++ bridge's namespace (`margelo::nitro::nitrotts::bridge::swift`) + * from `NitroTts-Swift-Cxx-Bridge.hpp`. + * This contains specialized C++ templates, and C++ helper functions that can be accessed from Swift. + */ + public typealias bridge = margelo.nitro.nitrotts.bridge.swift + + /** + * Holds an instance of the `HybridTtsFactorySpec` Swift protocol. + */ + private var __implementation: any HybridTtsFactorySpec + + /** + * Holds a weak pointer to the C++ class that wraps the Swift class. + */ + private var __cxxPart: bridge.std__weak_ptr_HybridTtsFactorySpec_ + + /** + * Create a new `HybridTtsFactorySpec_cxx` that wraps the given `HybridTtsFactorySpec`. + * All properties and methods bridge to C++ types. + */ + public init(_ implementation: any HybridTtsFactorySpec) { + self.__implementation = implementation + self.__cxxPart = .init() + /* no base class */ + } + + /** + * Get the actual `HybridTtsFactorySpec` instance this class wraps. + */ + @inline(__always) + public func getHybridTtsFactorySpec() -> any HybridTtsFactorySpec { + return __implementation + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `HybridTtsFactorySpec_cxx`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + public class func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> HybridTtsFactorySpec_cxx { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } + + /** + * Gets (or creates) the C++ part of this Hybrid Object. + * The C++ part is a `std::shared_ptr`. + */ + public func getCxxPart() -> bridge.std__shared_ptr_HybridTtsFactorySpec_ { + let cachedCxxPart = self.__cxxPart.lock() + if Bool(fromCxx: cachedCxxPart) { + return cachedCxxPart + } else { + let newCxxPart = bridge.create_std__shared_ptr_HybridTtsFactorySpec_(self.toUnsafe()) + __cxxPart = bridge.weakify_std__shared_ptr_HybridTtsFactorySpec_(newCxxPart) + return newCxxPart + } + } + + + + /** + * Get the memory size of the Swift class (plus size of any other allocations) + * so the JS VM can properly track it and garbage-collect the JS object if needed. + */ + @inline(__always) + public var memorySize: Int { + return MemoryHelper.getSizeOf(self.__implementation) + self.__implementation.memorySize + } + + /** + * Compares this object with the given [other] object for reference equality. + */ + @inline(__always) + public func equals(other: HybridTtsFactorySpec_cxx) -> Bool { + return self.__implementation === other.__implementation + } + + /** + * Call dispose() on the Swift class. + * This _may_ be called manually from JS. + */ + @inline(__always) + public func dispose() { + self.__implementation.dispose() + } + + /** + * Call toString() on the Swift class. + */ + @inline(__always) + public func toString() -> String { + return self.__implementation.toString() + } + + // Properties + + + // Methods + @inline(__always) + public final func createSession() -> bridge.Result_std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec____ { + do { + let __result = try self.__implementation.createSession() + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec___ in + let __promise = bridge.create_std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec___() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec___(__promise) + __result + .then({ __result in __promiseHolder.resolve({ () -> bridge.std__shared_ptr_HybridTtsSessionSpec_ in + let __cxxWrapped = __result.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }()) }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec____(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_std__shared_ptr_HybridTtsSessionSpec____(__exceptionPtr) + } + } + + @inline(__always) + public final func getEngines() -> bridge.Result_std__shared_ptr_Promise_std__vector_TtsEngine____ { + do { + let __result = try self.__implementation.getEngines() + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_std__vector_TtsEngine___ in + let __promise = bridge.create_std__shared_ptr_Promise_std__vector_TtsEngine___() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_std__vector_TtsEngine___(__promise) + __result + .then({ __result in __promiseHolder.resolve({ () -> bridge.std__vector_TtsEngine_ in + var __vector = bridge.create_std__vector_TtsEngine_(__result.count) + for __item in __result { + __vector.push_back(__item) + } + return __vector + }()) }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_std__vector_TtsEngine____(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_std__vector_TtsEngine____(__exceptionPtr) + } + } + + @inline(__always) + public final func getVoices(engineName: bridge.std__optional_std__string_) -> bridge.Result_std__shared_ptr_Promise_std__vector_TtsVoice____ { + do { + let __result = try self.__implementation.getVoices(engineName: { () -> String? in + if bridge.has_value_std__optional_std__string_(engineName) { + let __unwrapped = bridge.get_std__optional_std__string_(engineName) + return String(__unwrapped) + } else { + return nil + } + }()) + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_std__vector_TtsVoice___ in + let __promise = bridge.create_std__shared_ptr_Promise_std__vector_TtsVoice___() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_std__vector_TtsVoice___(__promise) + __result + .then({ __result in __promiseHolder.resolve({ () -> bridge.std__vector_TtsVoice_ in + var __vector = bridge.create_std__vector_TtsVoice_(__result.count) + for __item in __result { + __vector.push_back(__item) + } + return __vector + }()) }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_std__vector_TtsVoice____(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_std__vector_TtsVoice____(__exceptionPtr) + } + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/HybridTtsSessionSpec.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/HybridTtsSessionSpec.swift new file mode 100644 index 000000000..daa807b81 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/HybridTtsSessionSpec.swift @@ -0,0 +1,66 @@ +/// +/// HybridTtsSessionSpec.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/// See ``HybridTtsSessionSpec`` +public protocol HybridTtsSessionSpec_protocol: HybridObject { + // Properties + + + // Methods + func load(paragraphs: [TtsParagraph], initialIndex: Double, metadata: TtsMetadata, settings: TtsSettings) throws -> Promise + func play() throws -> Promise + func pause() throws -> Promise + func stop() throws -> Promise + func skipPrevious() throws -> Promise + func skipNext() throws -> Promise + func replayCurrent() throws -> Promise + func seekTo(index: Double) throws -> Promise + func updateSettings(settings: TtsSettings) throws -> Promise + func addOnStateChangedListener(listener: @escaping (_ state: TtsPlaybackState) -> Void) throws -> ListenerSubscription + func addOnProgressChangedListener(listener: @escaping (_ progress: TtsProgress) -> Void) throws -> ListenerSubscription + func addOnErrorListener(listener: @escaping (_ message: String) -> Void) throws -> ListenerSubscription +} + +public extension HybridTtsSessionSpec_protocol { + /// Default implementation of ``HybridObject.toString`` + func toString() -> String { + return "[HybridObject TtsSession]" + } +} + +/// See ``HybridTtsSessionSpec`` +open class HybridTtsSessionSpec_base { + private weak var cxxWrapper: HybridTtsSessionSpec_cxx? = nil + public init() { } + public func getCxxWrapper() -> HybridTtsSessionSpec_cxx { + #if DEBUG + guard self is any HybridTtsSessionSpec else { + fatalError("`self` is not a `HybridTtsSessionSpec`! Did you accidentally inherit from `HybridTtsSessionSpec_base` instead of `HybridTtsSessionSpec`?") + } + #endif + if let cxxWrapper = self.cxxWrapper { + return cxxWrapper + } else { + let cxxWrapper = HybridTtsSessionSpec_cxx(self as! any HybridTtsSessionSpec) + self.cxxWrapper = cxxWrapper + return cxxWrapper + } + } +} + +/** + * A Swift base-protocol representing the TtsSession HybridObject. + * Implement this protocol to create Swift-based instances of TtsSession. + * ```swift + * class HybridTtsSession : HybridTtsSessionSpec { + * // ... + * } + * ``` + */ +public typealias HybridTtsSessionSpec = HybridTtsSessionSpec_protocol & HybridTtsSessionSpec_base diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/HybridTtsSessionSpec_cxx.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/HybridTtsSessionSpec_cxx.swift new file mode 100644 index 000000000..c46980eb3 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/HybridTtsSessionSpec_cxx.swift @@ -0,0 +1,348 @@ +/// +/// HybridTtsSessionSpec_cxx.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * A class implementation that bridges HybridTtsSessionSpec over to C++. + * In C++, we cannot use Swift protocols - so we need to wrap it in a class to make it strongly defined. + * + * Also, some Swift types need to be bridged with special handling: + * - Enums need to be wrapped in Structs, otherwise they cannot be accessed bi-directionally (Swift bug: https://github.com/swiftlang/swift/issues/75330) + * - Other HybridObjects need to be wrapped/unwrapped from the Swift TCxx wrapper + * - Throwing methods need to be wrapped with a Result type, as exceptions cannot be propagated to C++ + */ +open class HybridTtsSessionSpec_cxx { + /** + * The Swift <> C++ bridge's namespace (`margelo::nitro::nitrotts::bridge::swift`) + * from `NitroTts-Swift-Cxx-Bridge.hpp`. + * This contains specialized C++ templates, and C++ helper functions that can be accessed from Swift. + */ + public typealias bridge = margelo.nitro.nitrotts.bridge.swift + + /** + * Holds an instance of the `HybridTtsSessionSpec` Swift protocol. + */ + private var __implementation: any HybridTtsSessionSpec + + /** + * Holds a weak pointer to the C++ class that wraps the Swift class. + */ + private var __cxxPart: bridge.std__weak_ptr_HybridTtsSessionSpec_ + + /** + * Create a new `HybridTtsSessionSpec_cxx` that wraps the given `HybridTtsSessionSpec`. + * All properties and methods bridge to C++ types. + */ + public init(_ implementation: any HybridTtsSessionSpec) { + self.__implementation = implementation + self.__cxxPart = .init() + /* no base class */ + } + + /** + * Get the actual `HybridTtsSessionSpec` instance this class wraps. + */ + @inline(__always) + public func getHybridTtsSessionSpec() -> any HybridTtsSessionSpec { + return __implementation + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `HybridTtsSessionSpec_cxx`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + public class func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> HybridTtsSessionSpec_cxx { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } + + /** + * Gets (or creates) the C++ part of this Hybrid Object. + * The C++ part is a `std::shared_ptr`. + */ + public func getCxxPart() -> bridge.std__shared_ptr_HybridTtsSessionSpec_ { + let cachedCxxPart = self.__cxxPart.lock() + if Bool(fromCxx: cachedCxxPart) { + return cachedCxxPart + } else { + let newCxxPart = bridge.create_std__shared_ptr_HybridTtsSessionSpec_(self.toUnsafe()) + __cxxPart = bridge.weakify_std__shared_ptr_HybridTtsSessionSpec_(newCxxPart) + return newCxxPart + } + } + + + + /** + * Get the memory size of the Swift class (plus size of any other allocations) + * so the JS VM can properly track it and garbage-collect the JS object if needed. + */ + @inline(__always) + public var memorySize: Int { + return MemoryHelper.getSizeOf(self.__implementation) + self.__implementation.memorySize + } + + /** + * Compares this object with the given [other] object for reference equality. + */ + @inline(__always) + public func equals(other: HybridTtsSessionSpec_cxx) -> Bool { + return self.__implementation === other.__implementation + } + + /** + * Call dispose() on the Swift class. + * This _may_ be called manually from JS. + */ + @inline(__always) + public func dispose() { + self.__implementation.dispose() + } + + /** + * Call toString() on the Swift class. + */ + @inline(__always) + public func toString() -> String { + return self.__implementation.toString() + } + + // Properties + + + // Methods + @inline(__always) + public final func load(paragraphs: bridge.std__vector_TtsParagraph_, initialIndex: Double, metadata: TtsMetadata, settings: TtsSettings) -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.load(paragraphs: paragraphs.map({ __item in __item }), initialIndex: initialIndex, metadata: metadata, settings: settings) + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func play() -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.play() + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func pause() -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.pause() + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func stop() -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.stop() + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func skipPrevious() -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.skipPrevious() + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func skipNext() -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.skipNext() + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func replayCurrent() -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.replayCurrent() + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func seekTo(index: Double) -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.seekTo(index: index) + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func updateSettings(settings: TtsSettings) -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.updateSettings(settings: settings) + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func addOnStateChangedListener(listener: bridge.Func_void_TtsPlaybackState) -> bridge.Result_ListenerSubscription_ { + do { + let __result = try self.__implementation.addOnStateChangedListener(listener: { () -> (TtsPlaybackState) -> Void in + let __wrappedFunction = bridge.wrap_Func_void_TtsPlaybackState(listener) + return { (__state: TtsPlaybackState) -> Void in + __wrappedFunction.call(__state.rawValue) + } + }()) + let __resultCpp = __result + return bridge.create_Result_ListenerSubscription_(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_ListenerSubscription_(__exceptionPtr) + } + } + + @inline(__always) + public final func addOnProgressChangedListener(listener: bridge.Func_void_TtsProgress) -> bridge.Result_ListenerSubscription_ { + do { + let __result = try self.__implementation.addOnProgressChangedListener(listener: { () -> (TtsProgress) -> Void in + let __wrappedFunction = bridge.wrap_Func_void_TtsProgress(listener) + return { (__progress: TtsProgress) -> Void in + __wrappedFunction.call(__progress) + } + }()) + let __resultCpp = __result + return bridge.create_Result_ListenerSubscription_(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_ListenerSubscription_(__exceptionPtr) + } + } + + @inline(__always) + public final func addOnErrorListener(listener: bridge.Func_void_std__string) -> bridge.Result_ListenerSubscription_ { + do { + let __result = try self.__implementation.addOnErrorListener(listener: { () -> (String) -> Void in + let __wrappedFunction = bridge.wrap_Func_void_std__string(listener) + return { (__message: String) -> Void in + __wrappedFunction.call(std.string(__message)) + } + }()) + let __resultCpp = __result + return bridge.create_Result_ListenerSubscription_(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_ListenerSubscription_(__exceptionPtr) + } + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/ListenerSubscription.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/ListenerSubscription.swift new file mode 100644 index 000000000..7b52745e3 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/ListenerSubscription.swift @@ -0,0 +1,37 @@ +/// +/// ListenerSubscription.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `ListenerSubscription`, backed by a C++ struct. + */ +public typealias ListenerSubscription = margelo.nitro.nitrotts.ListenerSubscription + +public extension ListenerSubscription { + private typealias bridge = margelo.nitro.nitrotts.bridge.swift + + /** + * Create a new instance of `ListenerSubscription`. + */ + init(remove: @escaping () -> Void) { + self.init({ () -> bridge.Func_void in + let __closureWrapper = Func_void(remove) + return bridge.create_Func_void(__closureWrapper.toUnsafe()) + }()) + } + + @inline(__always) + var remove: () -> Void { + return { () -> () -> Void in + let __wrappedFunction = bridge.wrap_Func_void(self.__remove) + return { () -> Void in + __wrappedFunction.call() + } + }() + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/TtsEngine.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsEngine.swift new file mode 100644 index 000000000..31b9b421b --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsEngine.swift @@ -0,0 +1,34 @@ +/// +/// TtsEngine.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `TtsEngine`, backed by a C++ struct. + */ +public typealias TtsEngine = margelo.nitro.nitrotts.TtsEngine + +public extension TtsEngine { + private typealias bridge = margelo.nitro.nitrotts.bridge.swift + + /** + * Create a new instance of `TtsEngine`. + */ + init(name: String, label: String) { + self.init(std.string(name), std.string(label)) + } + + @inline(__always) + var name: String { + return String(self.__name) + } + + @inline(__always) + var label: String { + return String(self.__label) + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/TtsMetadata.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsMetadata.swift new file mode 100644 index 000000000..3280078ba --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsMetadata.swift @@ -0,0 +1,52 @@ +/// +/// TtsMetadata.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `TtsMetadata`, backed by a C++ struct. + */ +public typealias TtsMetadata = margelo.nitro.nitrotts.TtsMetadata + +public extension TtsMetadata { + private typealias bridge = margelo.nitro.nitrotts.bridge.swift + + /** + * Create a new instance of `TtsMetadata`. + */ + init(novelName: String, chapterName: String, coverUri: String?) { + self.init(std.string(novelName), std.string(chapterName), { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = coverUri { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }()) + } + + @inline(__always) + var novelName: String { + return String(self.__novelName) + } + + @inline(__always) + var chapterName: String { + return String(self.__chapterName) + } + + @inline(__always) + var coverUri: String? { + return { () -> String? in + if bridge.has_value_std__optional_std__string_(self.__coverUri) { + let __unwrapped = bridge.get_std__optional_std__string_(self.__coverUri) + return String(__unwrapped) + } else { + return nil + } + }() + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/TtsParagraph.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsParagraph.swift new file mode 100644 index 000000000..4811b3ea7 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsParagraph.swift @@ -0,0 +1,34 @@ +/// +/// TtsParagraph.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `TtsParagraph`, backed by a C++ struct. + */ +public typealias TtsParagraph = margelo.nitro.nitrotts.TtsParagraph + +public extension TtsParagraph { + private typealias bridge = margelo.nitro.nitrotts.bridge.swift + + /** + * Create a new instance of `TtsParagraph`. + */ + init(id: String, text: String) { + self.init(std.string(id), std.string(text)) + } + + @inline(__always) + var id: String { + return String(self.__id) + } + + @inline(__always) + var text: String { + return String(self.__text) + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/TtsPlaybackState.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsPlaybackState.swift new file mode 100644 index 000000000..b875478cd --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsPlaybackState.swift @@ -0,0 +1,56 @@ +/// +/// TtsPlaybackState.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +/** + * Represents the JS union `TtsPlaybackState`, backed by a C++ enum. + */ +public typealias TtsPlaybackState = margelo.nitro.nitrotts.TtsPlaybackState + +public extension TtsPlaybackState { + /** + * Get a TtsPlaybackState for the given String value, or + * return `nil` if the given value was invalid/unknown. + */ + init?(fromString string: String) { + switch string { + case "idle": + self = .idle + case "loading": + self = .loading + case "playing": + self = .playing + case "paused": + self = .paused + case "completed": + self = .completed + case "error": + self = .error + default: + return nil + } + } + + /** + * Get the String value this TtsPlaybackState represents. + */ + var stringValue: String { + switch self { + case .idle: + return "idle" + case .loading: + return "loading" + case .playing: + return "playing" + case .paused: + return "paused" + case .completed: + return "completed" + case .error: + return "error" + } + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/TtsProgress.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsProgress.swift new file mode 100644 index 000000000..b3a26838c --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsProgress.swift @@ -0,0 +1,39 @@ +/// +/// TtsProgress.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `TtsProgress`, backed by a C++ struct. + */ +public typealias TtsProgress = margelo.nitro.nitrotts.TtsProgress + +public extension TtsProgress { + private typealias bridge = margelo.nitro.nitrotts.bridge.swift + + /** + * Create a new instance of `TtsProgress`. + */ + init(index: Double, total: Double, paragraphId: String) { + self.init(index, total, std.string(paragraphId)) + } + + @inline(__always) + var index: Double { + return self.__index + } + + @inline(__always) + var total: Double { + return self.__total + } + + @inline(__always) + var paragraphId: String { + return String(self.__paragraphId) + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/TtsSettings.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsSettings.swift new file mode 100644 index 000000000..090235b18 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsSettings.swift @@ -0,0 +1,70 @@ +/// +/// TtsSettings.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `TtsSettings`, backed by a C++ struct. + */ +public typealias TtsSettings = margelo.nitro.nitrotts.TtsSettings + +public extension TtsSettings { + private typealias bridge = margelo.nitro.nitrotts.bridge.swift + + /** + * Create a new instance of `TtsSettings`. + */ + init(engineName: String?, voiceIdentifier: String?, rate: Double, pitch: Double) { + self.init({ () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = engineName { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }(), { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = voiceIdentifier { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }(), rate, pitch) + } + + @inline(__always) + var engineName: String? { + return { () -> String? in + if bridge.has_value_std__optional_std__string_(self.__engineName) { + let __unwrapped = bridge.get_std__optional_std__string_(self.__engineName) + return String(__unwrapped) + } else { + return nil + } + }() + } + + @inline(__always) + var voiceIdentifier: String? { + return { () -> String? in + if bridge.has_value_std__optional_std__string_(self.__voiceIdentifier) { + let __unwrapped = bridge.get_std__optional_std__string_(self.__voiceIdentifier) + return String(__unwrapped) + } else { + return nil + } + }() + } + + @inline(__always) + var rate: Double { + return self.__rate + } + + @inline(__always) + var pitch: Double { + return self.__pitch + } +} diff --git a/modules/nitro-tts/nitrogen/generated/ios/swift/TtsVoice.swift b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsVoice.swift new file mode 100644 index 000000000..5821ced10 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/ios/swift/TtsVoice.swift @@ -0,0 +1,52 @@ +/// +/// TtsVoice.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `TtsVoice`, backed by a C++ struct. + */ +public typealias TtsVoice = margelo.nitro.nitrotts.TtsVoice + +public extension TtsVoice { + private typealias bridge = margelo.nitro.nitrotts.bridge.swift + + /** + * Create a new instance of `TtsVoice`. + */ + init(identifier: String, name: String, language: String?) { + self.init(std.string(identifier), std.string(name), { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = language { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }()) + } + + @inline(__always) + var identifier: String { + return String(self.__identifier) + } + + @inline(__always) + var name: String { + return String(self.__name) + } + + @inline(__always) + var language: String? { + return { () -> String? in + if bridge.has_value_std__optional_std__string_(self.__language) { + let __unwrapped = bridge.get_std__optional_std__string_(self.__language) + return String(__unwrapped) + } else { + return nil + } + }() + } +} diff --git a/modules/nitro-tts/nitrogen/generated/shared/c++/HybridTtsFactorySpec.cpp b/modules/nitro-tts/nitrogen/generated/shared/c++/HybridTtsFactorySpec.cpp new file mode 100644 index 000000000..f025dea48 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/shared/c++/HybridTtsFactorySpec.cpp @@ -0,0 +1,23 @@ +/// +/// HybridTtsFactorySpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridTtsFactorySpec.hpp" + +namespace margelo::nitro::nitrotts { + + void HybridTtsFactorySpec::loadHybridMethods() { + // load base methods/properties + HybridObject::loadHybridMethods(); + // load custom methods/properties + registerHybrids(this, [](Prototype& prototype) { + prototype.registerHybridMethod("createSession", &HybridTtsFactorySpec::createSession); + prototype.registerHybridMethod("getEngines", &HybridTtsFactorySpec::getEngines); + prototype.registerHybridMethod("getVoices", &HybridTtsFactorySpec::getVoices); + }); + } + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/shared/c++/HybridTtsFactorySpec.hpp b/modules/nitro-tts/nitrogen/generated/shared/c++/HybridTtsFactorySpec.hpp new file mode 100644 index 000000000..12c39a133 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/shared/c++/HybridTtsFactorySpec.hpp @@ -0,0 +1,76 @@ +/// +/// HybridTtsFactorySpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `HybridTtsSessionSpec` to properly resolve imports. +namespace margelo::nitro::nitrotts { class HybridTtsSessionSpec; } +// Forward declaration of `TtsEngine` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsEngine; } +// Forward declaration of `TtsVoice` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsVoice; } + +#include +#include "HybridTtsSessionSpec.hpp" +#include +#include "TtsEngine.hpp" +#include +#include "TtsVoice.hpp" +#include +#include + +namespace margelo::nitro::nitrotts { + + using namespace margelo::nitro; + + /** + * An abstract base class for `TtsFactory` + * Inherit this class to create instances of `HybridTtsFactorySpec` in C++. + * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual. + * @example + * ```cpp + * class HybridTtsFactory: public HybridTtsFactorySpec { + * public: + * HybridTtsFactory(...): HybridObject(TAG) { ... } + * // ... + * }; + * ``` + */ + class HybridTtsFactorySpec: public virtual HybridObject { + public: + // Constructor + explicit HybridTtsFactorySpec(): HybridObject(TAG) { } + + // Destructor + ~HybridTtsFactorySpec() override = default; + + public: + // Properties + + + public: + // Methods + virtual std::shared_ptr>> createSession() = 0; + virtual std::shared_ptr>> getEngines() = 0; + virtual std::shared_ptr>> getVoices(const std::optional& engineName) = 0; + + protected: + // Hybrid Setup + void loadHybridMethods() override; + + protected: + // Tag for logging + static constexpr auto TAG = "TtsFactory"; + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/shared/c++/HybridTtsSessionSpec.cpp b/modules/nitro-tts/nitrogen/generated/shared/c++/HybridTtsSessionSpec.cpp new file mode 100644 index 000000000..b01a0c5a5 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/shared/c++/HybridTtsSessionSpec.cpp @@ -0,0 +1,32 @@ +/// +/// HybridTtsSessionSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridTtsSessionSpec.hpp" + +namespace margelo::nitro::nitrotts { + + void HybridTtsSessionSpec::loadHybridMethods() { + // load base methods/properties + HybridObject::loadHybridMethods(); + // load custom methods/properties + registerHybrids(this, [](Prototype& prototype) { + prototype.registerHybridMethod("load", &HybridTtsSessionSpec::load); + prototype.registerHybridMethod("play", &HybridTtsSessionSpec::play); + prototype.registerHybridMethod("pause", &HybridTtsSessionSpec::pause); + prototype.registerHybridMethod("stop", &HybridTtsSessionSpec::stop); + prototype.registerHybridMethod("skipPrevious", &HybridTtsSessionSpec::skipPrevious); + prototype.registerHybridMethod("skipNext", &HybridTtsSessionSpec::skipNext); + prototype.registerHybridMethod("replayCurrent", &HybridTtsSessionSpec::replayCurrent); + prototype.registerHybridMethod("seekTo", &HybridTtsSessionSpec::seekTo); + prototype.registerHybridMethod("updateSettings", &HybridTtsSessionSpec::updateSettings); + prototype.registerHybridMethod("addOnStateChangedListener", &HybridTtsSessionSpec::addOnStateChangedListener); + prototype.registerHybridMethod("addOnProgressChangedListener", &HybridTtsSessionSpec::addOnProgressChangedListener); + prototype.registerHybridMethod("addOnErrorListener", &HybridTtsSessionSpec::addOnErrorListener); + }); + } + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/shared/c++/HybridTtsSessionSpec.hpp b/modules/nitro-tts/nitrogen/generated/shared/c++/HybridTtsSessionSpec.hpp new file mode 100644 index 000000000..6bdcb4ccd --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/shared/c++/HybridTtsSessionSpec.hpp @@ -0,0 +1,93 @@ +/// +/// HybridTtsSessionSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +// Forward declaration of `TtsParagraph` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsParagraph; } +// Forward declaration of `TtsMetadata` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsMetadata; } +// Forward declaration of `TtsSettings` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsSettings; } +// Forward declaration of `ListenerSubscription` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct ListenerSubscription; } +// Forward declaration of `TtsPlaybackState` to properly resolve imports. +namespace margelo::nitro::nitrotts { enum class TtsPlaybackState; } +// Forward declaration of `TtsProgress` to properly resolve imports. +namespace margelo::nitro::nitrotts { struct TtsProgress; } + +#include +#include "TtsParagraph.hpp" +#include +#include "TtsMetadata.hpp" +#include "TtsSettings.hpp" +#include "ListenerSubscription.hpp" +#include "TtsPlaybackState.hpp" +#include +#include "TtsProgress.hpp" +#include + +namespace margelo::nitro::nitrotts { + + using namespace margelo::nitro; + + /** + * An abstract base class for `TtsSession` + * Inherit this class to create instances of `HybridTtsSessionSpec` in C++. + * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual. + * @example + * ```cpp + * class HybridTtsSession: public HybridTtsSessionSpec { + * public: + * HybridTtsSession(...): HybridObject(TAG) { ... } + * // ... + * }; + * ``` + */ + class HybridTtsSessionSpec: public virtual HybridObject { + public: + // Constructor + explicit HybridTtsSessionSpec(): HybridObject(TAG) { } + + // Destructor + ~HybridTtsSessionSpec() override = default; + + public: + // Properties + + + public: + // Methods + virtual std::shared_ptr> load(const std::vector& paragraphs, double initialIndex, const TtsMetadata& metadata, const TtsSettings& settings) = 0; + virtual std::shared_ptr> play() = 0; + virtual std::shared_ptr> pause() = 0; + virtual std::shared_ptr> stop() = 0; + virtual std::shared_ptr> skipPrevious() = 0; + virtual std::shared_ptr> skipNext() = 0; + virtual std::shared_ptr> replayCurrent() = 0; + virtual std::shared_ptr> seekTo(double index) = 0; + virtual std::shared_ptr> updateSettings(const TtsSettings& settings) = 0; + virtual ListenerSubscription addOnStateChangedListener(const std::function& listener) = 0; + virtual ListenerSubscription addOnProgressChangedListener(const std::function& listener) = 0; + virtual ListenerSubscription addOnErrorListener(const std::function& listener) = 0; + + protected: + // Hybrid Setup + void loadHybridMethods() override; + + protected: + // Tag for logging + static constexpr auto TAG = "TtsSession"; + }; + +} // namespace margelo::nitro::nitrotts diff --git a/modules/nitro-tts/nitrogen/generated/shared/c++/ListenerSubscription.hpp b/modules/nitro-tts/nitrogen/generated/shared/c++/ListenerSubscription.hpp new file mode 100644 index 000000000..372784d0a --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/shared/c++/ListenerSubscription.hpp @@ -0,0 +1,83 @@ +/// +/// ListenerSubscription.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include + +namespace margelo::nitro::nitrotts { + + /** + * A struct which can be represented as a JavaScript object (ListenerSubscription). + */ + struct ListenerSubscription final { + public: + std::function remove SWIFT_PRIVATE; + + public: + ListenerSubscription() = default; + explicit ListenerSubscription(std::function remove): remove(remove) {} + + public: + // ListenerSubscription is not equatable because these properties are not equatable: remove + }; + +} // namespace margelo::nitro::nitrotts + +namespace margelo::nitro { + + // C++ ListenerSubscription <> JS ListenerSubscription (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitrotts::ListenerSubscription fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::nitrotts::ListenerSubscription( + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "remove"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitrotts::ListenerSubscription& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "remove"), JSIConverter>::toJSI(runtime, arg.remove)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "remove")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-tts/nitrogen/generated/shared/c++/TtsEngine.hpp b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsEngine.hpp new file mode 100644 index 000000000..8d8019bf9 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsEngine.hpp @@ -0,0 +1,87 @@ +/// +/// TtsEngine.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include + +namespace margelo::nitro::nitrotts { + + /** + * A struct which can be represented as a JavaScript object (TtsEngine). + */ + struct TtsEngine final { + public: + std::string name SWIFT_PRIVATE; + std::string label SWIFT_PRIVATE; + + public: + TtsEngine() = default; + explicit TtsEngine(std::string name, std::string label): name(name), label(label) {} + + public: + friend bool operator==(const TtsEngine& lhs, const TtsEngine& rhs) = default; + }; + +} // namespace margelo::nitro::nitrotts + +namespace margelo::nitro { + + // C++ TtsEngine <> JS TtsEngine (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitrotts::TtsEngine fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::nitrotts::TtsEngine( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "name"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "label"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitrotts::TtsEngine& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "name"), JSIConverter::toJSI(runtime, arg.name)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "label"), JSIConverter::toJSI(runtime, arg.label)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "name")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "label")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-tts/nitrogen/generated/shared/c++/TtsMetadata.hpp b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsMetadata.hpp new file mode 100644 index 000000000..7fa8e2d2f --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsMetadata.hpp @@ -0,0 +1,92 @@ +/// +/// TtsMetadata.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include +#include + +namespace margelo::nitro::nitrotts { + + /** + * A struct which can be represented as a JavaScript object (TtsMetadata). + */ + struct TtsMetadata final { + public: + std::string novelName SWIFT_PRIVATE; + std::string chapterName SWIFT_PRIVATE; + std::optional coverUri SWIFT_PRIVATE; + + public: + TtsMetadata() = default; + explicit TtsMetadata(std::string novelName, std::string chapterName, std::optional coverUri): novelName(novelName), chapterName(chapterName), coverUri(coverUri) {} + + public: + friend bool operator==(const TtsMetadata& lhs, const TtsMetadata& rhs) = default; + }; + +} // namespace margelo::nitro::nitrotts + +namespace margelo::nitro { + + // C++ TtsMetadata <> JS TtsMetadata (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitrotts::TtsMetadata fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::nitrotts::TtsMetadata( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "novelName"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "chapterName"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "coverUri"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitrotts::TtsMetadata& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "novelName"), JSIConverter::toJSI(runtime, arg.novelName)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "chapterName"), JSIConverter::toJSI(runtime, arg.chapterName)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "coverUri"), JSIConverter>::toJSI(runtime, arg.coverUri)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "novelName")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "chapterName")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "coverUri")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-tts/nitrogen/generated/shared/c++/TtsParagraph.hpp b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsParagraph.hpp new file mode 100644 index 000000000..1a3af97c9 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsParagraph.hpp @@ -0,0 +1,87 @@ +/// +/// TtsParagraph.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include + +namespace margelo::nitro::nitrotts { + + /** + * A struct which can be represented as a JavaScript object (TtsParagraph). + */ + struct TtsParagraph final { + public: + std::string id SWIFT_PRIVATE; + std::string text SWIFT_PRIVATE; + + public: + TtsParagraph() = default; + explicit TtsParagraph(std::string id, std::string text): id(id), text(text) {} + + public: + friend bool operator==(const TtsParagraph& lhs, const TtsParagraph& rhs) = default; + }; + +} // namespace margelo::nitro::nitrotts + +namespace margelo::nitro { + + // C++ TtsParagraph <> JS TtsParagraph (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitrotts::TtsParagraph fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::nitrotts::TtsParagraph( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "text"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitrotts::TtsParagraph& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "id"), JSIConverter::toJSI(runtime, arg.id)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "text"), JSIConverter::toJSI(runtime, arg.text)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "text")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-tts/nitrogen/generated/shared/c++/TtsPlaybackState.hpp b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsPlaybackState.hpp new file mode 100644 index 000000000..9312af089 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsPlaybackState.hpp @@ -0,0 +1,92 @@ +/// +/// TtsPlaybackState.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +namespace margelo::nitro::nitrotts { + + /** + * An enum which can be represented as a JavaScript union (TtsPlaybackState). + */ + enum class TtsPlaybackState { + IDLE SWIFT_NAME(idle) = 0, + LOADING SWIFT_NAME(loading) = 1, + PLAYING SWIFT_NAME(playing) = 2, + PAUSED SWIFT_NAME(paused) = 3, + COMPLETED SWIFT_NAME(completed) = 4, + ERROR SWIFT_NAME(error) = 5, + } CLOSED_ENUM; + +} // namespace margelo::nitro::nitrotts + +namespace margelo::nitro { + + // C++ TtsPlaybackState <> JS TtsPlaybackState (union) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitrotts::TtsPlaybackState fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + std::string unionValue = JSIConverter::fromJSI(runtime, arg); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("idle"): return margelo::nitro::nitrotts::TtsPlaybackState::IDLE; + case hashString("loading"): return margelo::nitro::nitrotts::TtsPlaybackState::LOADING; + case hashString("playing"): return margelo::nitro::nitrotts::TtsPlaybackState::PLAYING; + case hashString("paused"): return margelo::nitro::nitrotts::TtsPlaybackState::PAUSED; + case hashString("completed"): return margelo::nitro::nitrotts::TtsPlaybackState::COMPLETED; + case hashString("error"): return margelo::nitro::nitrotts::TtsPlaybackState::ERROR; + default: [[unlikely]] + throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum TtsPlaybackState - invalid value!"); + } + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitrotts::TtsPlaybackState arg) { + switch (arg) { + case margelo::nitro::nitrotts::TtsPlaybackState::IDLE: return JSIConverter::toJSI(runtime, "idle"); + case margelo::nitro::nitrotts::TtsPlaybackState::LOADING: return JSIConverter::toJSI(runtime, "loading"); + case margelo::nitro::nitrotts::TtsPlaybackState::PLAYING: return JSIConverter::toJSI(runtime, "playing"); + case margelo::nitro::nitrotts::TtsPlaybackState::PAUSED: return JSIConverter::toJSI(runtime, "paused"); + case margelo::nitro::nitrotts::TtsPlaybackState::COMPLETED: return JSIConverter::toJSI(runtime, "completed"); + case margelo::nitro::nitrotts::TtsPlaybackState::ERROR: return JSIConverter::toJSI(runtime, "error"); + default: [[unlikely]] + throw std::invalid_argument("Cannot convert TtsPlaybackState to JS - invalid value: " + + std::to_string(static_cast(arg)) + "!"); + } + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isString()) { + return false; + } + std::string unionValue = JSIConverter::fromJSI(runtime, value); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("idle"): + case hashString("loading"): + case hashString("playing"): + case hashString("paused"): + case hashString("completed"): + case hashString("error"): + return true; + default: + return false; + } + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-tts/nitrogen/generated/shared/c++/TtsProgress.hpp b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsProgress.hpp new file mode 100644 index 000000000..9f0ca7038 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsProgress.hpp @@ -0,0 +1,91 @@ +/// +/// TtsProgress.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include + +namespace margelo::nitro::nitrotts { + + /** + * A struct which can be represented as a JavaScript object (TtsProgress). + */ + struct TtsProgress final { + public: + double index SWIFT_PRIVATE; + double total SWIFT_PRIVATE; + std::string paragraphId SWIFT_PRIVATE; + + public: + TtsProgress() = default; + explicit TtsProgress(double index, double total, std::string paragraphId): index(index), total(total), paragraphId(paragraphId) {} + + public: + friend bool operator==(const TtsProgress& lhs, const TtsProgress& rhs) = default; + }; + +} // namespace margelo::nitro::nitrotts + +namespace margelo::nitro { + + // C++ TtsProgress <> JS TtsProgress (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitrotts::TtsProgress fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::nitrotts::TtsProgress( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "index"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "total"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "paragraphId"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitrotts::TtsProgress& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "index"), JSIConverter::toJSI(runtime, arg.index)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "total"), JSIConverter::toJSI(runtime, arg.total)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "paragraphId"), JSIConverter::toJSI(runtime, arg.paragraphId)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "index")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "total")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "paragraphId")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-tts/nitrogen/generated/shared/c++/TtsSettings.hpp b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsSettings.hpp new file mode 100644 index 000000000..252131a06 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsSettings.hpp @@ -0,0 +1,96 @@ +/// +/// TtsSettings.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include +#include + +namespace margelo::nitro::nitrotts { + + /** + * A struct which can be represented as a JavaScript object (TtsSettings). + */ + struct TtsSettings final { + public: + std::optional engineName SWIFT_PRIVATE; + std::optional voiceIdentifier SWIFT_PRIVATE; + double rate SWIFT_PRIVATE; + double pitch SWIFT_PRIVATE; + + public: + TtsSettings() = default; + explicit TtsSettings(std::optional engineName, std::optional voiceIdentifier, double rate, double pitch): engineName(engineName), voiceIdentifier(voiceIdentifier), rate(rate), pitch(pitch) {} + + public: + friend bool operator==(const TtsSettings& lhs, const TtsSettings& rhs) = default; + }; + +} // namespace margelo::nitro::nitrotts + +namespace margelo::nitro { + + // C++ TtsSettings <> JS TtsSettings (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitrotts::TtsSettings fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::nitrotts::TtsSettings( + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "engineName"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "voiceIdentifier"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "rate"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "pitch"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitrotts::TtsSettings& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "engineName"), JSIConverter>::toJSI(runtime, arg.engineName)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "voiceIdentifier"), JSIConverter>::toJSI(runtime, arg.voiceIdentifier)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "rate"), JSIConverter::toJSI(runtime, arg.rate)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "pitch"), JSIConverter::toJSI(runtime, arg.pitch)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "engineName")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "voiceIdentifier")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "rate")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "pitch")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-tts/nitrogen/generated/shared/c++/TtsVoice.hpp b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsVoice.hpp new file mode 100644 index 000000000..d36df83b7 --- /dev/null +++ b/modules/nitro-tts/nitrogen/generated/shared/c++/TtsVoice.hpp @@ -0,0 +1,92 @@ +/// +/// TtsVoice.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include +#include + +namespace margelo::nitro::nitrotts { + + /** + * A struct which can be represented as a JavaScript object (TtsVoice). + */ + struct TtsVoice final { + public: + std::string identifier SWIFT_PRIVATE; + std::string name SWIFT_PRIVATE; + std::optional language SWIFT_PRIVATE; + + public: + TtsVoice() = default; + explicit TtsVoice(std::string identifier, std::string name, std::optional language): identifier(identifier), name(name), language(language) {} + + public: + friend bool operator==(const TtsVoice& lhs, const TtsVoice& rhs) = default; + }; + +} // namespace margelo::nitro::nitrotts + +namespace margelo::nitro { + + // C++ TtsVoice <> JS TtsVoice (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::nitrotts::TtsVoice fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::nitrotts::TtsVoice( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "identifier"))), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "name"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "language"))) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitrotts::TtsVoice& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "identifier"), JSIConverter::toJSI(runtime, arg.identifier)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "name"), JSIConverter::toJSI(runtime, arg.name)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "language"), JSIConverter>::toJSI(runtime, arg.language)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "identifier")))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "name")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "language")))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/modules/nitro-tts/package.json b/modules/nitro-tts/package.json new file mode 100644 index 000000000..bc4d93cf9 --- /dev/null +++ b/modules/nitro-tts/package.json @@ -0,0 +1,53 @@ +{ + "name": "nitro-tts", + "version": "0.0.1", + "description": "Stateful native text-to-speech playback for LNReader", + "main": "lib/index", + "module": "lib/index", + "types": "lib/index.d.ts", + "react-native": "src/index", + "source": "src/index", + "files": [ + "src", + "react-native.config.js", + "lib", + "nitrogen", + "android/build.gradle", + "android/gradle.properties", + "android/fix-prefab.gradle", + "android/CMakeLists.txt", + "android/src", + "ios/**/*.swift", + "nitro.json", + "*.podspec" + ], + "scripts": { + "typecheck": "tsc --noEmit", + "specs": "tsc --noEmit false && nitrogen --logLevel=\"debug\"" + }, + "keywords": [ + "react-native", + "nitro", + "text-to-speech" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/LNReader/lnreader" + }, + "author": "LNReader", + "license": "MIT", + "homepage": "https://github.com/LNReader/lnreader#readme", + "devDependencies": { + "@types/react": "^19.2.17", + "nitrogen": "*", + "react": "19.2.3", + "react-native": "0.86.0", + "react-native-nitro-modules": "*", + "typescript": "^6.0.3" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-nitro-modules": "*" + } +} diff --git a/modules/nitro-tts/react-native.config.js b/modules/nitro-tts/react-native.config.js new file mode 100644 index 000000000..c36ba55b0 --- /dev/null +++ b/modules/nitro-tts/react-native.config.js @@ -0,0 +1,8 @@ +module.exports = { + dependency: { + platforms: { + ios: {}, + android: {}, + }, + }, +}; diff --git a/modules/nitro-tts/src/index.ts b/modules/nitro-tts/src/index.ts new file mode 100644 index 000000000..40c025865 --- /dev/null +++ b/modules/nitro-tts/src/index.ts @@ -0,0 +1,15 @@ +import { NitroModules } from 'react-native-nitro-modules'; +import type { TtsFactory } from './specs/TtsFactory.nitro'; + +export const Tts = NitroModules.createHybridObject('TtsFactory'); + +export type { TtsFactory } from './specs/TtsFactory.nitro'; +export type { TtsSession } from './specs/TtsSession.nitro'; +export type { ListenerSubscription } from './types/ListenerSubscription'; +export type { TtsEngine } from './types/TtsEngine'; +export type { TtsMetadata } from './types/TtsMetadata'; +export type { TtsParagraph } from './types/TtsParagraph'; +export type { TtsPlaybackState } from './types/TtsPlaybackState'; +export type { TtsProgress } from './types/TtsProgress'; +export type { TtsSettings } from './types/TtsSettings'; +export type { TtsVoice } from './types/TtsVoice'; diff --git a/modules/nitro-tts/src/specs/TtsFactory.nitro.ts b/modules/nitro-tts/src/specs/TtsFactory.nitro.ts new file mode 100644 index 000000000..b183a6e60 --- /dev/null +++ b/modules/nitro-tts/src/specs/TtsFactory.nitro.ts @@ -0,0 +1,30 @@ +import type { HybridObject } from 'react-native-nitro-modules'; +import type { TtsEngine } from '../types/TtsEngine'; +import type { TtsVoice } from '../types/TtsVoice'; +import type { TtsSession } from './TtsSession.nitro'; + +/** + * Creates ready native text-to-speech playback sessions. + * + * @see {@linkcode TtsFactory.createSession} + */ +export interface TtsFactory + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { + /** + * Creates or reconnects to the app-owned playback session. + */ + createSession(): Promise; + + /** + * Lists text-to-speech engines installed on the device. + * + * Android only: resolves to an empty array on iOS. + */ + getEngines(): Promise; + + /** + * Lists voices offered by `engineName`, or by the system default engine + * when omitted. iOS ignores `engineName` and lists system voices. + */ + getVoices(engineName?: string): Promise; +} diff --git a/modules/nitro-tts/src/specs/TtsSession.nitro.ts b/modules/nitro-tts/src/specs/TtsSession.nitro.ts new file mode 100644 index 000000000..a18784b23 --- /dev/null +++ b/modules/nitro-tts/src/specs/TtsSession.nitro.ts @@ -0,0 +1,68 @@ +import type { HybridObject } from 'react-native-nitro-modules'; +import type { ListenerSubscription } from '../types/ListenerSubscription'; +import type { TtsMetadata } from '../types/TtsMetadata'; +import type { TtsParagraph } from '../types/TtsParagraph'; +import type { TtsPlaybackState } from '../types/TtsPlaybackState'; +import type { TtsProgress } from '../types/TtsProgress'; +import type { TtsSettings } from '../types/TtsSettings'; + +/** + * Controls one native text-to-speech queue and its media controls. + * + * @see {@linkcode TtsSession.load} + */ +export interface TtsSession + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { + /** + * Replaces the active paragraph queue and resolves after native preparation. + */ + load( + paragraphs: TtsParagraph[], + initialIndex: number, + metadata: TtsMetadata, + settings: TtsSettings, + ): Promise; + + /** Starts or resumes playback. */ + play(): Promise; + + /** Pauses playback while preserving the active paragraph. */ + pause(): Promise; + + /** Stops playback and clears the active queue. */ + stop(): Promise; + + /** Starts the paragraph before the active paragraph. */ + skipPrevious(): Promise; + + /** Starts the paragraph after the active paragraph. */ + skipNext(): Promise; + + /** Restarts the active paragraph from its beginning. */ + replayCurrent(): Promise; + + /** Starts the paragraph at `index`. */ + seekTo(index: number): Promise; + + /** Applies voice, rate, and pitch preferences. */ + updateSettings(settings: TtsSettings): Promise; + + /** + * Observes playback-state changes. + */ + addOnStateChangedListener( + listener: (state: TtsPlaybackState) => void, + ): ListenerSubscription; + + /** + * Observes active-paragraph changes. + */ + addOnProgressChangedListener( + listener: (progress: TtsProgress) => void, + ): ListenerSubscription; + + /** + * Observes native playback failures. + */ + addOnErrorListener(listener: (message: string) => void): ListenerSubscription; +} diff --git a/modules/nitro-tts/src/types/ListenerSubscription.ts b/modules/nitro-tts/src/types/ListenerSubscription.ts new file mode 100644 index 000000000..d56c3b316 --- /dev/null +++ b/modules/nitro-tts/src/types/ListenerSubscription.ts @@ -0,0 +1,9 @@ +/** + * Removes a listener registered on a native TTS session. + * + * @see {@linkcode ListenerSubscription.remove} + */ +export interface ListenerSubscription { + /** Stops future listener emissions. */ + remove: () => void; +} diff --git a/modules/nitro-tts/src/types/TtsEngine.ts b/modules/nitro-tts/src/types/TtsEngine.ts new file mode 100644 index 000000000..ac6aba86d --- /dev/null +++ b/modules/nitro-tts/src/types/TtsEngine.ts @@ -0,0 +1,14 @@ +/** + * A text-to-speech engine installed on the device. + * + * Android only: iOS has no concept of swappable synthesis engines, so + * {@linkcode TtsFactory.getEngines} always resolves to an empty array there. + * + * @see {@linkcode TtsFactory.getEngines} + */ +export interface TtsEngine { + /** Platform engine identifier (the Android package name). */ + name: string; + /** Human-readable label shown in the picker. */ + label: string; +} diff --git a/modules/nitro-tts/src/types/TtsMetadata.ts b/modules/nitro-tts/src/types/TtsMetadata.ts new file mode 100644 index 000000000..0372f50a9 --- /dev/null +++ b/modules/nitro-tts/src/types/TtsMetadata.ts @@ -0,0 +1,13 @@ +/** + * Describes the chapter shown by native media controls. + * + * @see {@linkcode TtsSession.load} + */ +export interface TtsMetadata { + /** Novel title displayed as the media artist. */ + novelName: string; + /** Chapter title displayed as the media title. */ + chapterName: string; + /** Optional local or remote cover URI. */ + coverUri?: string; +} diff --git a/modules/nitro-tts/src/types/TtsParagraph.ts b/modules/nitro-tts/src/types/TtsParagraph.ts new file mode 100644 index 000000000..bb50c3c90 --- /dev/null +++ b/modules/nitro-tts/src/types/TtsParagraph.ts @@ -0,0 +1,11 @@ +/** + * One independently navigable paragraph in a TTS queue. + * + * @see {@linkcode TtsSession.load} + */ +export interface TtsParagraph { + /** Stable key used to synchronize native progress with the WebView DOM. */ + id: string; + /** Text sent to the selected native speech voice. */ + text: string; +} diff --git a/modules/nitro-tts/src/types/TtsPlaybackState.ts b/modules/nitro-tts/src/types/TtsPlaybackState.ts new file mode 100644 index 000000000..98e77f348 --- /dev/null +++ b/modules/nitro-tts/src/types/TtsPlaybackState.ts @@ -0,0 +1,12 @@ +/** + * Current lifecycle state of a native TTS session. + * + * @see {@linkcode TtsSession.addOnStateChangedListener} + */ +export type TtsPlaybackState = + | 'idle' + | 'loading' + | 'playing' + | 'paused' + | 'completed' + | 'error'; diff --git a/modules/nitro-tts/src/types/TtsProgress.ts b/modules/nitro-tts/src/types/TtsProgress.ts new file mode 100644 index 000000000..ce168740b --- /dev/null +++ b/modules/nitro-tts/src/types/TtsProgress.ts @@ -0,0 +1,13 @@ +/** + * Identifies the paragraph currently owned by native playback. + * + * @see {@linkcode TtsSession.addOnProgressChangedListener} + */ +export interface TtsProgress { + /** Zero-based paragraph index. */ + index: number; + /** Total number of paragraphs in the active queue. */ + total: number; + /** Stable identifier of the active paragraph. */ + paragraphId: string; +} diff --git a/modules/nitro-tts/src/types/TtsSettings.ts b/modules/nitro-tts/src/types/TtsSettings.ts new file mode 100644 index 000000000..a4f031af7 --- /dev/null +++ b/modules/nitro-tts/src/types/TtsSettings.ts @@ -0,0 +1,18 @@ +/** + * Native speech preferences applied to every queued paragraph. + * + * @see {@linkcode TtsSession.updateSettings} + */ +export interface TtsSettings { + /** + * Android only: engine package name from {@linkcode TtsFactory.getEngines}, + * or the system default when absent. Ignored on iOS. + */ + engineName?: string; + /** Platform voice identifier, or the platform default when absent. */ + voiceIdentifier?: string; + /** Speech-rate multiplier selected by the reader. */ + rate: number; + /** Voice-pitch multiplier selected by the reader. */ + pitch: number; +} diff --git a/modules/nitro-tts/src/types/TtsVoice.ts b/modules/nitro-tts/src/types/TtsVoice.ts new file mode 100644 index 000000000..8b07ad8ba --- /dev/null +++ b/modules/nitro-tts/src/types/TtsVoice.ts @@ -0,0 +1,13 @@ +/** + * A voice offered by a text-to-speech engine. + * + * @see {@linkcode TtsFactory.getVoices} + */ +export interface TtsVoice { + /** Platform voice identifier, passed back via {@linkcode TtsSettings.voiceIdentifier}. */ + identifier: string; + /** Human-readable voice name. */ + name: string; + /** BCP-47 language tag, when known. */ + language?: string; +} diff --git a/modules/nitro-tts/tsconfig.json b/modules/nitro-tts/tsconfig.json new file mode 100644 index 000000000..c9f370d8c --- /dev/null +++ b/modules/nitro-tts/tsconfig.json @@ -0,0 +1,26 @@ +{ + "include": ["src"], + "compilerOptions": { + "composite": true, + "outDir": "lib", + "rootDir": "src", + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "lib": ["esnext"], + "module": "esnext", + "moduleResolution": "bundler", + "noEmit": false, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "esnext", + "verbatimModuleSyntax": true + } +} diff --git a/package.json b/package.json index f1c1f5287..d5cb23b66 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,19 @@ { "name": "lnreader", - "version": "2.0.3", + "version": "2.1.2", "private": true, "scripts": { - "dev:android": "pnpm run generate:env:debug && react-native run-android --appIdSuffix \"debug\" --active-arch-only", - "dev:ios": "react-native run-ios", - "dev:start": "react-native start", - "dev:clean-start": "pnpm run dev:start -- --reset-cache", - "build:release:android": "pnpm run generate:env:release && cd android && ./gradlew clean && ./gradlew assembleRelease", + "dev:android:release": "pnpm run generate:env:release && npx expo run:android --variant release", + "dev:android": "pnpm run generate:env:debug && npx expo run:android --app-id com.rajarsheechatterjee.LNReader.debug", + "dev:ios": "npx expo run:ios", + "dev:start": "npx expo start", + "dev:clean-start": "npx expo start --clear", + "build:release:android": "pnpm run generate:env:release && pnpm exec expo prebuild --platform android --clean --no-install && mkdir -p android/app/build/intermediates/sourcemaps/react/release && cd android && NODE_ENV=production ./gradlew :app:assembleRelease --build-cache --parallel", "build:open-apk": "open ./android/app/build/outputs/apk/release/", - "generate:env:debug": "node scripts/generate-env-file.cjs Debug", - "generate:env:release": "node scripts/generate-env-file.cjs Release", + "generate:env:debug": "node scripts/generate-env-file.cjs --build-type Debug", + "generate:env:release": "node scripts/generate-env-file.cjs --build-type Release", + "release:package": "node scripts/package-release-artifacts.cjs", + "release:prepare": "node scripts/prepare-release.cjs", "generate:string-types": "node scripts/generate-string-types.cjs", "generate:db-migration": "drizzle-kit generate", "upgrade:migration-format": "drizzle-kit up", @@ -21,155 +24,148 @@ "test:db": "jest --selectProjects db", "test:rn": "jest --selectProjects rn", "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx", - "lint:fix": "pnpm run lint -- --fix", + "lint:fix": "pnpm run lint --fix", "format": "prettier --write \"./src/**/*.{js,jsx,ts,tsx}\" ./scripts", "format:check": "prettier --check \"./src/**/*.{js,jsx,ts,tsx}\" ./scripts", "type-check": "tsc --noEmit", - "clean:full": "rm -rf node_modules/ ./android/build/ ./android/app/build/ ./android/app/.cxx && pnpm i && cd android/ && ./gradlew clean && cd .. && pnpm run dev:start -- --reset-cache", - "clean:android": "rm -rf ./android/build/ ./android/app/build/ ./android/app/.cxx && cd android/ && ./gradlew clean && cd .. && pnpm run dev:start -- --reset-cache", + "clean:generated": "rm -rf .expo .rock android/build android/app/build android/app/.cxx modules/*/android/build modules/*/android/.cxx modules/*/lib src/generated tsconfig.tsbuildinfo modules/*/tsconfig.tsbuildinfo", + "clean:full": "pnpm run clean:generated && rm -rf node_modules && pnpm install && cd android && ./gradlew clean && cd .. && npx expo start --clear", + "check": "pnpm run format:check && pnpm run lint && pnpm run type-check", "prepare": "husky install", "nix:shell": "nix develop --extra-experimental-features nix-command --extra-experimental-features flakes", "nix:emulator": "nix develop .#emulator --extra-experimental-features nix-command --extra-experimental-features flakes" }, - "codegenConfig": { - "name": "LNReaderSpec", - "type": "modules", - "jsSrcsDir": "specs", - "android": { - "javaPackageName": "com.lnreader.spec" - }, - "ios": { - "modulesProvider": { - "NativeEpubUtil": "RCTNativeEpubUtil", - "NativeFile": "RCTNativeFile", - "NativeVolumeButtonListener": "RCTNativeVolumeButtonListener", - "NativeZipArchive": "RCTNativeZipArchive" - } - } - }, "dependencies": { - "@cd-z/react-native-epub-creator": "^3.0.0", - "@gorhom/bottom-sheet": "^5.2.8", - "@legendapp/list": "^2.0.19", - "@noble/ciphers": "^2.1.1", - "@op-engineering/op-sqlite": "^15.2.9", - "@preeternal/react-native-cookie-manager": "^6.3.1", - "@react-native-community/slider": "^5.1.2", + "@expo/dom-webview": "^57.0.1", + "@gorhom/bottom-sheet": "^5.2.14", + "@legendapp/list": "^3.3.3", + "@noble/ciphers": "^2.2.0", + "@op-engineering/op-sqlite": "^15.2.14", + "@pchmn/expo-material3-theme": "^1.4.0", + "@preeternal/react-native-cookie-manager": "^6.3.3", "@react-native-documents/picker": "^12.0.1", "@react-native-google-signin/google-signin": "^16.1.2", - "@react-native-vector-icons/common": "^13.0.0", - "@react-native-vector-icons/material-design-icons": "^13.0.0", - "@react-native/assets-registry": "^0.83.4", - "@react-native/codegen": "^0.83.4", - "@react-native/gradle-plugin": "^0.83.4", - "@react-navigation/bottom-tabs": "^7.15.9", - "@react-navigation/native": "^7.2.2", - "@react-navigation/native-stack": "^7.14.10", - "@react-navigation/stack": "^7.8.9", + "@react-native-vector-icons/common": "^13.0.1", + "@react-native-vector-icons/material-design-icons": "^13.1.2", + "@react-navigation/bottom-tabs": "^7.18.11", + "@react-navigation/native": "^7.3.11", + "@react-navigation/native-stack": "^7.18.3", + "@react-navigation/stack": "^7.10.14", + "@shopify/flash-list": "^2.0.2", "babel-plugin-inline-import": "^3.0.0", "cheerio": "1.0.0-rc.12", "color": "^5.0.3", - "dayjs": "^1.11.20", - "drizzle-orm": "1.0.0-beta.20", - "expo": "^55.0.9", - "expo-clipboard": "~55.0.9", - "expo-document-picker": "~55.0.9", - "expo-file-system": "~55.0.12", - "expo-haptics": "~55.0.9", - "expo-keep-awake": "~55.0.4", - "expo-linear-gradient": "~55.0.9", - "expo-linking": "~55.0.9", - "expo-localization": "~55.0.9", - "expo-navigation-bar": "~55.0.9", - "expo-notifications": "~55.0.14", - "expo-speech": "~55.0.9", - "expo-web-browser": "~55.0.10", + "dayjs": "^1.11.21", + "drizzle-orm": "1.0.0-beta.22", + "expo": "^57.0.7", + "expo-build-properties": "~57.0.6", + "expo-clipboard": "~57.0.1", + "expo-dev-client": "^57.0.7", + "expo-document-picker": "~57.0.1", + "expo-file-system": "~57.0.1", + "expo-haptics": "~57.0.1", + "expo-image": "~57.0.1", + "expo-keep-awake": "~57.0.1", + "expo-linear-gradient": "~57.0.1", + "expo-linking": "~57.0.3", + "expo-localization": "~57.0.1", + "expo-navigation-bar": "~57.0.2", + "expo-speech": "~57.0.1", + "expo-splash-screen": "^57.0.4", + "expo-web-browser": "~57.0.1", "htmlparser2": "^12.0.0", "i18n-js": "^4.5.3", - "lodash-es": "^4.17.23", - "lottie-ios": "^3.5.0", - "lottie-react-native": "^5.1.6", - "protobufjs": "^8.0.0", - "react": "^19.2.4", - "react-native": "^0.83.4", - "react-native-background-actions": "^4.0.1", - "react-native-config": "^1.6.1", + "lodash-es": "^4.18.1", + "lottie-react-native": "^7.3.8", + "protobufjs": "^8.7.1", + "react": "19.2.3", + "react-native": "0.86.0", "react-native-device-info": "^15.0.2", "react-native-draggable-flatlist": "^4.0.3", - "react-native-drawer-layout": "^4.2.2", + "react-native-drawer-layout": "^4.2.8", "react-native-edge-to-edge": "^1.8.1", "react-native-error-boundary": "^3.1.0", - "react-native-file-access": "^4.0.2", - "react-native-gesture-handler": "^2.30.1", - "react-native-lottie-splash-screen": "^1.1.2", - "react-native-mmkv": "^4.3.0", - "react-native-nitro-modules": "^0.35.2", - "react-native-pager-view": "^8.0.0", - "react-native-paper": "^5.15.0", - "react-native-reanimated": "^4.3.0", - "react-native-saf-x": "^2.2.3", + "react-native-gesture-handler": "^2.32.0", + "react-native-mmkv": "^4.3.2", + "react-native-pager-view": "^8.0.2", + "react-native-paper": "^5.15.3", + "react-native-reanimated": "^4.5.0", "react-native-safe-area-context": "^5.7.0", - "react-native-screens": "^4.24.0", + "react-native-screens": "^4.25.2", "react-native-shimmer-placeholder": "^2.0.9", - "react-native-tab-view": "^4.3.0", + "react-native-svg": "15.15.4", + "react-native-tab-view": "^4.3.2", "react-native-url-polyfill": "^3.0.0", "react-native-webview": "^13.16.1", - "react-native-worklets": "^0.8.1", - "react-native-zip-archive": "^7.0.2", - "sanitize-html": "^2.17.2", - "urlencode": "^2.0.0" + "react-native-worklets": "^0.10.0", + "sanitize-html": "^2.17.6", + "urlencode": "^2.0.0", + "zustand": "^5.0.14" }, "devDependencies": { - "@babel/core": "^7.29.0", - "@babel/plugin-transform-export-namespace-from": "^7.27.1", - "@babel/preset-env": "^7.29.2", - "@babel/runtime": "^7.29.2", - "@react-native-community/cli": "^20.1.3", - "@react-native-community/cli-platform-android": "^20.1.3", - "@react-native-community/cli-platform-ios": "^20.1.3", - "@react-native/babel-preset": "^0.83.4", - "@react-native/eslint-config": "^0.83.4", - "@react-native/eslint-plugin": "^0.83.4", - "@react-native/metro-config": "^0.83.4", - "@react-native/typescript-config": "^0.83.4", + "@babel/core": "^7.29.7", + "@babel/plugin-transform-export-namespace-from": "^7.29.7", + "@babel/preset-env": "^7.29.7", + "@babel/runtime": "^7.29.7", + "@eslint/eslintrc": "^3.3.6", + "@rozenite/expo-atlas-plugin": "^1.13.0", + "@rozenite/metro": "^1.13.0", + "@rozenite/sqlite-plugin": "^1.13.0", "@testing-library/react-native": "^13.3.3", "@types/better-sqlite3": "^7.6.13", "@types/color": "^4.2.1", "@types/jest": "^29.5.14", "@types/lodash-es": "^4.17.12", - "@types/react": "~19.2.14", + "@types/react": "~19.2.17", "@types/sanitize-html": "^2.16.1", - "@typescript-eslint/eslint-plugin": "^8.58.0", - "@typescript-eslint/parser": "^8.58.0", + "@typescript-eslint/eslint-plugin": "^8.64.0", + "@typescript-eslint/parser": "^8.64.0", "babel-plugin-module-resolver": "^5.0.3", "babel-plugin-react-compiler": "^1.0.0", - "better-sqlite3": "^12.8.0", + "better-sqlite3": "^12.11.1", "drizzle-kit": "1.0.0-beta.20", - "eslint": "^8.57.1", - "eslint-plugin-eslint-comments": "^3.2.0", + "eslint": "^9.0.0", + "eslint-config-expo": "~57.0.0", "eslint-plugin-ft-flow": "^3.0.11", - "eslint-plugin-jest": "^29.15.1", - "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^7.0.1", - "eslint-plugin-react-native": "^5.0.0", + "eslint-plugin-jest": "^29.15.4", "eslint-plugin-testing-library": "^7.16.2", - "hermes-compiler": "^250829098.0.2", "husky": "^7.0.4", "jest": "^29.7.0", - "jest-expo": "^55.0.11", + "jest-expo": "^57.0.2", "lint-staged": "^12.5.0", + "nitrogen": "^0.36.1", "prettier": "2.8.8", - "react-test-renderer": "19.2.4", - "typescript": "~5.9.3" + "react-native-nitro-modules": "^0.36.0", + "react-test-renderer": "19.2.3", + "typescript": "~6.0.3" + }, + "expo": { + "doctor": { + "reactNativeDirectoryCheck": { + "exclude": [ + "native-file", + "nitro-tts", + "native-volume-button-listener", + "native-zip-archive", + "nitro-epub", + "native-background-tasks", + "@react-native-vector-icons/material-design-icons", + "lottie-ios" + ] + } + } }, "lint-staged": { - "*.{js,jsx,ts,tsx}": [ - "eslint --fix", - "prettier --check" + "*.{ts,tsx,js,jsx}": [ + "prettier --write", + "eslint --fix" + ], + "*.{json,md,css,scss,yml,yaml}": [ + "prettier --write" ] }, "engines": { - "node": ">=20" + "node": ">=22.11.0" }, - "packageManager": "pnpm@10.27.0" + "packageManager": "pnpm@11.15.0" } diff --git a/patches/@legendapp__list@3.3.3.patch b/patches/@legendapp__list@3.3.3.patch new file mode 100644 index 000000000..914356bb9 --- /dev/null +++ b/patches/@legendapp__list@3.3.3.patch @@ -0,0 +1,62 @@ +diff --git a/react-native.js b/react-native.js +index 229f09a8230ab24079d471f65d28d36908f0f2ea..5c9a6c2501d8de3c44e138593831f3442b4b9204 100644 +--- a/react-native.js ++++ b/react-native.js +@@ -7256,15 +7256,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded + useWindowScroll: useWindowScrollResolved + }; + state.refScroller = refScroller; +- if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) { +- resetAdaptiveRender(ctx); +- } +- if (shouldResetFreshDataLayout) { +- resetInitialRenderState(ctx, { +- resetInitialScroll: !!initialScrollProp, +- resetLayout: true +- }); +- } ++ React2.useLayoutEffect(() => { ++ if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) { ++ resetAdaptiveRender(ctx); ++ } ++ if (shouldResetFreshDataLayout) { ++ resetInitialRenderState(ctx, { ++ resetInitialScroll: !!initialScrollProp, ++ resetLayout: true ++ }); ++ } ++ }, [experimental_adaptiveRender, initialScrollProp, isFirstLocal, previousAdaptiveRender, shouldResetFreshDataLayout]); + const memoizedLastItemKeys = React2.useMemo(() => { + if (!dataProp.length) return []; + return Array.from( +diff --git a/react-native.mjs b/react-native.mjs +index c2e0f3823f524e31e2105a5fbda76be78c926226..dc0c9c6d5b887968c631dd0ba1646fd40ba28c63 100644 +--- a/react-native.mjs ++++ b/react-native.mjs +@@ -7235,15 +7235,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded + useWindowScroll: useWindowScrollResolved + }; + state.refScroller = refScroller; +- if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) { +- resetAdaptiveRender(ctx); +- } +- if (shouldResetFreshDataLayout) { +- resetInitialRenderState(ctx, { +- resetInitialScroll: !!initialScrollProp, +- resetLayout: true +- }); +- } ++ useLayoutEffect(() => { ++ if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) { ++ resetAdaptiveRender(ctx); ++ } ++ if (shouldResetFreshDataLayout) { ++ resetInitialRenderState(ctx, { ++ resetInitialScroll: !!initialScrollProp, ++ resetLayout: true ++ }); ++ } ++ }, [experimental_adaptiveRender, initialScrollProp, isFirstLocal, previousAdaptiveRender, shouldResetFreshDataLayout]); + const memoizedLastItemKeys = useMemo(() => { + if (!dataProp.length) return []; + return Array.from( diff --git a/plugins/android/android-build-types.gradle b/plugins/android/android-build-types.gradle new file mode 100644 index 000000000..afe10204f --- /dev/null +++ b/plugins/android/android-build-types.gradle @@ -0,0 +1,54 @@ +def releaseAbiSplitsEnabled = providers + .gradleProperty("releaseAbiSplits") + .map { value -> value.toBoolean() } + .getOrElse(false) + +react { + hermesFlags = ["-O", "-output-source-map"] +} + +android { + splits { + abi { + enable releaseAbiSplitsEnabled + reset() + include "arm64-v8a", "armeabi-v7a", "x86", "x86_64" + universalApk releaseAbiSplitsEnabled + } + } + + buildTypes { + debug { + signingConfig signingConfigs.debug + applicationIdSuffix "debug" + versionNameSuffix "-debug" + } + + preRelease { + initWith release + matchingFallbacks = ["release"] + applicationIdSuffix ".preRelease" + versionNameSuffix "-pre-release" + signingConfig signingConfigs.debug + resValue "string", "app_name", "LNReader Preview" + } + } +} + +def preReleaseVersionCode = providers.gradleProperty("preReleaseVersionCode") +def preReleaseVersionName = providers.gradleProperty("preReleaseVersionName") + +androidComponents { + onVariants(selector().withBuildType("preRelease")) { variant -> + variant.outputs.each { output -> + if (preReleaseVersionCode.isPresent()) { + output.versionCode.set( + preReleaseVersionCode.map { value -> value.toInteger() }, + ) + } + if (preReleaseVersionName.isPresent()) { + output.versionName.set(preReleaseVersionName) + } + } + } +} diff --git a/plugins/withAndroidCustomizations.js b/plugins/withAndroidCustomizations.js new file mode 100644 index 000000000..4b5099175 --- /dev/null +++ b/plugins/withAndroidCustomizations.js @@ -0,0 +1,190 @@ +const { + AndroidConfig, + withAndroidColors, + withAndroidColorsNight, + withAndroidStyles, + withAppBuildGradle, + withDangerousMod, +} = require('@expo/config-plugins'); +const fs = require('fs'); +const path = require('path'); + +const BUILD_TYPES_MARKER = '// @generated by with-android-customizations'; + +const BUILD_TYPES_APPLY = `${BUILD_TYPES_MARKER} +apply from: new File( + rootDir, + "../plugins/android/android-build-types.gradle", +) +`; + +const ANDROID_ASSET_VARIANTS = [ + { source: 'release', target: 'main' }, + { source: 'preview', target: 'preRelease' }, + { source: 'debug', target: 'debug' }, +]; + +const LAUNCHER_RESOURCE_NAMES = [ + 'ic_launcher', + 'ic_launcher_background', + 'ic_launcher_foreground', + 'ic_launcher_monochrome', + 'ic_launcher_round', +]; + +const DRAWABLE_RESOURCE_NAMES = [ + 'notification_icon', + 'splash_icon', + 'splashscreen_logo', +]; +const ANDROID_DENSITIES = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi', 'xxxhdpi']; + +/** + * `AppTheme` inherits `android:windowBackground` from + * `Theme.AppCompat.DayNight.NoActionBar`, which resolves to a near-white colour + * whenever the system is in light mode. The window is visible for the frames + * between the splash screen going away and React drawing its first frame, so + * the app opens with a white flash for anyone using a dark theme on a light + * system. These are the backgrounds of the default light and dark themes. + */ +const WINDOW_BACKGROUND_RESOURCE = 'lnreaderWindowBackground'; +const WINDOW_BACKGROUND_LIGHT = '#FEFBFF'; +const WINDOW_BACKGROUND_DARK = '#1B1B1F'; + +const withWindowBackgroundColors = config => { + config = withAndroidColors(config, config => { + config.modResults = AndroidConfig.Colors.assignColorValue( + config.modResults, + { name: WINDOW_BACKGROUND_RESOURCE, value: WINDOW_BACKGROUND_LIGHT }, + ); + + return config; + }); + + return withAndroidColorsNight(config, config => { + config.modResults = AndroidConfig.Colors.assignColorValue( + config.modResults, + { name: WINDOW_BACKGROUND_RESOURCE, value: WINDOW_BACKGROUND_DARK }, + ); + + return config; + }); +}; + +const withWindowBackgroundStyle = config => + withAndroidStyles(config, config => { + config.modResults = AndroidConfig.Styles.assignStylesValue( + config.modResults, + { + add: true, + parent: AndroidConfig.Styles.getAppThemeGroup(), + name: 'android:windowBackground', + value: `@color/${WINDOW_BACKGROUND_RESOURCE}`, + }, + ); + + return config; + }); + +const withBuildGradleCustomizations = config => + withAppBuildGradle(config, config => { + const { contents } = config.modResults; + + if (!contents.includes(BUILD_TYPES_MARKER)) { + config.modResults.contents = `${contents.trimEnd()} + +${BUILD_TYPES_APPLY}`; + } + + return config; + }); + +const removeGeneratedIconResources = resDir => { + if (!fs.existsSync(resDir)) { + return; + } + + for (const resourceDirName of fs.readdirSync(resDir)) { + const resourceDir = path.join(resDir, resourceDirName); + + if (!fs.statSync(resourceDir).isDirectory()) { + continue; + } + + if (resourceDirName.startsWith('mipmap-')) { + for (const resourceName of LAUNCHER_RESOURCE_NAMES) { + for (const extension of ['png', 'webp', 'xml']) { + fs.rmSync(path.join(resourceDir, `${resourceName}.${extension}`), { + force: true, + }); + } + } + } + + if (resourceDirName.startsWith('drawable-')) { + for (const resourceName of DRAWABLE_RESOURCE_NAMES) { + for (const extension of ['png', 'webp', 'xml']) { + fs.rmSync(path.join(resourceDir, `${resourceName}.${extension}`), { + force: true, + }); + } + } + } + } +}; + +const installVariantSplashIcons = resDir => { + for (const density of ANDROID_DENSITIES) { + const drawableDir = path.join(resDir, `drawable-${density}`); + const sourcePath = path.join(drawableDir, 'splash_icon.png'); + const splashPath = path.join(drawableDir, 'splashscreen_logo.png'); + const nightDrawableDir = path.join(resDir, `drawable-night-${density}`); + const nightSplashPath = path.join( + nightDrawableDir, + 'splashscreen_logo.png', + ); + + fs.renameSync(sourcePath, splashPath); + fs.mkdirSync(nightDrawableDir, { recursive: true }); + fs.copyFileSync(splashPath, nightSplashPath); + } +}; + +const withAndroidVariantAssets = config => + withDangerousMod(config, [ + 'android', + config => { + const { projectRoot, platformProjectRoot } = config.modRequest; + + for (const variant of ANDROID_ASSET_VARIANTS) { + const sourceResDir = path.join( + projectRoot, + 'assets', + 'android-icons', + variant.source, + 'res', + ); + const targetResDir = path.join( + platformProjectRoot, + 'app', + 'src', + variant.target, + 'res', + ); + + removeGeneratedIconResources(targetResDir); + fs.mkdirSync(targetResDir, { recursive: true }); + fs.cpSync(sourceResDir, targetResDir, { recursive: true }); + installVariantSplashIcons(targetResDir); + } + + return config; + }, + ]); + +module.exports = config => { + config = withBuildGradleCustomizations(config); + config = withWindowBackgroundColors(config); + config = withWindowBackgroundStyle(config); + return withAndroidVariantAssets(config); +}; diff --git a/plugins/withReaderAssets.js b/plugins/withReaderAssets.js new file mode 100644 index 000000000..bef8498c0 --- /dev/null +++ b/plugins/withReaderAssets.js @@ -0,0 +1,31 @@ +const { withDangerousMod } = require('@expo/config-plugins'); +const fs = require('fs'); +const path = require('path'); + +const withReaderAssets = (config) => { + config = withDangerousMod(config, [ + 'android', + (config) => { + const projectRoot = config.modRequest.projectRoot; + const platformRoot = config.modRequest.platformProjectRoot; + const assetsDir = path.join(platformRoot, 'app', 'src', 'main', 'assets'); + const sourceRoot = path.join(projectRoot, 'assets', 'reader'); + + fs.mkdirSync(assetsDir, { recursive: true }); + + for (const subdir of ['css', 'js', 'fonts']) { + const src = path.join(sourceRoot, subdir); + const dest = path.join(assetsDir, subdir); + if (fs.existsSync(src)) { + fs.cpSync(src, dest, { recursive: true }); + } + } + + return config; + }, + ]); + + return config; +}; + +module.exports = withReaderAssets; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e09012fea..d707f3f33 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,64 +4,61 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + '@legendapp/list@3.3.3': 37a9c8d7d9c1bd33c957ba6ea78291c7b08532dc32de4925f3d612cb41f1a659 + importers: .: dependencies: - '@cd-z/react-native-epub-creator': - specifier: ^3.0.0 - version: 3.0.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@expo/dom-webview': + specifier: ^57.0.1 + version: 57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) '@gorhom/bottom-sheet': - specifier: ^5.2.8 - version: 5.2.8(@types/react@19.2.14)(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-reanimated@4.3.0(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^5.2.14 + version: 5.2.14(24fcb93f20601afa4f41878e40cdc0fb) '@legendapp/list': - specifier: ^2.0.19 - version: 2.0.19(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^3.3.3 + version: 3.3.3(patch_hash=37a9c8d7d9c1bd33c957ba6ea78291c7b08532dc32de4925f3d612cb41f1a659)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) '@noble/ciphers': - specifier: ^2.1.1 - version: 2.1.1 + specifier: ^2.2.0 + version: 2.2.0 '@op-engineering/op-sqlite': - specifier: ^15.2.9 - version: 15.2.9(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^15.2.14 + version: 15.2.14(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + '@pchmn/expo-material3-theme': + specifier: ^1.4.0 + version: 1.4.0(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) '@preeternal/react-native-cookie-manager': - specifier: ^6.3.1 - version: 6.3.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@react-native-community/slider': - specifier: ^5.1.2 - version: 5.1.2 + specifier: ^6.3.3 + version: 6.3.3(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) '@react-native-documents/picker': specifier: ^12.0.1 - version: 12.0.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + version: 12.0.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) '@react-native-google-signin/google-signin': specifier: ^16.1.2 - version: 16.1.2(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + version: 16.1.2(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) '@react-native-vector-icons/common': - specifier: ^13.0.0 - version: 13.0.0(@react-native/assets-registry@0.83.4)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^13.0.1 + version: 13.0.1(@react-native/assets-registry@0.86.0)(expo-font@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) '@react-native-vector-icons/material-design-icons': - specifier: ^13.0.0 - version: 13.0.0(@react-native/assets-registry@0.83.4)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@react-native/assets-registry': - specifier: ^0.83.4 - version: 0.83.4 - '@react-native/codegen': - specifier: ^0.83.4 - version: 0.83.4(@babel/core@7.29.0) - '@react-native/gradle-plugin': - specifier: ^0.83.4 - version: 0.83.4 + specifier: ^13.1.2 + version: 13.1.2(@expo/config-plugins@57.0.5(supports-color@9.4.0)(typescript@6.0.3))(@react-native/assets-registry@0.86.0)(expo-font@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) '@react-navigation/bottom-tabs': - specifier: ^7.15.9 - version: 7.15.9(@react-navigation/native@7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-screens@4.24.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^7.18.11 + version: 7.18.11(b07337b648742f75a968ee96fc7e36e1) '@react-navigation/native': - specifier: ^7.2.2 - version: 7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^7.3.11 + version: 7.3.11(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) '@react-navigation/native-stack': - specifier: ^7.14.10 - version: 7.14.10(@react-navigation/native@7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-screens@4.24.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^7.18.3 + version: 7.18.3(b07337b648742f75a968ee96fc7e36e1) '@react-navigation/stack': - specifier: ^7.8.9 - version: 7.8.9(05167d8527e2c9f68e950b4e3fb9e48b) + specifier: ^7.10.14 + version: 7.10.14(84780aa2bc277522b3a415ae0584f553) + '@shopify/flash-list': + specifier: ^2.0.2 + version: 2.3.2(@babel/runtime@7.29.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) babel-plugin-inline-import: specifier: ^3.0.0 version: 3.0.0 @@ -72,50 +69,59 @@ importers: specifier: ^5.0.3 version: 5.0.3 dayjs: - specifier: ^1.11.20 - version: 1.11.20 + specifier: ^1.11.21 + version: 1.11.21 drizzle-orm: - specifier: 1.0.0-beta.20 - version: 1.0.0-beta.20(@op-engineering/op-sqlite@15.2.9(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@sinclair/typebox@0.34.49)(@types/better-sqlite3@7.6.13)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(better-sqlite3@12.8.0)(expo-sqlite@16.0.10(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(mssql@11.0.1(@azure/core-client@1.10.1))(zod@4.3.6) + specifier: 1.0.0-beta.22 + version: 1.0.0-beta.22(@op-engineering/op-sqlite@15.2.14(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(@sinclair/typebox@0.34.52)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.11.1)(zod@4.4.3) expo: - specifier: ^55.0.9 - version: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + specifier: ^57.0.7 + version: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + expo-build-properties: + specifier: ~57.0.6 + version: 57.0.6(expo@57.0.7) expo-clipboard: - specifier: ~55.0.9 - version: 55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + expo-dev-client: + specifier: ^57.0.7 + version: 57.0.7(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)) expo-document-picker: - specifier: ~55.0.9 - version: 55.0.9(expo@55.0.9) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.7) expo-file-system: - specifier: ~55.0.12 - version: 55.0.12(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)) expo-haptics: - specifier: ~55.0.9 - version: 55.0.9(expo@55.0.9) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.7) + expo-image: + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) expo-keep-awake: - specifier: ~55.0.4 - version: 55.0.4(expo@55.0.9)(react@19.2.4) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.7)(react@19.2.3) expo-linear-gradient: - specifier: ~55.0.9 - version: 55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) expo-linking: - specifier: ~55.0.9 - version: 55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + specifier: ~57.0.3 + version: 57.0.3(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0) expo-localization: - specifier: ~55.0.9 - version: 55.0.9(expo@55.0.9)(react@19.2.4) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.7)(react@19.2.3) expo-navigation-bar: - specifier: ~55.0.9 - version: 55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - expo-notifications: - specifier: ~55.0.14 - version: 55.0.14(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + specifier: ~57.0.2 + version: 57.0.2(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0) expo-speech: - specifier: ~55.0.9 - version: 55.0.9(expo@55.0.9) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.7) + expo-splash-screen: + specifier: ^57.0.4 + version: 57.0.4(expo@57.0.7)(supports-color@9.4.0)(typescript@6.0.3) expo-web-browser: - specifier: ~55.0.10 - version: 55.0.10(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)) htmlparser2: specifier: ^12.0.0 version: 12.0.0 @@ -123,141 +129,111 @@ importers: specifier: ^4.5.3 version: 4.5.3 lodash-es: - specifier: ^4.17.23 - version: 4.17.23 - lottie-ios: - specifier: ^3.5.0 - version: 3.5.0 + specifier: ^4.18.1 + version: 4.18.1 lottie-react-native: - specifier: ^5.1.6 - version: 5.1.6(lottie-ios@3.5.0)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^7.3.8 + version: 7.3.8(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) protobufjs: - specifier: ^8.0.0 - version: 8.0.0 + specifier: ^8.7.1 + version: 8.7.1 react: - specifier: ^19.2.4 - version: 19.2.4 + specifier: 19.2.3 + version: 19.2.3 react-native: - specifier: ^0.83.4 - version: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-background-actions: - specifier: ^4.0.1 - version: 4.0.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)) - react-native-config: - specifier: ^1.6.1 - version: 1.6.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: 0.86.0 + version: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) react-native-device-info: specifier: ^15.0.2 - version: 15.0.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)) + version: 15.0.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)) react-native-draggable-flatlist: specifier: ^4.0.3 - version: 4.0.3(@babel/core@7.29.0)(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-reanimated@4.3.0(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)) + version: 4.0.3(caa27418ce3e0b0439d72a52bab7509e) react-native-drawer-layout: - specifier: ^4.2.2 - version: 4.2.2(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-reanimated@4.3.0(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^4.2.8 + version: 4.2.8(303ab35fdba57cc5c055f2029a2df72d) react-native-edge-to-edge: specifier: ^1.8.1 - version: 1.8.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + version: 1.8.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-native-error-boundary: specifier: ^3.1.0 - version: 3.1.0(react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-file-access: - specifier: ^4.0.2 - version: 4.0.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + version: 3.1.0(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-native-gesture-handler: - specifier: ^2.30.1 - version: 2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-lottie-splash-screen: - specifier: ^1.1.2 - version: 1.1.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^2.32.0 + version: 2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-native-mmkv: - specifier: ^4.3.0 - version: 4.3.0(react-native-nitro-modules@0.35.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-nitro-modules: - specifier: ^0.35.2 - version: 0.35.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^4.3.2 + version: 4.3.2(react-native-nitro-modules@0.36.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-native-pager-view: - specifier: ^8.0.0 - version: 8.0.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^8.0.2 + version: 8.0.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-native-paper: - specifier: ^5.15.0 - version: 5.15.0(react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^5.15.3 + version: 5.15.3(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-native-reanimated: - specifier: ^4.3.0 - version: 4.3.0(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-saf-x: - specifier: ^2.2.3 - version: 2.2.3(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^4.5.0 + version: 4.5.2(react-native-worklets@0.10.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-native-safe-area-context: specifier: ^5.7.0 - version: 5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + version: 5.8.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-native-screens: - specifier: ^4.24.0 - version: 4.24.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^4.25.2 + version: 4.26.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-native-shimmer-placeholder: specifier: ^2.0.9 - version: 2.0.9(prop-types@15.8.1)(react-native-linear-gradient@2.8.3(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)) + version: 2.0.9(prop-types@15.8.1)(react-native-linear-gradient@2.8.3(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)) + react-native-svg: + specifier: 15.15.4 + version: 15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-native-tab-view: - specifier: ^4.3.0 - version: 4.3.0(react-native-pager-view@8.0.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^4.3.2 + version: 4.3.2(react-native-pager-view@8.0.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-native-url-polyfill: specifier: ^3.0.0 - version: 3.0.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)) + version: 3.0.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)) react-native-webview: specifier: ^13.16.1 - version: 13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + version: 13.17.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-native-worklets: - specifier: ^0.8.1 - version: 0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-zip-archive: - specifier: ^7.0.2 - version: 7.0.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + specifier: ^0.10.0 + version: 0.10.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0) sanitize-html: - specifier: ^2.17.2 - version: 2.17.2 + specifier: ^2.17.6 + version: 2.17.6 urlencode: specifier: ^2.0.0 version: 2.0.0 + zustand: + specifier: ^5.0.14 + version: 5.0.14(@types/react@19.2.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) devDependencies: '@babel/core': - specifier: ^7.29.0 - version: 7.29.0 + specifier: ^7.29.7 + version: 7.29.7(supports-color@9.4.0) '@babel/plugin-transform-export-namespace-from': - specifier: ^7.27.1 - version: 7.27.1(@babel/core@7.29.0) + specifier: ^7.29.7 + version: 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) '@babel/preset-env': - specifier: ^7.29.2 - version: 7.29.2(@babel/core@7.29.0) + specifier: ^7.29.7 + version: 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) '@babel/runtime': - specifier: ^7.29.2 - version: 7.29.2 - '@react-native-community/cli': - specifier: ^20.1.3 - version: 20.1.3(typescript@5.9.3) - '@react-native-community/cli-platform-android': - specifier: ^20.1.3 - version: 20.1.3 - '@react-native-community/cli-platform-ios': - specifier: ^20.1.3 - version: 20.1.3 - '@react-native/babel-preset': - specifier: ^0.83.4 - version: 0.83.4(@babel/core@7.29.0) - '@react-native/eslint-config': - specifier: ^0.83.4 - version: 0.83.4(eslint@8.57.1)(jest@29.7.0(@types/node@25.5.0))(prettier@2.8.8)(typescript@5.9.3) - '@react-native/eslint-plugin': - specifier: ^0.83.4 - version: 0.83.4 - '@react-native/metro-config': - specifier: ^0.83.4 - version: 0.83.4(@babel/core@7.29.0) - '@react-native/typescript-config': - specifier: ^0.83.4 - version: 0.83.4 + specifier: ^7.29.7 + version: 7.29.7 + '@eslint/eslintrc': + specifier: ^3.3.6 + version: 3.3.6(supports-color@9.4.0) + '@rozenite/expo-atlas-plugin': + specifier: ^1.13.0 + version: 1.13.0(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0) + '@rozenite/metro': + specifier: ^1.13.0 + version: 1.13.0(supports-color@9.4.0) + '@rozenite/sqlite-plugin': + specifier: ^1.13.0 + version: 1.13.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) '@testing-library/react-native': specifier: ^13.3.3 - version: 13.3.3(jest@29.7.0(@types/node@25.5.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react-test-renderer@19.2.4(react@19.2.4))(react@19.2.4) + version: 13.3.3(jest@29.7.0(@types/node@26.1.1)(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) '@types/better-sqlite3': specifier: ^7.6.13 version: 7.6.13 @@ -271,17 +247,17 @@ importers: specifier: ^4.17.12 version: 4.17.12 '@types/react': - specifier: ~19.2.14 - version: 19.2.14 + specifier: ~19.2.17 + version: 19.2.17 '@types/sanitize-html': specifier: ^2.16.1 version: 2.16.1 '@typescript-eslint/eslint-plugin': - specifier: ^8.58.0 - version: 8.58.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + specifier: ^8.64.0 + version: 8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) '@typescript-eslint/parser': - specifier: ^8.58.0 - version: 8.58.0(eslint@8.57.1)(typescript@5.9.3) + specifier: ^8.64.0 + version: 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) babel-plugin-module-resolver: specifier: ^5.0.3 version: 5.0.3 @@ -289,172 +265,88 @@ importers: specifier: ^1.0.0 version: 1.0.0 better-sqlite3: - specifier: ^12.8.0 - version: 12.8.0 + specifier: ^12.11.1 + version: 12.11.1 drizzle-kit: specifier: 1.0.0-beta.20 version: 1.0.0-beta.20 eslint: - specifier: ^8.57.1 - version: 8.57.1 - eslint-plugin-eslint-comments: - specifier: ^3.2.0 - version: 3.2.0(eslint@8.57.1) + specifier: ^9.0.0 + version: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) + eslint-config-expo: + specifier: ~57.0.0 + version: 57.0.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) eslint-plugin-ft-flow: specifier: ^3.0.11 - version: 3.0.11(eslint@8.57.1)(hermes-eslint@0.33.3) + version: 3.0.11(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(hermes-eslint@0.37.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))) eslint-plugin-jest: - specifier: ^29.15.1 - version: 29.15.1(@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@29.7.0(@types/node@25.5.0))(typescript@5.9.3) - eslint-plugin-react: - specifier: ^7.37.5 - version: 7.37.5(eslint@8.57.1) - eslint-plugin-react-hooks: - specifier: ^7.0.1 - version: 7.0.1(eslint@8.57.1) - eslint-plugin-react-native: - specifier: ^5.0.0 - version: 5.0.0(eslint@8.57.1) + specifier: ^29.15.4 + version: 29.15.4(@typescript-eslint/eslint-plugin@8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(jest@29.7.0(@types/node@26.1.1)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) eslint-plugin-testing-library: specifier: ^7.16.2 - version: 7.16.2(eslint@8.57.1)(typescript@5.9.3) - hermes-compiler: - specifier: ^250829098.0.2 - version: 250829098.0.2 + version: 7.16.2(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) husky: specifier: ^7.0.4 version: 7.0.4 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@25.5.0) + version: 29.7.0(@types/node@26.1.1)(supports-color@9.4.0) jest-expo: - specifier: ^55.0.11 - version: 55.0.11(@babel/core@7.29.0)(expo@55.0.9)(jest@29.7.0(@types/node@25.5.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + specifier: ^57.0.2 + version: 57.0.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(expo@57.0.7)(jest@29.7.0(@types/node@26.1.1)(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0) lint-staged: specifier: ^12.5.0 version: 12.5.0 + nitrogen: + specifier: ^0.36.1 + version: 0.36.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) prettier: specifier: 2.8.8 version: 2.8.8 + react-native-nitro-modules: + specifier: ^0.36.0 + version: 0.36.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) react-test-renderer: - specifier: 19.2.4 - version: 19.2.4(react@19.2.4) + specifier: 19.2.3 + version: 19.2.3(react@19.2.3) typescript: - specifier: ~5.9.3 - version: 5.9.3 + specifier: ~6.0.3 + version: 6.0.3 packages: - '@azure-rest/core-client@2.5.1': - resolution: {integrity: sha512-EHaOXW0RYDKS5CFffnixdyRPak5ytiCtU7uXDcP/uiY+A6jFRwNGzzJBiznkCzvi5EYpY+YWinieqHb0oY916A==} - engines: {node: '>=20.0.0'} - - '@azure/abort-controller@2.1.2': - resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==} - engines: {node: '>=18.0.0'} - - '@azure/core-auth@1.10.1': - resolution: {integrity: sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==} - engines: {node: '>=20.0.0'} - - '@azure/core-client@1.10.1': - resolution: {integrity: sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==} - engines: {node: '>=20.0.0'} - - '@azure/core-http-compat@2.3.2': - resolution: {integrity: sha512-Tf6ltdKzOJEgxZeWLCjMxrxbodB/ZeCbzzA1A2qHbhzAjzjHoBVSUeSl/baT/oHAxhc4qdqVaDKnc2+iE932gw==} - engines: {node: '>=20.0.0'} - peerDependencies: - '@azure/core-client': ^1.10.0 - '@azure/core-rest-pipeline': ^1.22.0 - - '@azure/core-lro@2.7.2': - resolution: {integrity: sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==} - engines: {node: '>=18.0.0'} - - '@azure/core-paging@1.6.2': - resolution: {integrity: sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==} - engines: {node: '>=18.0.0'} - - '@azure/core-rest-pipeline@1.23.0': - resolution: {integrity: sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ==} - engines: {node: '>=20.0.0'} - - '@azure/core-tracing@1.3.1': - resolution: {integrity: sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==} - engines: {node: '>=20.0.0'} - - '@azure/core-util@1.13.1': - resolution: {integrity: sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==} - engines: {node: '>=20.0.0'} - - '@azure/identity@4.13.1': - resolution: {integrity: sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==} - engines: {node: '>=20.0.0'} - - '@azure/keyvault-common@2.0.0': - resolution: {integrity: sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==} - engines: {node: '>=18.0.0'} - - '@azure/keyvault-keys@4.10.0': - resolution: {integrity: sha512-eDT7iXoBTRZ2n3fLiftuGJFD+yjkiB1GNqzU2KbY1TLYeXeSPVTVgn2eJ5vmRTZ11978jy2Kg2wI7xa9Tyr8ag==} - engines: {node: '>=18.0.0'} - - '@azure/logger@1.3.0': - resolution: {integrity: sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==} - engines: {node: '>=20.0.0'} - - '@azure/msal-browser@5.6.2': - resolution: {integrity: sha512-ZgcN9ToRJ80f+wNPBBKYJ+DG0jlW7ktEjYtSNkNsTrlHVMhKB8tKMdI1yIG1I9BJtykkXtqnuOjlJaEMC7J6aw==} - engines: {node: '>=0.8.0'} - - '@azure/msal-common@16.4.0': - resolution: {integrity: sha512-twXt09PYtj1PffNNIAzQlrBd0DS91cdA6i1gAfzJ6BnPM4xNk5k9q/5xna7jLIjU3Jnp0slKYtucshGM8OGNAw==} - engines: {node: '>=0.8.0'} - - '@azure/msal-node@5.1.1': - resolution: {integrity: sha512-71grXU6+5hl+3CL3joOxlj/AW6rmhthuTlG0fRqsTrhPArQBpZuUFzCIlKOGdcafLUa/i1hBdV78ZxJdlvRA+g==} - engines: {node: '>=20'} - - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.0': - resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} - '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} - '@babel/eslint-parser@7.28.6': - resolution: {integrity: sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==} - engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} - peerDependencies: - '@babel/core': ^7.11.0 - eslint: ^7.5.0 || ^8.0.0 || ^9.0.0 - - '@babel/generator@7.29.1': - resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.27.3': - resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.28.6': - resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} + '@babel/helper-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-create-regexp-features-plugin@7.28.5': - resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} + '@babel/helper-create-regexp-features-plugin@7.29.7': + resolution: {integrity: sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -464,111 +356,117 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} - '@babel/helper-member-expression-to-functions@7.28.5': - resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-optimise-call-expression@7.27.1': - resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + '@babel/helper-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} engines: {node: '>=6.9.0'} - '@babel/helper-remap-async-to-generator@7.27.1': - resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + '@babel/helper-remap-async-to-generator@7.29.7': + resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-replace-supers@7.28.6': - resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} - '@babel/helper-wrap-function@7.28.6': - resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} + '@babel/helper-wrap-function@7.29.7': + resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.29.2': - resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.2': - resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': - resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7': + resolution: {integrity: sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7': + resolution: {integrity: sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': - resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7': + resolution: {integrity: sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': - resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7': + resolution: {integrity: sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': - resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7': + resolution: {integrity: sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6': - resolution: {integrity: sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==} + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7': + resolution: {integrity: sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-proposal-decorators@7.29.0': - resolution: {integrity: sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==} + '@babel/plugin-proposal-decorators@7.29.7': + resolution: {integrity: sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-proposal-export-default-from@7.27.1': - resolution: {integrity: sha512-hjlsMBl1aJc5lp8MoCDEZCiYzlgdRAShOjAfRw6X+GlpLpUPU7c3XNLsKFZbQk/1cRzBlJ7CXg3xJAJMrFa1Uw==} + '@babel/plugin-proposal-export-default-from@7.29.7': + resolution: {integrity: sha512-p+G5BNXDcy3bOXplhY4HybQ1GxH3i2Tppmdm/3epyRu2VgJJZuUlZ61MqRTg582Q7ZLBdP7fePYvsumSEkMxcQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -600,8 +498,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-decorators@7.28.6': - resolution: {integrity: sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==} + '@babel/plugin-syntax-decorators@7.29.7': + resolution: {integrity: sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -611,26 +509,26 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-export-default-from@7.28.6': - resolution: {integrity: sha512-Svlx1fjJFnNz0LZeUaybRukSxZI3KkpApUmIRzEdXC5k8ErTOz0OD0kNrICi5Vc3GlpP5ZCeRyRO+mfWTSz+iQ==} + '@babel/plugin-syntax-export-default-from@7.29.7': + resolution: {integrity: sha512-foag0BB37ROhdeIX9O8G0jX7hw0UekJc04cHMrYLOnrErsnBKqJGHJ8eDRpoCFZBvEPPygmmtw4qyU97qa4oOw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-flow@7.28.6': - resolution: {integrity: sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==} + '@babel/plugin-syntax-flow@7.29.7': + resolution: {integrity: sha512-ajMX6QPcyomotqwpzhkYGxcK2i/us0rs1Qo9QvUpa+Fca0FTmqrzKrctoIYLMxcOhGZldGT/BAVkRGTWBiR8gQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-assertions@7.28.6': - resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==} + '@babel/plugin-syntax-import-assertions@7.29.7': + resolution: {integrity: sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.28.6': - resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + '@babel/plugin-syntax-import-attributes@7.29.7': + resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -645,8 +543,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-jsx@7.28.6': - resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + '@babel/plugin-syntax-jsx@7.29.7': + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -693,8 +591,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.28.6': - resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + '@babel/plugin-syntax-typescript@7.29.7': + resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -705,368 +603,368 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-arrow-functions@7.27.1': - resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + '@babel/plugin-transform-arrow-functions@7.29.7': + resolution: {integrity: sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-generator-functions@7.29.0': - resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} + '@babel/plugin-transform-async-generator-functions@7.29.7': + resolution: {integrity: sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-to-generator@7.28.6': - resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} + '@babel/plugin-transform-async-to-generator@7.29.7': + resolution: {integrity: sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoped-functions@7.27.1': - resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + '@babel/plugin-transform-block-scoped-functions@7.29.7': + resolution: {integrity: sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoping@7.28.6': - resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} + '@babel/plugin-transform-block-scoping@7.29.7': + resolution: {integrity: sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-properties@7.28.6': - resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} + '@babel/plugin-transform-class-properties@7.29.7': + resolution: {integrity: sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-static-block@7.28.6': - resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==} + '@babel/plugin-transform-class-static-block@7.29.7': + resolution: {integrity: sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.28.6': - resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} + '@babel/plugin-transform-classes@7.29.7': + resolution: {integrity: sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-computed-properties@7.28.6': - resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} + '@babel/plugin-transform-computed-properties@7.29.7': + resolution: {integrity: sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-destructuring@7.28.5': - resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} + '@babel/plugin-transform-destructuring@7.29.7': + resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-dotall-regex@7.28.6': - resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==} + '@babel/plugin-transform-dotall-regex@7.29.7': + resolution: {integrity: sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-duplicate-keys@7.27.1': - resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + '@babel/plugin-transform-duplicate-keys@7.29.7': + resolution: {integrity: sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0': - resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==} + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-dynamic-import@7.27.1': - resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + '@babel/plugin-transform-dynamic-import@7.29.7': + resolution: {integrity: sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-explicit-resource-management@7.28.6': - resolution: {integrity: sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==} + '@babel/plugin-transform-explicit-resource-management@7.29.7': + resolution: {integrity: sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-exponentiation-operator@7.28.6': - resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==} + '@babel/plugin-transform-exponentiation-operator@7.29.7': + resolution: {integrity: sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-export-namespace-from@7.27.1': - resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + '@babel/plugin-transform-export-namespace-from@7.29.7': + resolution: {integrity: sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-flow-strip-types@7.27.1': - resolution: {integrity: sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==} + '@babel/plugin-transform-flow-strip-types@7.29.7': + resolution: {integrity: sha512-wRHeUjUjCZnMHmiO5bRgjFLcoEh7JyTdByOW11ahhwNa4V0bmeGEaIvt51yq0zQp2yWIpqfxXXPyUP6GFJZHOQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-for-of@7.27.1': - resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + '@babel/plugin-transform-for-of@7.29.7': + resolution: {integrity: sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-function-name@7.27.1': - resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + '@babel/plugin-transform-function-name@7.29.7': + resolution: {integrity: sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-json-strings@7.28.6': - resolution: {integrity: sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==} + '@babel/plugin-transform-json-strings@7.29.7': + resolution: {integrity: sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-literals@7.27.1': - resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + '@babel/plugin-transform-literals@7.29.7': + resolution: {integrity: sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-logical-assignment-operators@7.28.6': - resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} + '@babel/plugin-transform-logical-assignment-operators@7.29.7': + resolution: {integrity: sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-member-expression-literals@7.27.1': - resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + '@babel/plugin-transform-member-expression-literals@7.29.7': + resolution: {integrity: sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-amd@7.27.1': - resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + '@babel/plugin-transform-modules-amd@7.29.7': + resolution: {integrity: sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-commonjs@7.28.6': - resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + '@babel/plugin-transform-modules-commonjs@7.29.7': + resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.29.0': - resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==} + '@babel/plugin-transform-modules-systemjs@7.29.7': + resolution: {integrity: sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-umd@7.27.1': - resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + '@babel/plugin-transform-modules-umd@7.29.7': + resolution: {integrity: sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': - resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-new-target@7.27.1': - resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + '@babel/plugin-transform-new-target@7.29.7': + resolution: {integrity: sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': - resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7': + resolution: {integrity: sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-numeric-separator@7.28.6': - resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} + '@babel/plugin-transform-numeric-separator@7.29.7': + resolution: {integrity: sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-rest-spread@7.28.6': - resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} + '@babel/plugin-transform-object-rest-spread@7.29.7': + resolution: {integrity: sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-super@7.27.1': - resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + '@babel/plugin-transform-object-super@7.29.7': + resolution: {integrity: sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-catch-binding@7.28.6': - resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==} + '@babel/plugin-transform-optional-catch-binding@7.29.7': + resolution: {integrity: sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-chaining@7.28.6': - resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + '@babel/plugin-transform-optional-chaining@7.29.7': + resolution: {integrity: sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-parameters@7.27.7': - resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + '@babel/plugin-transform-parameters@7.29.7': + resolution: {integrity: sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-methods@7.28.6': - resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} + '@babel/plugin-transform-private-methods@7.29.7': + resolution: {integrity: sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-property-in-object@7.28.6': - resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==} + '@babel/plugin-transform-private-property-in-object@7.29.7': + resolution: {integrity: sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-property-literals@7.27.1': - resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + '@babel/plugin-transform-property-literals@7.29.7': + resolution: {integrity: sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-display-name@7.28.0': - resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==} + '@babel/plugin-transform-react-display-name@7.29.7': + resolution: {integrity: sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-development@7.27.1': - resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==} + '@babel/plugin-transform-react-jsx-development@7.29.7': + resolution: {integrity: sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-self@7.27.1': - resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-source@7.27.1': - resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx@7.28.6': - resolution: {integrity: sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==} + '@babel/plugin-transform-react-jsx@7.29.7': + resolution: {integrity: sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-pure-annotations@7.27.1': - resolution: {integrity: sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==} + '@babel/plugin-transform-react-pure-annotations@7.29.7': + resolution: {integrity: sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.29.0': - resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} + '@babel/plugin-transform-regenerator@7.29.7': + resolution: {integrity: sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regexp-modifiers@7.28.6': - resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==} + '@babel/plugin-transform-regexp-modifiers@7.29.7': + resolution: {integrity: sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-reserved-words@7.27.1': - resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + '@babel/plugin-transform-reserved-words@7.29.7': + resolution: {integrity: sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-runtime@7.29.0': - resolution: {integrity: sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==} + '@babel/plugin-transform-runtime@7.29.7': + resolution: {integrity: sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-shorthand-properties@7.27.1': - resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + '@babel/plugin-transform-shorthand-properties@7.29.7': + resolution: {integrity: sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-spread@7.28.6': - resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} + '@babel/plugin-transform-spread@7.29.7': + resolution: {integrity: sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-sticky-regex@7.27.1': - resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + '@babel/plugin-transform-sticky-regex@7.29.7': + resolution: {integrity: sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-template-literals@7.27.1': - resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + '@babel/plugin-transform-template-literals@7.29.7': + resolution: {integrity: sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typeof-symbol@7.27.1': - resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + '@babel/plugin-transform-typeof-symbol@7.29.7': + resolution: {integrity: sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.28.6': - resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==} + '@babel/plugin-transform-typescript@7.29.7': + resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-escapes@7.27.1': - resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + '@babel/plugin-transform-unicode-escapes@7.29.7': + resolution: {integrity: sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-property-regex@7.28.6': - resolution: {integrity: sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==} + '@babel/plugin-transform-unicode-property-regex@7.29.7': + resolution: {integrity: sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-regex@7.27.1': - resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + '@babel/plugin-transform-unicode-regex@7.29.7': + resolution: {integrity: sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-sets-regex@7.28.6': - resolution: {integrity: sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==} + '@babel/plugin-transform-unicode-sets-regex@7.29.7': + resolution: {integrity: sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.29.2': - resolution: {integrity: sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==} + '@babel/preset-env@7.29.7': + resolution: {integrity: sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1076,32 +974,26 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - '@babel/preset-react@7.28.5': - resolution: {integrity: sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/preset-typescript@7.28.5': - resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + '@babel/preset-typescript@7.29.7': + resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.29.2': - resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': @@ -1112,15 +1004,6 @@ packages: peerDependencies: react: '>=16.3.0' - '@cd-z/epub-constructor@3.0.3': - resolution: {integrity: sha512-0Q4DBZ+H6kOihAfREu1dcfwkuwIoOtpXK24im24STP89aIb1HoAhGd3MKLKsZK3tIPVsdfjI2esPnYpmhVXxrw==} - - '@cd-z/react-native-epub-creator@3.0.0': - resolution: {integrity: sha512-vpI3viNxkSJWuSCuotFKfe1Ulg7Z+VPBTSt4GkGSGwHwpZxkNnh/UOZqyHBTtn3yvjaq2PK3GABDZlxlDhiQvA==} - peerDependencies: - react: '*' - react-native: '*' - '@drizzle-team/brocli@0.11.0': resolution: {integrity: sha512-hD3pekGiPg0WPCCGAZmusBBJsDqGUR66Y452YgQsZOnkdQ7ViEPKuyP4huUGEZQefp8g34RRodXYmJ2TbCH+tg==} @@ -1128,6 +1011,15 @@ packages: resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==} engines: {node: '>=0.8.0'} + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -1294,16 +1186,36 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@8.57.1': - resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.6': + resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@expo/cli@55.0.19': - resolution: {integrity: sha512-PPNWwPXHcLDFgNNmkLmlLm3fLiNTxr7sbhNx4mXdjo0U/2Wg3rWaCeg1yMx49llOpDLZEWJpyAwPvTBqWc8glw==} + '@expo/cli@57.0.9': + resolution: {integrity: sha512-41z9z68SynNXasZOjuT1si5Sq5OKL6SLf40ZjikbtZgDuvBO8HaUsaDzsJ0c1UZJ3N+vMzYCc1JUIDyRkVBjkA==} hasBin: true peerDependencies: expo: '*' @@ -1318,20 +1230,20 @@ packages: '@expo/code-signing-certificates@0.0.6': resolution: {integrity: sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==} - '@expo/config-plugins@55.0.7': - resolution: {integrity: sha512-XZUoDWrsHEkH3yasnDSJABM/UxP5a1ixzRwU/M+BToyn/f0nTrSJJe/Ay/FpxkI4JSNz2n0e06I23b2bleXKVA==} + '@expo/config-plugins@57.0.5': + resolution: {integrity: sha512-xhUGgzpFWRghDUH98+Wl4RDakYhTsbyMg6aOYiBjRzPO/THH8tKMw3vlksgFYlU2PkiAdABJN3tNPf5qmvOQhA==} - '@expo/config-types@55.0.5': - resolution: {integrity: sha512-sCmSUZG4mZ/ySXvfyyBdhjivz8Q539X1NondwDdYG7s3SBsk+wsgPJzYsqgAG/P9+l0xWjUD2F+kQ1cAJ6NNLg==} + '@expo/config-types@57.0.2': + resolution: {integrity: sha512-ewW08OonrcRIsRKIlFvvcmmafE5zemb1ocu3HkNwtVPyRtj2w42pZCAkMIROYpcVBaPnc3mDT9UZDzwXWC3i6g==} - '@expo/config@55.0.11': - resolution: {integrity: sha512-14AkSmR1gOIUhCsPJ0cAo5ZduMNsPQsmFV9jBNZn1xC5Zb3D8x5eqvUie5QzWaUwdcyrq79uYJ2bTCiC6+nD0Q==} + '@expo/config@57.0.5': + resolution: {integrity: sha512-XqveHQzr6PTqHGnv6NVVZ1CFgB/TgR2mKtHsJA/gYS/76pe2cP1yK/O820xGW2RTnDGTmyhOdagmK6khcN46vg==} '@expo/devcert@1.2.1': resolution: {integrity: sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==} - '@expo/devtools@55.0.2': - resolution: {integrity: sha512-4VsFn9MUriocyuhyA+ycJP3TJhUsOFHDc270l9h3LhNpXMf6wvIdGcA0QzXkZtORXmlDybWXRP2KT1k36HcQkA==} + '@expo/devtools@57.0.1': + resolution: {integrity: sha512-GyUf+wFNkbttaX0jR7MZa9bm77U0IrLg6d2AjpxdyoXw/w4abHoXG0oFufwLMgP9zLTd5+Ct4X/ffNUTnlzZgg==} peerDependencies: react: '*' react-native: '*' @@ -1341,81 +1253,88 @@ packages: react-native: optional: true - '@expo/dom-webview@55.0.3': - resolution: {integrity: sha512-bY4/rfcZ0f43DvOtMn8/kmPlmo01tex5hRoc5hKbwBwQjqWQuQt0ACwu7akR9IHI4j0WNG48eL6cZB6dZUFrzg==} + '@expo/dom-webview@57.0.1': + resolution: {integrity: sha512-lAKsME4SAq+8sf56oN0DX5TBYyruupoRxbWbD2xf9RnKY8y6x8eb9LCE5pxSN0qyWdqnp+0wmyWzDkKboThKAw==} peerDependencies: expo: '*' react: '*' react-native: '*' - '@expo/env@2.1.1': - resolution: {integrity: sha512-rVvHC4I6xlPcg+mAO09ydUi2Wjv1ZytpLmHOSzvXzBAz9mMrJggqCe4s4dubjJvi/Ino/xQCLhbaLCnTtLpikg==} + '@expo/env@2.4.2': + resolution: {integrity: sha512-28pqaEqwnmLduZ00Pq9HkSzE5wbj1MTwp5/n8nm8rD8MCjR9eUnVOwmNksPI3Be2ReAPO/DbPn1puy0mvoocsQ==} engines: {node: '>=20.12.0'} - '@expo/fingerprint@0.16.6': - resolution: {integrity: sha512-nRITNbnu3RKSHPvKVehrSU4KG2VY9V8nvULOHBw98ukHCAU4bGrU5APvcblOkX3JAap+xEHsg/mZvqlvkLInmQ==} + '@expo/expo-modules-macros-plugin@0.6.1': + resolution: {integrity: sha512-cpsLZE4rqkc1Y3eZTkxB98jrqY1YXgetmtxFt8q89jBRmk3quRuk1BZo+VcnCSObZardjg99r1k5xijEMONFGA==} + + '@expo/fingerprint@0.20.5': + resolution: {integrity: sha512-XCDfmbkTpTsYVq1xvvUJvXjfFQs2Hj+icQACBrc6BZmA91YPr2H3uw8sUX13d+1ij6E8lgVCtsK9jh3J2cN/SQ==} hasBin: true - '@expo/image-utils@0.8.12': - resolution: {integrity: sha512-3KguH7kyKqq7pNwLb9j6BBdD/bjmNwXZG/HPWT6GWIXbwrvAJt2JNyYTP5agWJ8jbbuys1yuCzmkX+TU6rmI7A==} + '@expo/image-utils@0.11.3': + resolution: {integrity: sha512-yMVjkndhXm9mct0uMq+ndxqT6FgAnhucdUfmXuQ6V6uE021GOiYCACO+KZ0MB4vearPSvbWTGfi32QQr2qocfQ==} - '@expo/json-file@10.0.12': - resolution: {integrity: sha512-inbDycp1rMAelAofg7h/mMzIe+Owx6F7pur3XdQ3EPTy00tme+4P6FWgHKUcjN8dBSrnbRNpSyh5/shzHyVCyQ==} + '@expo/inline-modules@0.1.3': + resolution: {integrity: sha512-eHSxWYfgq65mP3Qz8PclVjUkSrDIlGl3va9U7PMcTpGItOvee/i0ZzGinH5A25oARR5ouD64eESBKwtT/CwdHg==} - '@expo/local-build-cache-provider@55.0.7': - resolution: {integrity: sha512-Qg9uNZn1buv4zJUA4ZQaz+ZnKDCipRgjoEg2Gcp8Qfy+2Gq5yZKX4YN1TThCJ01LJk/pvJsCRxXlXZSwdZppgg==} + '@expo/json-file@11.0.1': + resolution: {integrity: sha512-zxHWj4MKKMAL29ZQSY/Fssx4Thluk40JmuGNaeS078wy/NhlFhnVi+rHHunulE3xJAJ0CM73m8VK2+GkF9eRwQ==} - '@expo/log-box@55.0.8': - resolution: {integrity: sha512-WVEuW1XcntUdOpQk8k9cUymM5FHKmEcPr6QO9SVIin3WYk5FbbwHRYr1T6GfwWF0UA2s9w9heeYolesq99vFIw==} + '@expo/local-build-cache-provider@57.0.4': + resolution: {integrity: sha512-B/cI73shkLSYBYuFyh+zCbS+WhqJgawWPW4MPdMiNLJKv9RmV4dv1FGjsidiIiG2k4kYKerBDLK4bLbC7qERQQ==} + + '@expo/log-box@57.0.1': + resolution: {integrity: sha512-fuVNHhOerdRWtpq27gD6JTSVYESsfRu+SMdrNCWxW+gFnusS6dGKfx3lKGBZ4ZkMNiLWn8maBHo39YKzJNXFYQ==} peerDependencies: - '@expo/dom-webview': ^55.0.3 + '@expo/dom-webview': ^57.0.1 expo: '*' react: '*' react-native: '*' - '@expo/metro-config@55.0.11': - resolution: {integrity: sha512-qGxq7RwWpj0zNvZO/e5aizKrOKYYBrVPShSbxPOVB1EXcexxTPTxnOe4pYFg/gKkLIJe0t3jSSF8IDWlGdaaOg==} + '@expo/metro-config@57.0.6': + resolution: {integrity: sha512-liXA9axM3aykAdil4qdHOYKmQqTDdXYkAoT3Eny+SQEo3btERJCbOk8VH48/G0sVbXCj82AbSS8s3XNEQNqbDQ==} peerDependencies: expo: '*' peerDependenciesMeta: expo: optional: true - '@expo/metro@54.2.0': - resolution: {integrity: sha512-h68TNZPGsk6swMmLm9nRSnE2UXm48rWwgcbtAHVMikXvbxdS41NDHHeqg1rcQ9AbznDRp6SQVC2MVpDnsRKU1w==} + '@expo/metro-file-map@57.0.1': + resolution: {integrity: sha512-8JXfVstZN7QnP4NianZZnlTVboOWR0sG8trUDNajOjnbGlPln29vponXM84tY+3tAHapz5/TxE53L0ixUwqPtA==} + + '@expo/metro@56.0.0': + resolution: {integrity: sha512-5gIgQHtEpjjvsjKfVtIv23a98LLRV0/y07PDShEwYSytAMlE3FSF8RHXqtHc1sUJL6dn7hnuIBpIbrLXXuVi0A==} - '@expo/osascript@2.4.2': - resolution: {integrity: sha512-/XP7PSYF2hzOZzqfjgkoWtllyeTN8dW3aM4P6YgKcmmPikKL5FdoyQhti4eh6RK5a5VrUXJTOlTNIpIHsfB5Iw==} + '@expo/osascript@2.7.1': + resolution: {integrity: sha512-Zn03EX6In7ts2lPUW2ESUSkEhEWQN1qqsiXjadtZMJOuZRkMiAg1ZQHuvz9DjByDWNJ2pBwAGyrts9lj9k389g==} engines: {node: '>=12'} - '@expo/package-manager@1.10.3': - resolution: {integrity: sha512-ZuXiK/9fCrIuLjPSe1VYmfp0Sa85kCMwd8QQpgyi5ufppYKRtLBg14QOgUqj8ZMbJTxE0xqzd0XR7kOs3vAK9A==} + '@expo/package-manager@1.13.1': + resolution: {integrity: sha512-y/K+CaYYpZpNGZhSX4HyLT/vyIunFjNfyoxNysPBCefeLKI/VCx6f9LNPzrxayr3rCYO5bl9O8H+HRQK265Nkg==} - '@expo/plist@0.5.2': - resolution: {integrity: sha512-o4xdVdBpe4aTl3sPMZ2u3fJH4iG1I768EIRk1xRZP+GaFI93MaR3JvoFibYqxeTmLQ1p1kNEVqylfUjezxx45g==} + '@expo/plist@0.8.1': + resolution: {integrity: sha512-3gTReGIUm0oRaMClsAJYxBnVPCl6fVpsl8HS+DTVxDhW4GyVyxg9E/Znm3BvcHtUJ51RJJI14pC1wvrNilCRHw==} - '@expo/prebuild-config@55.0.11': - resolution: {integrity: sha512-PqjbTTHXS0dnZMH4X5/0rnLxKfQqyN1s/5lmxITn+U6WDUNibatUepfjwV+5C2jU4hv5z2haqX6e9hQ0zUtDMA==} - peerDependencies: - expo: '*' + '@expo/prebuild-config@57.0.8': + resolution: {integrity: sha512-NQjRuTLvxUnggK57pKTHLtzS8YYtjrQSGYu+CjgWaO8lNlqdoBbvHrxGYtc9tBuOI3xVPaTfuIvjikxX1r+UhQ==} - '@expo/require-utils@55.0.3': - resolution: {integrity: sha512-TS1m5tW45q4zoaTlt6DwmdYHxvFTIxoLrTHKOFrIirHIqIXnHCzpceg8wumiBi+ZXSaGY2gobTbfv+WVhJY6Fw==} + '@expo/require-utils@57.0.3': + resolution: {integrity: sha512-ns05X1K8tM+Qtzp6dNloUFOopSdh3J+HC61BtOR8WHhgtPFyX8TKuO2diqZUqVg9K8yfkWug7g8tBS0qRniSTA==} peerDependencies: - typescript: ^5.0.0 || ^5.0.0-0 + typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 peerDependenciesMeta: typescript: optional: true - '@expo/router-server@55.0.11': - resolution: {integrity: sha512-Kd8J1OOlFR00DZxn+1KfiQiXZtRut6cj8+ynqHJa7dtt/lTL4tGkYistqmVhpKJ6w886eRY5WivKy7o0ZBFkJA==} + '@expo/router-server@57.0.3': + resolution: {integrity: sha512-gkboMZUv+eAK4XSBGSIQ6at3dSa/QYARm+8PKj8pMEhGnt08vgcXpWAW5rYJMoLukaDD/bUDX/JU2Iz1Azwziw==} peerDependencies: - '@expo/metro-runtime': ^55.0.6 + '@expo/metro-runtime': ^57.0.5 expo: '*' - expo-constants: ^55.0.9 - expo-font: ^55.0.4 + expo-constants: ^57.0.5 + expo-font: ^57.0.1 expo-router: '*' - expo-server: ^55.0.6 + expo-server: ^57.0.1 react: '*' react-dom: '*' react-server-dom-webpack: ~19.0.1 || ~19.1.2 || ~19.2.1 @@ -1429,35 +1348,33 @@ packages: react-server-dom-webpack: optional: true - '@expo/schema-utils@55.0.2': - resolution: {integrity: sha512-QZ5WKbJOWkCrMq0/kfhV9ry8te/OaS34YgLVpG8u9y2gix96TlpRTbxM/YATjNcUR2s4fiQmPCOxkGtog4i37g==} + '@expo/schema-utils@57.0.2': + resolution: {integrity: sha512-fMu/jyN0l1Wzv7XkeWR4IYCx1M8ryui3FdBNGrWwbRgJ7EhxXxK8E2jxP2W3pbgUwUY0V3hG8+GyfCZwny+Lxw==} '@expo/sdk-runtime-versions@1.0.0': resolution: {integrity: sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==} - '@expo/spawn-async@1.7.2': - resolution: {integrity: sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==} + '@expo/server@0.5.3': + resolution: {integrity: sha512-WXsWzeBs5v/h0PUfHyNLLz07rwwO5myQ1A5DGYewyyGLmsyl61yVCe8AgAlp1wkiMsqhj2hZqI2u3K10QnCMrQ==} + + '@expo/spawn-async@1.8.0': + resolution: {integrity: sha512-eb9xxd/LbuEGSdua4NumCu/McVB9EM+F/JxB9pWgnERw4HQ9XyTNH1KapG6oqLWR8TuRK2LQfzJlmNi94CVobw==} engines: {node: '>=12'} '@expo/sudo-prompt@9.3.2': resolution: {integrity: sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==} - '@expo/vector-icons@15.1.1': - resolution: {integrity: sha512-Iu2VkcoI5vygbtYngm7jb4ifxElNVXQYdDrYkT7UCEIiKLeWnQY0wf2ZhHZ+Wro6Sc5TaumpKUOqDRpLi5rkvw==} + '@expo/ws-tunnel@2.0.0': + resolution: {integrity: sha512-j+JfTRdCk820J9dU0sA2SqshQIKFOMo7ED84w9MJFcebfbNQgsLztEY/SABDkGnjatrW4xGqnUhVRxSBVyCkXw==} peerDependencies: - expo-font: '>=14.0.4' - react: '*' - react-native: '*' - - '@expo/ws-tunnel@1.0.6': - resolution: {integrity: sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==} + ws: ^8.0.0 - '@expo/xcpretty@4.4.1': - resolution: {integrity: sha512-KZNxZvnGCtiM2aYYZ6Wz0Ix5r47dAvpNLApFtZWnSoERzAdOMzVBOPysBoM0JlF6FKWZ8GPqgn6qt3dV/8Zlpg==} + '@expo/xcpretty@4.4.4': + resolution: {integrity: sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw==} hasBin: true - '@gorhom/bottom-sheet@5.2.8': - resolution: {integrity: sha512-+N27SMpbBxXZQ/IA2nlEV6RGxL/qSFHKfdFKcygvW+HqPG5jVNb1OqehLQsGfBP+Up42i0gW5ppI+DhpB7UCzA==} + '@gorhom/bottom-sheet@5.2.14': + resolution: {integrity: sha512-uLQFlDjp9z+jrOFcMSEldPqL5JdaXL3vXOh+juhwoNvXgTsEorJLjHTugXu+YccAG/0KJnShzKCrb71MHBsvJg==} peerDependencies: '@types/react': '*' '@types/react-native': '*' @@ -1477,24 +1394,25 @@ packages: react: '*' react-native: '*' - '@hapi/hoek@9.3.0': - resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} - '@hapi/topo@5.1.0': - resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} - '@humanwhocodes/config-array@0.13.0': - resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} '@isaacs/ttlcache@1.4.1': resolution: {integrity: sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==} @@ -1504,8 +1422,8 @@ packages: resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + '@istanbuljs/schema@0.1.6': + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} engines: {node: '>=8'} '@jest/console@29.7.0': @@ -1525,8 +1443,8 @@ packages: resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/diff-sequences@30.3.0': - resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==} + '@jest/diff-sequences@30.4.0': + resolution: {integrity: sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/environment@29.7.0': @@ -1566,8 +1484,8 @@ packages: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/schemas@30.0.5': - resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} + '@jest/schemas@30.4.1': + resolution: {integrity: sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/source-map@29.6.3': @@ -1609,120 +1527,61 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@js-joda/core@5.7.0': - resolution: {integrity: sha512-WBu4ULVVxySLLzK1Ppq+OdfP+adRS4ntmDQT915rzDJ++i95gc2jZkM5B6LWEAwN3lGXpfie3yPABozdD3K3Vg==} - '@js-temporal/polyfill@0.5.1': resolution: {integrity: sha512-hloP58zRVCRSpgDxmqCWJNlizAlUgJFqG2ypq79DCvyv9tHjRYMDOcPFjzfl/A1/YxDvRCZz8wvZvmapQnKwFQ==} engines: {node: '>=12'} - '@legendapp/list@2.0.19': - resolution: {integrity: sha512-zDWg8yg0smKxxk+M7gwAbZAnf5uczohPA+IjqLSkImz7+e9ytxeT0Mq35RBO9RTKODOXfV/aIgm1uqUHLBEdmg==} + '@legendapp/list@3.3.3': + resolution: {integrity: sha512-p3g4xG6f//s4XQKhuus2189GCQgOHEIbJXHePqeDxj+6UQQQyij4YBjyArNSCgqoP0c03sxDPSOuCFB128Ql6g==} peerDependencies: react: '*' + react-dom: '*' react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true - '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': - resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} - - '@noble/ciphers@2.1.1': - resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==} - engines: {node: '>= 20.19.0'} + '@material/material-color-utilities@0.3.0': + resolution: {integrity: sha512-ztmtTd6xwnuh2/xu+Vb01btgV8SQWYCaK56CkRK8gEkWe5TuDyBcYJ0wgkMRn+2VcE9KUmhvkz+N9GHrqw/C0g==} - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@noble/ciphers@2.2.0': + resolution: {integrity: sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA==} + engines: {node: '>= 20.19.0'} - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} - '@op-engineering/op-sqlite@15.2.9': - resolution: {integrity: sha512-Z1uHCGLox0kFWRi0joXfYNFGY3Qr5iy2UnyoX6j+vF6DL3isM3+ZwErD5SokcpWKGw46ak+wHjSkbZS7WD3R8g==} + '@op-engineering/op-sqlite@15.2.14': + resolution: {integrity: sha512-b6ZtLBL/wIosshJnP+6xsthPdNxEBSxDHbeSEApJyzeXWRfQpRWxm5e5wkFH6OUfdzDNgx3p6VnqWObFIq8xoQ==} peerDependencies: + '@sqlite.org/sqlite-wasm': '*' react: '*' react-native: '*' + peerDependenciesMeta: + '@sqlite.org/sqlite-wasm': + optional: true - '@preeternal/react-native-cookie-manager@6.3.1': - resolution: {integrity: sha512-AHFfce8zkRZKrEEIXI3rN+lT510iRAvSGTZ7w817LCr1PPTNY0MA7KPa70iUtrsxS2RfpXtIx2W3Cp5Pp01pBA==} + '@pchmn/expo-material3-theme@1.4.0': + resolution: {integrity: sha512-XNt0gpZX5argOQ7JiFnNdiXHpHjmpwJoMQrlnd1kpb8ly/IWZN2hh011incmfZXV2Xs+p6Uo1BGDKv8hW4MQTQ==} peerDependencies: + expo: '*' react: '*' react-native: '*' - '@protobufjs/aspromise@1.1.2': - resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - - '@protobufjs/base64@1.1.2': - resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - - '@protobufjs/codegen@2.0.4': - resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} - - '@protobufjs/eventemitter@1.1.0': - resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} - - '@protobufjs/fetch@1.1.0': - resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} - - '@protobufjs/float@1.0.2': - resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - - '@protobufjs/inquire@1.1.0': - resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} - - '@protobufjs/path@1.1.2': - resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - - '@protobufjs/pool@1.1.0': - resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - - '@protobufjs/utf8@1.1.0': - resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - - '@react-native-community/cli-clean@20.1.3': - resolution: {integrity: sha512-sFLdLzapfC0scjgzBJJWYDY2RhHPjuuPkA5r6q0gc/UQH/izXpMpLrhh1DW84cMDraNACK0U62tU7ebNaQ1LMQ==} - - '@react-native-community/cli-config-android@20.1.3': - resolution: {integrity: sha512-DNHDP+OWLyhKShGciBqPcxhxfp1Z/7GQcb4F+TGyCeKQAr+JdnUjRXN3X+YCU/v+g2kbYYyRJKlGabzkVvdrAw==} - - '@react-native-community/cli-config-apple@20.1.3': - resolution: {integrity: sha512-QX9B83nAfCPs0KiaYz61kAEHWr9sttooxzRzNdQwvZTwnsIpvWOT9GvMMj/19OeXiQzMJBzZX0Pgt6+spiUsDQ==} - - '@react-native-community/cli-config@20.1.3': - resolution: {integrity: sha512-n73nW0cG92oNF0r994pPqm0DjAShOm3F8LSffDYhJqNAno+h/csmv/37iL4NtSpmKIO8xqsG3uVTXz9X/hzNaQ==} - - '@react-native-community/cli-doctor@20.1.3': - resolution: {integrity: sha512-EI+mAPWn255/WZ4CQohy1I049yiaxVr41C3BeQ2BCyhxODIDR8XRsLzYb1t9MfqK/C3ZncUN2mPSRXFeKPPI1w==} - - '@react-native-community/cli-platform-android@20.1.3': - resolution: {integrity: sha512-bzB9ELPOISuqgtDZXFPQlkuxx1YFkNx3cNgslc5ElCrk+5LeCLQLIBh/dmIuK8rwUrPcrramjeBj++Noc+TaAA==} - - '@react-native-community/cli-platform-apple@20.1.3': - resolution: {integrity: sha512-XJ+DqAD4hkplWVXK5AMgN7pP9+4yRSe5KfZ/b42+ofkDBI55ALlUmX+9HWE3fMuRjcotTCoNZqX2ov97cFDXpQ==} - - '@react-native-community/cli-platform-ios@20.1.3': - resolution: {integrity: sha512-2qL48SINotuHbZO73cgqSwqd/OWNx0xTbFSdujhpogV4p8BNwYYypfjh4vJY5qJEB5PxuoVkMXT+aCADpg9nBg==} - - '@react-native-community/cli-server-api@20.1.3': - resolution: {integrity: sha512-hsNsdUKZDd2T99OuNuiXz4VuvLa1UN0zcxefmPjXQgI0byrBLzzDr+o7p03sKuODSzKi2h+BMnUxiS07HACQLA==} - - '@react-native-community/cli-tools@20.1.3': - resolution: {integrity: sha512-EAn0vPCMxtHhfWk2UwLmSUfPfLUnFgC7NjiVJVTKJyVk5qGnkPfoT8te/1IUXFTysUB0F0RIi+NgDB4usFOLeA==} - - '@react-native-community/cli-types@20.1.3': - resolution: {integrity: sha512-IdAcegf0pH1hVraxWTG1ACLkYC0LDQfqtaEf42ESyLIF3Xap70JzL/9tAlxw7lSCPZPFWhrcgU0TBc4SkC/ecw==} - - '@react-native-community/cli@20.1.3': - resolution: {integrity: sha512-sLo8cu9JyFNfuuF1C+8NJ4DHE/PEFaXGd4enkcxi/OJjGG8+sOQrdjNQ4i+cVh/2c+ah1mEMwsYjc3z0+/MqSg==} - engines: {node: '>=20.19.4'} - hasBin: true - - '@react-native-community/slider@5.1.2': - resolution: {integrity: sha512-UV/MjCyCtSjS5BQDrrGIMmCXm309xEG6XbR0Dj65kzTraJSVDxSjQS2uBUXgX+5SZUOCzCxzv3OufOZBdtQY4w==} + '@preeternal/react-native-cookie-manager@6.3.3': + resolution: {integrity: sha512-ddA3H7nbV0n/EdJ3ZXDd9GpT9jzhBiMrVeXftJf1mJ1gqyQnZ6d8EEd+k/lnjzfhqK6Z2kvXggCSpwAH991Gvw==} + peerDependencies: + react: '*' + react-native: '*' '@react-native-documents/picker@12.0.1': resolution: {integrity: sha512-vpJKb4t/5bnxe9+gQl+plJfKrrIsmYwANGhNH2B9E1dS1+6FDBzg4Dwmcq4ueaGfkRKEPJ606mJttVEH1ZKZaA==} @@ -1740,13 +1599,14 @@ packages: expo: optional: true - '@react-native-vector-icons/common@13.0.0': - resolution: {integrity: sha512-FJ0Ql5UTGVtK0ak4vLTxmhFHadb8NmTk4yOWoggh7UvC2pVQNyJK7L9nIZeIZ0IaVJtKfmKXtBWA0nKqqzQ/FQ==} + '@react-native-vector-icons/common@13.0.1': + resolution: {integrity: sha512-UPC6L3tW5rXCjBn4kgw9RPURUILIg8tFpEY2uaYwU8aCjEHkywNCMcAO8+PvMCDkR6aICPeHYA0OXvMgrjsF4g==} engines: {node: '>=20.19.0 <21.0.0 || >=22.0.0'} hasBin: true peerDependencies: '@react-native-vector-icons/get-image': ^13.0.0 '@react-native/assets-registry': '*' + expo-font: '*' react: '*' react-native: '*' peerDependenciesMeta: @@ -1754,123 +1614,121 @@ packages: optional: true '@react-native/assets-registry': optional: true + expo-font: + optional: true - '@react-native-vector-icons/material-design-icons@13.0.0': - resolution: {integrity: sha512-TNZDhQX20eWsFDMqIbW1+Hmfv/eY36vXBphxa+viIm2YcvCuBF969x+311zCVC0y5OTH4pHARV8Php8sfCEXWA==} + '@react-native-vector-icons/material-design-icons@13.1.2': + resolution: {integrity: sha512-Qc8IQCxbnHOk8CvTAb+dLzYgRMbJOLiZ8Up7TRsNixY6EqwPx9/W3DeK5niKtNQ4dIfbALeYz41yyvDM7w7mag==} engines: {node: '>= 18.0.0'} peerDependencies: + '@expo/config-plugins': '>=10.0.0' react: '*' react-native: '*' + peerDependenciesMeta: + '@expo/config-plugins': + optional: true - '@react-native/assets-registry@0.83.4': - resolution: {integrity: sha512-aqKtpbJDSQeSX/Dwv0yMe1/Rd2QfXi12lnyZDXNn/OEKz59u6+LuPBVgO/9CRyclHmdlvwg8c7PJ9eX2ZMnjWg==} - engines: {node: '>= 20.19.4'} + '@react-native/assets-registry@0.86.0': + resolution: {integrity: sha512-nIaXbm2jX1OTYp0qbviJ3O6KZivoE8z3BnhUQ2LsqfZSWRoOK/n1qsiAr6oALiNKWnXY3j2KPwtYORnZzp8xew==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/babel-plugin-codegen@0.83.4': - resolution: {integrity: sha512-UFsK+c1rvT84XZfzpmwKePsc5nTr5LK7hh18TI0DooNlVcztDbMDsQZpDnhO/gmk7aTbWEqO5AB3HJ7tvGp+Jg==} - engines: {node: '>= 20.19.4'} + '@react-native/babel-plugin-codegen@0.86.0': + resolution: {integrity: sha512-qdsABWNW7uTll90l4Vh03gjeyu3WVDi2CyiiyvYGMRDcoYbjbQi6df3BMAm9lQI2yslZ1T14LlDDAsgTwNxplA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/babel-preset@0.83.4': - resolution: {integrity: sha512-SXPFn3Jp4gOzlBDnDOKPzMfxQPKJMYJs05EmEeFB/6km46xZ9l+2YKXwAwxfNhHnmwNf98U/bnVndU95I0TMCw==} - engines: {node: '>= 20.19.4'} + '@react-native/babel-preset@0.86.0': + resolution: {integrity: sha512-bYQcWiPySNvF4dns9Ls9gMmwgq66ohvM9Fwc/Kn8r85t66UNHxch3p1QwPiSorDelFauZwJbgo9+ReibTgvpbA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@babel/core': '*' - '@react-native/codegen@0.83.4': - resolution: {integrity: sha512-CJ7XutzIqJPz3Lp/5TOiRWlU/JAjTboMT1BHNLSXjYHXwTmgHM3iGEbpCOtBMjWvsojRTJyRO/G3ghInIIXEYg==} - engines: {node: '>= 20.19.4'} + '@react-native/codegen@0.86.0': + resolution: {integrity: sha512-uTs9DBo3+/lUqinsGZK0FKJRBVClrwMXoZToaDxE1Q2SL2e55vs2GwyZfIKzPl5uJnbu4PfFMIp0/mLXLWUMuA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@babel/core': '*' - '@react-native/community-cli-plugin@0.83.4': - resolution: {integrity: sha512-8os0weQEnjUhWy7Db881+JKRwNHVGM40VtTRvltAyA/YYkrGg4kPCqiTybMxQDEcF3rnviuxHyI+ITiglfmgmQ==} - engines: {node: '>= 20.19.4'} + '@react-native/community-cli-plugin@0.86.0': + resolution: {integrity: sha512-Jv8p1ebEPfTzs8gmrjsdT2XMXFfeAg45Pman+XPLFGaSeGAZkutRFRyX9Cs9aGTSOyIA9YPJ6vDNb1ayTf1FKQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@react-native-community/cli': '*' - '@react-native/metro-config': '*' + '@react-native/metro-config': 0.86.0 peerDependenciesMeta: '@react-native-community/cli': optional: true '@react-native/metro-config': optional: true - '@react-native/debugger-frontend@0.83.4': - resolution: {integrity: sha512-mCE2s/S7SEjax3gZb6LFAraAI3x13gRVWJWqT0HIm71e4ITObENNTDuMw4mvZ/wr4Gz2wv4FcBH5/Nla9LXOcg==} - engines: {node: '>= 20.19.4'} - - '@react-native/debugger-shell@0.83.4': - resolution: {integrity: sha512-FtAnrvXqy1xeZ+onwilvxEeeBsvBlhtfrHVIC2R/BOJAK9TbKEtFfjio0wsn3DQIm+UZq48DSa+p9jJZ2aJUww==} - engines: {node: '>= 20.19.4'} + '@react-native/debugger-frontend@0.86.0': + resolution: {integrity: sha512-7Mb3nDfyJeys+ELF75Ageu7VKERlnIMoO+aNPoXqTXvz+b41L6l2CqMyLpDHxkBSlenij6gEepPNgaIyWHbJZw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/dev-middleware@0.83.4': - resolution: {integrity: sha512-3s9nXZc/kj986nI2RPqxiIJeTS3o7pvZDxbHu7GE9WVIGX9YucA1l/tEiXd7BAm3TBFOfefDOT08xD46wH+R3Q==} - engines: {node: '>= 20.19.4'} + '@react-native/debugger-shell@0.86.0': + resolution: {integrity: sha512-Y0zEkZzLz8ou6o/VLml1A31X/rMgc6DRjwxwzPMa94qRTMY070WeBCNTITQo4kKTBAUgbxh07oXPQqp0Tpja8w==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/eslint-config@0.83.4': - resolution: {integrity: sha512-IhHBeXZqa2QBJc+dAhCVEtbwCCT6U0S6lDYbbZNW2jxsivZGt9I+gh270kaDtg7pHWVrYj+7b7DQY3hxHC/yhg==} - engines: {node: '>= 20.19.4'} - peerDependencies: - eslint: '>=8' - prettier: '>=2' + '@react-native/dev-middleware@0.86.0': + resolution: {integrity: sha512-20pTO6yTybmvXvro520H6C7jydIQnLKOl5qFtVEcHSdFrY63r3OGei+Rx9bILgSRmH6jgnfEcijcMx7pwWuQtw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/eslint-plugin@0.83.4': - resolution: {integrity: sha512-2wagFcwYy6iISsWuWXhwomoXRqMofwwj4XmjhPsNsF91OHBUNkxGANDfz7aIJyjmEvEKQlYR3SEQ+TRlRqyoMg==} - engines: {node: '>= 20.19.4'} + '@react-native/gradle-plugin@0.86.0': + resolution: {integrity: sha512-a1RcfaEDqWExCGfCwadIxt4l8FvKYgFqeMf2uzeKyAOnb+vTGNIeCvifFL2MqvgaeYxlER437HbMIajGcuJ1pQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/gradle-plugin@0.83.4': - resolution: {integrity: sha512-AhaSWw2k3eMKqZ21IUdM7rpyTYOpAfsBbIIiom1QQii3QccX0uW2AWTcRhfuWRxqr2faGFaOBYedWl2fzp5hgw==} + '@react-native/jest-preset@0.86.0': + resolution: {integrity: sha512-KA+xpIP3DvJy7PQJ9c6ZdEKkOPChl+Rk/rV2MhQACEAzfhWU84407KZQv4ccyO3B4caD0gPrFjE96a4P993nsQ==} engines: {node: '>= 20.19.4'} + peerDependencies: + react: ^19.2.3 - '@react-native/js-polyfills@0.83.4': - resolution: {integrity: sha512-wYUdv0rt4MjhKhQloO1AnGDXhZQOFZHDxm86dEtEA0WcsCdVrFdRULFM+rKUC/QQtJW2rS6WBqtBusgtrsDADg==} - engines: {node: '>= 20.19.4'} + '@react-native/js-polyfills@0.86.0': + resolution: {integrity: sha512-zYy/Cjd1VTnZ2iCNaG9bDF9C3l2ntESiPRscjIlI5FKugu6aeTwsDSv1aI8Bc4Kp3vEdoVg+UQhLAhE4svREaQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/metro-babel-transformer@0.83.4': - resolution: {integrity: sha512-gbqn9OmTou3TykLIbZHC2lw/tjdIPr/6KH8Uz4TAPf5f0yZuWoYtQrJ/UBJ+KVbJC5/A2vN8BXkwWXVz90hJIw==} - engines: {node: '>= 20.19.4'} + '@react-native/metro-babel-transformer@0.86.0': + resolution: {integrity: sha512-SjKej3E5qIahqo/G+rSOrmJUQM44RyKtWtO+VfmKAAMoJWkBFomM22hTLKCIS5cdbIAJ9COAmU+KAi2wVSO0wQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@babel/core': '*' - '@react-native/metro-config@0.83.4': - resolution: {integrity: sha512-uYAJLDj1xWm05grHhv3swEHPsygzrl+xIbQ2b84sh9jU0LFSTgxSGv5CGgwee+F3j637GQBEoRP0ymyLqjXUIA==} - engines: {node: '>= 20.19.4'} - - '@react-native/normalize-colors@0.83.4': - resolution: {integrity: sha512-9ezxaHjxqTkTOLg62SGg7YhFaE+fxa/jlrWP0nwf7eGFHlGOiTAaRR2KUfiN3K05e+EMbEhgcH/c7bgaXeGyJw==} + '@react-native/metro-config@0.86.0': + resolution: {integrity: sha512-7v+xbTeEci9ZcQ/Z1OqI4RXcqN69wSMDYL5BAMvOReZ7U04+aDQ0/SQhClYPn6x2/RxM4WzMKSAuNyLKqvYVtw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/typescript-config@0.83.4': - resolution: {integrity: sha512-no0qYsvmdquZtgfaJNUKCBLEczgMcipg1fLjV64sxWSPle/8xm1WdW1geywhg5ZPxiiLCjbrFf4/1GG9DHnkQA==} + '@react-native/normalize-colors@0.86.0': + resolution: {integrity: sha512-kG0wfCGghUKlfxkJyyHCDVutWVYWK7/DG58ojA/4v9EfulgF+osuSQmlbNb3rcKX58qutm7JcldSeVLgGFha9g==} - '@react-native/virtualized-lists@0.83.4': - resolution: {integrity: sha512-vNF/8kokMW8JEjG4n+j7veLTjHRRABlt4CaTS6+wtqzvWxCJHNIC8fhCqrDPn9fIn8sNePd8DyiFVX5L9TBBRA==} - engines: {node: '>= 20.19.4'} + '@react-native/virtualized-lists@0.86.0': + resolution: {integrity: sha512-4/ZLXdf/OSpPDVO0AsQ1SJdRIzt5t9BNQ46QwGgxvX7/cirYR5k8KXctNGGgW8lQo2gZChEfY2zFCZg9nM/jiw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@types/react': ^19.2.0 react: '*' - react-native: '*' + react-native: 0.86.0 peerDependenciesMeta: '@types/react': optional: true - '@react-navigation/bottom-tabs@7.15.9': - resolution: {integrity: sha512-Ou28A1aZLj5wiFQ3F93aIsrI4NCwn3IJzkkjNo9KLFXsc0Yks+UqrVaFlffHFLsrbajuGRG/OQpnMA1ljayY5Q==} + '@react-navigation/bottom-tabs@7.18.11': + resolution: {integrity: sha512-LlTj0w16sswqL3/I7HJV1FRV4BDDlO50/7JXgRSwgntxIYwXO4W6Z5UohMo/+fi8mEkvPulzMJg9WE/oubdqEg==} peerDependencies: - '@react-navigation/native': ^7.2.2 + '@react-navigation/native': ^7.3.11 react: '>= 18.2.0' react-native: '*' react-native-safe-area-context: '>= 4.0.0' react-native-screens: '>= 4.0.0' - '@react-navigation/core@7.17.2': - resolution: {integrity: sha512-Rt2OZwcgOmjv401uLGAKaRM6xo0fiBce/A7LfRHI1oe5FV+KooWcgAoZ2XOtgKj6UzVMuQWt3b2e6rxo/mDJRA==} + '@react-navigation/core@7.21.8': + resolution: {integrity: sha512-jsCLOIgB3eYv7KQsdSpyW2mC0wfInWYAiFVkPn3ZUQJ1FgjwbqY2//JaCWgTRGKBj3uDy36z5SeYx6WIYpweEw==} peerDependencies: react: '>= 18.2.0' - '@react-navigation/elements@2.9.14': - resolution: {integrity: sha512-lKqzu+su2pI/YIZmR7L7xdOs4UL+rVXKJAMpRMBrwInEy96SjIFst6QDGpE89Dunnu3VjVpjWfByo9f2GWBHDQ==} + '@react-navigation/elements@2.9.33': + resolution: {integrity: sha512-tQvZo1pakH6O9wkh5fWD8KlsTCG4eYxp2HD5Cjr9xjpTjqsnS13/BQV21Cu6fcKVCQ89SY6w+0+uXeKc3CzAVg==} peerDependencies: '@react-native-masked-view/masked-view': '>= 0.2.0' - '@react-navigation/native': ^7.2.2 + '@react-navigation/native': ^7.3.11 react: '>= 18.2.0' react-native: '*' react-native-safe-area-context: '>= 4.0.0' @@ -1878,48 +1736,96 @@ packages: '@react-native-masked-view/masked-view': optional: true - '@react-navigation/native-stack@7.14.10': - resolution: {integrity: sha512-mCbYbYhi7Em2R2nEgwYGdLU38smy+KK+HMMVcwuzllWsF3Qb+jOUEYbB6Or7LvE7SS77BZ6sHdx4HptCEv50hQ==} + '@react-navigation/native-stack@7.18.3': + resolution: {integrity: sha512-UhhDEMgc9Wx9u0LFvjynHNozf9f/ZhcCviq7fqMiNnUKvKcFzz3oTcORirFuLtsk8olG3ypEJtmDQAa6vw5uiw==} peerDependencies: - '@react-navigation/native': ^7.2.2 + '@react-navigation/native': ^7.3.11 react: '>= 18.2.0' react-native: '*' react-native-safe-area-context: '>= 4.0.0' react-native-screens: '>= 4.0.0' - '@react-navigation/native@7.2.2': - resolution: {integrity: sha512-kem1Ko2BcbAjmbQIv66dNmr6EtfDut3QU0qjsVhMnLLhktwyXb6FzZYp8gTrUb6AvkAbaJoi+BF5Pl55pAUa5w==} + '@react-navigation/native@7.3.11': + resolution: {integrity: sha512-CWg3lLOVYwnbjPdIzJWrpqSF6F2+1F6QMUvQfLFA6MizwOTUrkZ1w1q7a7m0Pis5ful+EFpoPqHYuLoQ5XmoCw==} peerDependencies: react: '>= 18.2.0' react-native: '*' - '@react-navigation/routers@7.5.3': - resolution: {integrity: sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==} + '@react-navigation/routers@7.6.2': + resolution: {integrity: sha512-cYzWE/+kcX6RAaX1/peL07f4aIpxeE7uNDr89qJpfGuVz3WHs9dGE1QwbCyCK95fVYn1tvsOmbXbvR0o43uqKQ==} - '@react-navigation/stack@7.8.9': - resolution: {integrity: sha512-lxw+kIExNchrIIXBUVfPsqsmcuoLhdVWhkKy9l1Ns2gu0JAz80lKri3ftsBbDXaE9c0LgSq5kaoxq+AkD/c3cg==} + '@react-navigation/stack@7.10.14': + resolution: {integrity: sha512-RQy2uK/iq/5CoSqiBfV69GzQuWzqJZJXdG5WQk7y1ROrSQkEHWojnADsQREZsCafJXu9vl0C1ygq+f5Fu4Zf7A==} peerDependencies: - '@react-navigation/native': ^7.2.2 + '@react-navigation/native': ^7.3.11 react: '>= 18.2.0' react-native: '*' react-native-gesture-handler: '>= 2.0.0' react-native-safe-area-context: '>= 4.0.0' react-native-screens: '>= 4.0.0' - '@sideway/address@4.1.5': - resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} + '@rozenite/agent-bridge@1.13.0': + resolution: {integrity: sha512-rFlhAzCOY1GJZi7zuYgG6N2zirnwCqruVlfofpeFGKClq1Z14GZT5V3UbKYdrpZBOqqDbLEIT4UkPoz7Bfaclg==} + engines: {node: '>=20'} + peerDependencies: + react: '>=17' + + '@rozenite/agent-shared@1.13.0': + resolution: {integrity: sha512-CF5JWUBTpAtTJ1oPZ2lQgT46JcmrzoaN5dL+G92SOPgVsgBinBVCXcY38MGC7mZ/7iM/fccKQPHvbFSric4wjQ==} + engines: {node: '>=20'} + + '@rozenite/expo-atlas-plugin@1.13.0': + resolution: {integrity: sha512-bpER3owQM/7b4JF/n7EUjUA3DuFv38AhUtARqefOMxMwkWt6LPDKpPDLOe8l7jxz1LheOWaAtlvbIN6L/abNxg==} + peerDependencies: + react: '*' + react-native: '*' + + '@rozenite/metro@1.13.0': + resolution: {integrity: sha512-Qlyl/SPWgAf8m9EBubDF8fsuGXIy1bMDS/plmomlJ/Vctuihw9djBGE+Aim5W6nD9YWbcTTfIiLKpLYzgwVzjw==} + engines: {node: '>=20'} + + '@rozenite/middleware@1.13.0': + resolution: {integrity: sha512-3xgfeTkL/h3653Ewl566vHCnu16gA7fGFnS+Z6Jk2scsDYZru5gBoPETzgEPBJ3vMMRDjYcQfpkQCHLyvINiSg==} + engines: {node: '>=20'} + + '@rozenite/plugin-bridge@1.13.0': + resolution: {integrity: sha512-wLctFXRDL2v4SS8aaBNSsqReLmlqvWly+0F5ZlaJ2jIPdsxjV6HdJXCKhoN90IyidDVH5IVfdhnR1I9RJw9vHw==} + engines: {node: '>=20'} + peerDependencies: + react: '>=17' + + '@rozenite/runtime@1.13.0': + resolution: {integrity: sha512-3U+63lZtNW+D+JRETINZTvD0bYjXDRJd818bSQlNOJNAZ6AQTBShNM+4enYjmJLJApzkfxhLUjVDQ+AwkvlQ2A==} + engines: {node: '>=20'} + + '@rozenite/sqlite-plugin@1.13.0': + resolution: {integrity: sha512-ET1ywf3S0vI1AJdxXvO9YTwWcJqeAmIn5KZgqkF3G1t4KRb8gWdnHotcRCGPEkbrdWrgMD9yXXuN3J6hZA9A7w==} + peerDependencies: + expo-sqlite: '*' + react: '*' + react-native: '*' + peerDependenciesMeta: + expo-sqlite: + optional: true - '@sideway/formula@3.0.1': - resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==} + '@rozenite/tools@1.13.0': + resolution: {integrity: sha512-fZnEEdyQQNABeCUNAndfdcDRzcXSoojj+USwHMbkCaM7BClPGdurKE/7kewGhYlp901cpahDOGtRF+eXEZ6UIQ==} - '@sideway/pinpoint@2.0.0': - resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@shopify/flash-list@2.3.2': + resolution: {integrity: sha512-vQnd0y0Ag11yzS30llaeCrtIU3xYTMe7KEOP3dveLImnVjQEUw6BEg1+Ztja6aODlVNz1BpvxtlQ5eZGxiLwKw==} + peerDependencies: + '@babel/runtime': '*' + react: '*' + react-native: '*' - '@sinclair/typebox@0.27.10': - resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} + '@sinclair/typebox@0.27.12': + resolution: {integrity: sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==} - '@sinclair/typebox@0.34.49': - resolution: {integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==} + '@sinclair/typebox@0.34.52': + resolution: {integrity: sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==} '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -1927,9 +1833,6 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@tediousjs/connection-string@0.5.0': - resolution: {integrity: sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==} - '@testing-library/react-native@13.3.3': resolution: {integrity: sha512-k6Mjsd9dbZgvY4Bl7P1NIpePQNi+dfYtlJ5voi9KQlynxSyQkfOgJmYGCYmw/aSgH/rUcFvG8u5gd4npzgRDyg==} engines: {node: '>=18'} @@ -1942,10 +1845,16 @@ packages: jest: optional: true - '@tootallnate/once@2.0.0': - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + '@tootallnate/once@2.0.1': + resolution: {integrity: sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==} engines: {node: '>= 10'} + '@ts-morph/common@0.29.0': + resolution: {integrity: sha512-35oUmphHbJvQ/+UTwFNme/t2p3FoKiGJ5auTjjpNTop2dyREspirjMy82PLSC1pnDJ8ah1GU98hwpVt64YXQsg==} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1970,6 +1879,9 @@ packages: '@types/color@4.2.1': resolution: {integrity: sha512-ResWeDLy1vozIMbD6JLRKuNBbIcIlBkjTIxVHHd5Cqtm77T+ahH3BWE/PWv1OhFd1HAwcn8no4ig2uTaRXpYQQ==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} @@ -1991,23 +1903,26 @@ packages: '@types/jsdom@20.0.1': resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} '@types/lodash@4.17.24': resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} - '@types/mssql@9.1.9': - resolution: {integrity: sha512-P0nCgw6vzY23UxZMnbI4N7fnLGANt4LI4yvxze1paPj+LuN28cFv5EI+QidP8udnId/BKhkcRhm/BleNsjK65A==} + '@types/node@26.1.1': + resolution: {integrity: sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==} - '@types/node@25.5.0': - resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} + '@types/react-test-renderer@19.1.0': + resolution: {integrity: sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ==} - '@types/react@19.2.14': - resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} - - '@types/readable-stream@4.0.23': - resolution: {integrity: sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==} + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} '@types/sanitize-html@2.16.1': resolution: {integrity: sha512-n9wjs8bCOTyN/ynwD8s/nTcTreIHB1vf31vhLMGqUPNHaweKC4/fAl4Dj+hUlCTKYgm4P3k83fmiFfzkZ6sgMA==} @@ -2024,142 +1939,308 @@ packages: '@types/yargs@17.0.35': resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - '@typescript-eslint/eslint-plugin@8.58.0': - resolution: {integrity: sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==} + '@typescript-eslint/eslint-plugin@8.64.0': + resolution: {integrity: sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.58.0 + '@typescript-eslint/parser': ^8.64.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.58.0': - resolution: {integrity: sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==} + '@typescript-eslint/parser@8.64.0': + resolution: {integrity: sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.58.0': - resolution: {integrity: sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==} + '@typescript-eslint/project-service@8.64.0': + resolution: {integrity: sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.58.0': - resolution: {integrity: sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==} + '@typescript-eslint/scope-manager@8.64.0': + resolution: {integrity: sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.58.0': - resolution: {integrity: sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==} + '@typescript-eslint/tsconfig-utils@8.64.0': + resolution: {integrity: sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.58.0': - resolution: {integrity: sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==} + '@typescript-eslint/type-utils@8.64.0': + resolution: {integrity: sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.58.0': - resolution: {integrity: sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==} + '@typescript-eslint/types@8.64.0': + resolution: {integrity: sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.58.0': - resolution: {integrity: sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==} + '@typescript-eslint/typescript-estree@8.64.0': + resolution: {integrity: sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.58.0': - resolution: {integrity: sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==} + '@typescript-eslint/utils@8.64.0': + resolution: {integrity: sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.58.0': - resolution: {integrity: sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==} + '@typescript-eslint/visitor-keys@8.64.0': + resolution: {integrity: sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typespec/ts-http-runtime@0.3.4': - resolution: {integrity: sha512-CI0NhTrz4EBaa0U+HaaUZrJhPoso8sG7ZFya8uQoBA57fjzrjRSv87ekCjLZOFExN+gXE/z0xuN2QfH4H2HrLQ==} - engines: {node: '>=20.0.0'} + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260707.2': + resolution: {integrity: sha512-wny2pgKjGbiZtnOIHVa3tXC1UfDqxNEFzyPGmiqybedG8hipG2Nfp0l5UxbaKCjkLacUpH/W5bP2hBOMVhCOzg==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@typescript/native-preview-darwin-x64@7.0.0-dev.20260707.2': + resolution: {integrity: sha512-Afc7M5zOwo+GpfcYwz5Z8HMB2tPVsui7nNIqEuuFB73MPdVqNn/Wmpe4tP4MRri0AtJnJknoHBaTJ/VDAp/Jhw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] - '@vscode/sudo-prompt@9.3.2': - resolution: {integrity: sha512-gcXoCN00METUNFeQOFJ+C9xUI0DKB+0EGMVg7wbVYRHBw2Eq3fKisDZOkRdOz3kqXRKOENMfShPOmypw1/8nOw==} + '@typescript/native-preview-linux-arm64@7.0.0-dev.20260707.2': + resolution: {integrity: sha512-iITBa2WjjTI5N9t5l7Z4KoOSI+2zBlhbvFzsD/f8qX8QoKjz/Y4DPyBDgezYi8nkqjjksbgSOJ3/ykzhwrB9cg==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] - '@xmldom/xmldom@0.8.12': - resolution: {integrity: sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==} - engines: {node: '>=10.0.0'} + '@typescript/native-preview-linux-arm@7.0.0-dev.20260707.2': + resolution: {integrity: sha512-hJm/UOqZTr9FHmR7uNm8VGX4oKtfWk0Jem0zPeJFNC8ckGUfSBueyiEYMZB+XmRc1aG4x1E46y3CplP4CLHvGQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] - abab@2.0.6: - resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} - deprecated: Use your platform's native atob() and btoa() methods instead + '@typescript/native-preview-linux-x64@7.0.0-dev.20260707.2': + resolution: {integrity: sha512-du0dzi6y97Po5vDNdPJTyyijHCpaS22JLRnKZEJXBDaO9gCIymOv/5QQokFRuOlQm0bWl3i9PF4OVdGP6uAOQA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} + '@typescript/native-preview-win32-arm64@7.0.0-dev.20260707.2': + resolution: {integrity: sha512-SsAwfhyHJ1akgBc+99z4+hwdbHsdWaKB8EwCNIMA6JfSLMeUjffrYvxu+vfMyxVtOVOz7RrRXRoiDiu4a2sCtg==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] - accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} + '@typescript/native-preview-win32-x64@7.0.0-dev.20260707.2': + resolution: {integrity: sha512-DL4u27stv0fo71sVhOzHSwE+YMZsbBijVI+kg5dLDLilSH79WFTJ8RSQ46vJrCMt+Gjlv/JOZP1PuLJDfioYeQ==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} + '@typescript/native-preview@7.0.0-dev.20260707.2': + resolution: {integrity: sha512-oUGp+Rep/hqMhPunyinsALUwSlzHINSxitifPiSaeqoKOKD2OlR9NE3TaPqwsl4NlGslsOSUXI1JotWQzpYCPg==} + engines: {node: '>=16.20.0'} + hasBin: true - acorn-globals@7.0.1: - resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + '@ungap/structured-clone@1.3.3': + resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} + cpu: [arm] + os: [android] - acorn-walk@8.3.5: - resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} - engines: {node: '>=0.4.0'} + '@unrs/resolver-binding-android-arm64@1.12.2': + resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==} + cpu: [arm64] + os: [android] - acorn@8.16.0: - resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} - engines: {node: '>=0.4.0'} - hasBin: true + '@unrs/resolver-binding-darwin-arm64@1.12.2': + resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==} + cpu: [arm64] + os: [darwin] - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} + '@unrs/resolver-binding-darwin-x64@1.12.2': + resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==} + cpu: [x64] + os: [darwin] - agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} + '@unrs/resolver-binding-freebsd-x64@1.12.2': + resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==} + cpu: [x64] + os: [freebsd] - aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==} + cpu: [arm] + os: [linux] - ajv@6.14.0: - resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==} + cpu: [arm] + os: [linux] - anser@1.4.10: - resolution: {integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==} + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} + cpu: [arm64] + os: [linux] + libc: [glibc] - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} + cpu: [arm64] + os: [linux] + libc: [musl] - ansi-escapes@6.2.1: - resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==} - engines: {node: '>=14.16'} + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} + cpu: [loong64] + os: [linux] + libc: [glibc] - ansi-fragments@0.2.1: - resolution: {integrity: sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==} + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} + cpu: [loong64] + os: [linux] + libc: [musl] - ansi-regex@4.1.1: + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} + cpu: [arm64] + os: [openharmony] + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==} + cpu: [x64] + os: [win32] + + '@xmldom/xmldom@0.8.13': + resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==} + engines: {node: '>=10.0.0'} + + '@xmldom/xmldom@0.9.10': + resolution: {integrity: sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==} + engines: {node: '>=14.6'} + + abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-globals@7.0.1: + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + engines: {node: '>=0.4.0'} + + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + agent-cli-detector@0.1.3: + resolution: {integrity: sha512-XjBe6lT5sK9xhAn0ToFvdwicsaGvzZkax1iviPnL8sFFYHoagnTTn2E4YTFiyM956iqkOkFcgcWCiER0kkShbQ==} + engines: {node: '>=18.18'} + hasBin: true + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + anser@1.4.10: + resolution: {integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-escapes@6.2.1: + resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==} + engines: {node: '>=14.16'} + + ansi-regex@4.1.1: resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} engines: {node: '>=6'} @@ -2191,9 +2272,6 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - appdirsjs@1.2.7: - resolution: {integrity: sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==} - arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -2207,6 +2285,9 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + array-includes@3.1.9: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} @@ -2215,6 +2296,10 @@ packages: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + array.prototype.flat@1.3.3: resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} engines: {node: '>= 0.4'} @@ -2234,10 +2319,6 @@ packages: asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - astral-regex@1.0.0: - resolution: {integrity: sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==} - engines: {node: '>=4'} - astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} @@ -2246,9 +2327,6 @@ packages: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} - async-limiter@1.0.1: - resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -2256,9 +2334,6 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - await-lock@2.2.2: - resolution: {integrity: sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==} - babel-jest@29.7.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2305,11 +2380,11 @@ packages: babel-plugin-react-native-web@0.21.2: resolution: {integrity: sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==} - babel-plugin-syntax-hermes-parser@0.32.0: - resolution: {integrity: sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg==} + babel-plugin-syntax-hermes-parser@0.36.0: + resolution: {integrity: sha512-LhD0xdoedDw7ansQgXbB2DADLZIK/LRXuWNBPuVzMc5S2WK5GyT89tCM+cQzxFGO0mGyLK6D5TrVOJJzAoDy8Q==} - babel-plugin-syntax-hermes-parser@0.32.1: - resolution: {integrity: sha512-HgErPZTghW76Rkq9uqn5ESeiD97FbqpZ1V170T1RG2RDp+7pJVQV2pQJs7y5YzN0/gcT6GM5ci9apRnIwuyPdQ==} + babel-plugin-syntax-hermes-parser@0.36.1: + resolution: {integrity: sha512-ycduwJbvdvIMmVvlAZqGggS+pm5Eu4Bk9pcV9Sm2Z4PJNRVsKkv0g7vHj+LeuC1gHTeF67sJXFOq61IlqCa2hA==} babel-plugin-transform-flow-enums@0.0.2: resolution: {integrity: sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==} @@ -2319,12 +2394,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 || ^8.0.0-0 - babel-preset-expo@55.0.13: - resolution: {integrity: sha512-7m3Hpi6R1M+3u2LEU15OV59ATtbqz6kFvL6y9TaZTeOGLV28MFULawCQw3BtO/qMYUPz0vkH1OdbCuG7E2cTbg==} + babel-preset-expo@57.0.3: + resolution: {integrity: sha512-JuTLwC4dt30GF3L8sY7EBwh5iD7L3dSWumfg+i99bFK2SIXWyfn01UHxu+azfKXFU/ufim9oUt9KUoJ9AvyOPA==} peerDependencies: '@babel/runtime': ^7.20.0 expo: '*' - expo-widgets: ^55.0.8 + expo-widgets: ^57.0.5 react-refresh: '>=0.14.0 <1.0.0' peerDependenciesMeta: '@babel/runtime': @@ -2340,9 +2415,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - badgin@1.2.3: - resolution: {integrity: sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -2353,25 +2425,25 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.13: - resolution: {integrity: sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw==} + baseline-browser-mapping@2.10.43: + resolution: {integrity: sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==} engines: {node: '>=6.0.0'} hasBin: true - better-opn@3.0.2: - resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} - engines: {node: '>=12.0.0'} + basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} - better-sqlite3@12.8.0: - resolution: {integrity: sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ==} - engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x} + better-sqlite3@12.11.1: + resolution: {integrity: sha512-dq9AtApgg5PGFtBzPFSBl3HZQjHok5gaQCM6zh2Yk0aSmDCs1CbnVI8/HgASQkNKsWFpseIO9beg5xxpYhbIfA==} + engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x || 26.x} big-integer@1.6.52: resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} engines: {node: '>=0.6'} - bignumber.js@10.0.2: - resolution: {integrity: sha512-E8Wp9O06QA6lneJ4aRUXKYf/1GIomqUEmUMwtIOMtDxf1U52ffJY+y7JBk/8wRafA8qOIqLnXQGqonYXZdBnFQ==} + bignumber.js@11.1.5: + resolution: {integrity: sha512-6WmzCNtUnfKpbozq+hOgWaZMMzORmYBwF1xZScyoIX3QRYWeKTtxxwDOW5tIz7C9BdjkIYHGTcelCLkXg0mndw==} bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} @@ -2379,11 +2451,12 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - bl@6.1.6: - resolution: {integrity: sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==} + body-parser@1.20.6: + resolution: {integrity: sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.2.2: - resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + body-parser@2.3.0: + resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} engines: {node: '>=18'} boolbase@1.0.0: @@ -2400,44 +2473,34 @@ packages: resolution: {integrity: sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==} engines: {node: '>= 5.10.0'} - brace-expansion@1.1.13: - resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} + brace-expansion@1.1.16: + resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} - brace-expansion@2.0.3: - resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==} + brace-expansion@2.1.2: + resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==} - brace-expansion@5.0.5: - resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + brace-expansion@5.0.7: + resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} engines: {node: 18 || 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.28.2: - resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + browserslist@4.28.6: + resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - bundle-name@4.1.0: - resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} - engines: {node: '>=18'} - bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -2446,8 +2509,8 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} engines: {node: '>= 0.4'} call-bound@1.0.4: @@ -2466,8 +2529,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001784: - resolution: {integrity: sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==} + caniuse-lite@1.0.30001806: + resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -2481,6 +2544,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} @@ -2504,8 +2571,8 @@ packages: engines: {node: '>=12.13.0'} hasBin: true - chromium-edge-launcher@0.2.0: - resolution: {integrity: sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==} + chromium-edge-launcher@0.3.0: + resolution: {integrity: sha512-p03azHlGjtyRvFEee3cyvtsRYdniSkwjkzmM/KmVnqT5d7QkkwpJBhis/zCLMYdQMVJ5tt140TBNqqrZPaWeFA==} ci-info@2.0.0: resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} @@ -2541,13 +2608,14 @@ packages: resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + cliui@9.0.1: + resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} + engines: {node: '>=20'} + clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} @@ -2556,6 +2624,9 @@ packages: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + code-block-writer@13.0.3: + resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} + collect-v8-coverage@1.0.3: resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} @@ -2598,9 +2669,6 @@ packages: resolution: {integrity: sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==} engines: {node: '>=18'} - colorette@1.4.0: - resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} - colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -2608,13 +2676,6 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - command-exists@1.2.9: - resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} - - commander@11.1.0: - resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} - engines: {node: '>=16'} - commander@12.1.0: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} @@ -2645,25 +2706,39 @@ packages: resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} engines: {node: '>= 0.10.0'} + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + core-js-compat@3.49.0: resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} - cosmiconfig@9.0.1: - resolution: {integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true - create-jest@29.7.0: resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2676,6 +2751,10 @@ packages: css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + css-what@6.2.2: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} @@ -2709,8 +2788,8 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} - dayjs@1.11.20: - resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} @@ -2737,10 +2816,6 @@ packages: supports-color: optional: true - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} @@ -2752,9 +2827,6 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - dedent@0.6.0: - resolution: {integrity: sha512-cSfRWjXJtZQeRuZGVvDrJroCR5V2UvBNUMHsPCdNYzuAG8b9V8aAy3KUcdQrGQPXs17Y+ojbPh1aOCplg9YR9g==} - dedent@1.7.2: resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} peerDependencies: @@ -2778,14 +2850,6 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - default-browser-id@5.0.1: - resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} - engines: {node: '>=18'} - - default-browser@5.5.0: - resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} - engines: {node: '>=18'} - defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} @@ -2797,10 +2861,6 @@ packages: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} - define-lazy-prop@3.0.0: - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} - engines: {node: '>=12'} - define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -2829,22 +2889,18 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dnssd-advertise@1.1.4: - resolution: {integrity: sha512-AmGyK9WpNf06WeP5TjHZq/wNzP76OuEeaiTlKr9E/EEelYLczywUKoqRz+DPRq/ErssjT4lU+/W7wzJW+7K/ZA==} + dnssd-advertise@1.1.6: + resolution: {integrity: sha512-Ndrrf6BMPalkQPd/zubL+4YghH2J9NspapQ09uDXwYbvOPkP0oaqf5CkcwJ0b50kS2O3ul6yVu+jz+RY62Cejg==} doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - dom-serializer@3.0.0: - resolution: {integrity: sha512-x+9D6nkC8tdXOQUS32egtZpZFLP90+HBZmWjuT920srbJvD/zPgFB9t4k3pEhlw5BQrXStQtRc1Y1zuriXk+Nw==} + dom-serializer@3.1.1: + resolution: {integrity: sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==} engines: {node: '>=20.19.0'} domelementtype@2.3.0: @@ -2876,10 +2932,12 @@ packages: drizzle-kit@1.0.0-beta.20: resolution: {integrity: sha512-qMUBnrOQIU+H32aF80BSn7lT1IQuKmofCypmkrKMuOMqvM0bhz5hjCHim1bLcXUzXYRtTSr6U2pe0MSV79WbAg==} + deprecated: The 1.0.0-beta line is superseded by the 1.0 release candidate. Install drizzle-kit@rc instead. hasBin: true - drizzle-orm@1.0.0-beta.20: - resolution: {integrity: sha512-7qiuw+Z6yGr+ywt3PS5dP6UCfdymIuFT/ni6GnPGzLhkBIolNBTo4ByMBWTxJ7dW/Ya6d73GtkeuKfcVcriVHA==} + drizzle-orm@1.0.0-beta.22: + resolution: {integrity: sha512-F+DZyVIvH0oVKa/w08Cle1xfoH+pc+htIXHG/frnMLG72aby9NYYr9oc+9XvghnoO4umxFItduz0OMmQJMnenw==} + deprecated: The 1.0.0-beta line is superseded by the 1.0 release candidate. Install drizzle-orm@rc instead. peerDependencies: '@aws-sdk/client-rds-data': '>=3' '@cloudflare/workers-types': '>=4' @@ -2956,6 +3014,8 @@ packages: optional: true '@types/better-sqlite3': optional: true + '@types/mssql': + optional: true '@types/pg': optional: true '@types/sql.js': @@ -2976,6 +3036,8 @@ packages: optional: true gel: optional: true + mssql: + optional: true mysql2: optional: true pg: @@ -3000,19 +3062,19 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.330: - resolution: {integrity: sha512-jFNydB5kFtYUobh4IkWUnXeyDbjf/r9gcUEXe1xcrcUxIGfTdzPXA+ld6zBRbwvgIGVzDll/LTIiDztEtckSnA==} + electron-to-chromium@1.5.393: + resolution: {integrity: sha512-kiDJdIUawuEIcp9XoICKp1iTYDEbgguIPq526N1Q7jIQDeQ3CqoMx71025PI/7E48Ddtw2HuWsVjY7afEgNxmg==} emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -3046,27 +3108,18 @@ packages: resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} engines: {node: '>=20.19.0'} - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - - envinfo@7.21.0: - resolution: {integrity: sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==} - engines: {node: '>=4'} - hasBin: true - error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} error-stack-parser@2.1.4: resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - errorhandler@1.5.2: - resolution: {integrity: sha512-kNAL7hESndBCrWwS72QyV3IVOTrVmj9D062FV5BQswNL5zEdeRmz/WJFyh6Aj/plvvSOrzddkxW57HgkZcR9Fw==} - engines: {node: '>= 0.8'} + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} - es-abstract@1.24.1: - resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -3077,12 +3130,12 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.3.1: - resolution: {integrity: sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==} + es-iterator-helpers@1.4.0: + resolution: {integrity: sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==} engines: {node: '>= 0.4'} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: @@ -3093,8 +3146,8 @@ packages: resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} engines: {node: '>= 0.4'} - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + es-to-primitive@1.3.4: + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} engines: {node: '>= 0.4'} esbuild@0.25.12: @@ -3126,24 +3179,53 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-config-prettier@8.10.2: - resolution: {integrity: sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==} - hasBin: true + eslint-config-expo@57.0.0: + resolution: {integrity: sha512-T7OTN9xrSZYjLw4qTkL1Mn2WfAUVmMGY38+OYAcraI1uiTFVH6jfkSkv84WLTqnneblLcb6AsMDV+SHcUj3hGw==} peerDependencies: - eslint: '>=7.0.0' + eslint: '>=8.10' - eslint-plugin-eslint-comments@3.2.0: - resolution: {integrity: sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==} - engines: {node: '>=6.5.0'} + eslint-import-resolver-node@0.3.10: + resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==} + + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.14.0: + resolution: {integrity: sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==} + engines: {node: '>=4'} peerDependencies: - eslint: '>=4.19.1' + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true - eslint-plugin-ft-flow@2.0.3: - resolution: {integrity: sha512-Vbsd/b+LYA99jUbsL6viEUWShFaYQt2YQs3QN3f+aeszOhh2sgdcU0mjzDyD4yyBvMc8qy2uwvBBWfMzEX06tg==} - engines: {node: '>=12.22.0'} + eslint-plugin-expo@1.1.0: + resolution: {integrity: sha512-vPP0EPx7IA7ZfP49dY4rq9RV5jqkFWG+Pih3/oGjzIRjMI+ogcOE8i6isYkLXAdw/yvFV2BRZkTaQaiOGQqn6Q==} + engines: {node: '>=18.0.0'} peerDependencies: - '@babel/eslint-parser': ^7.12.0 - eslint: ^8.1.0 + eslint: '>=8.10' eslint-plugin-ft-flow@3.0.11: resolution: {integrity: sha512-6ZJ4KYGYjIosCcU883zBBT1nFsKP58xrTOwguiw3/HRq0EpYAyhrF1nCGbK7V23cmKtPXMpDfl8qPupt5s5W8w==} @@ -3151,8 +3233,18 @@ packages: eslint: ^8.56.0 || ^9.0.0 hermes-eslint: '>=0.15.0' - eslint-plugin-jest@29.15.1: - resolution: {integrity: sha512-6BjyErCQauz3zfJvzLw/kAez2lf4LEpbHLvWBfEcG4EI0ZiRSwjoH2uZulMouU8kRkBH+S0rhqn11IhTvxKgKw==} + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jest@29.15.4: + resolution: {integrity: sha512-6ln5i9Nkrb27X4w91ZPt/xHDsVQnvxTS2ntgq6r32u+8gymdUrp88TdcBXSveZW0Dl+M5v2H6K75kJhMvUGhjg==} engines: {node: ^20.12.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@typescript-eslint/eslint-plugin': ^8.0.0 @@ -3167,24 +3259,11 @@ packages: typescript: optional: true - eslint-plugin-react-hooks@7.0.1: - resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} engines: {node: '>=18'} peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - - eslint-plugin-react-native-globals@0.1.2: - resolution: {integrity: sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g==} - - eslint-plugin-react-native@4.1.0: - resolution: {integrity: sha512-QLo7rzTBOl43FvVqDdq5Ql9IoElIuTdjrz9SKAXCvULvBoRZ44JGSkx9z4999ZusCsb4rK3gjS8gOGyeYqZv2Q==} - peerDependencies: - eslint: ^3.17.0 || ^4 || ^5 || ^6 || ^7 || ^8 - - eslint-plugin-react-native@5.0.0: - resolution: {integrity: sha512-VyWlyCC/7FC/aONibOwLkzmyKg4j9oI8fzrk9WYNs4I8/m436JuOTAFwLvEn1CVvc7La4cPfbCyspP4OYpP52Q==} - peerDependencies: - eslint: ^3.17.0 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 eslint-plugin-react@7.37.5: resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} @@ -3198,35 +3277,35 @@ packages: peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@2.1.0: - resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} - engines: {node: '>=10'} + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@5.0.1: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@8.57.1: - resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + eslint@9.39.5: + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} @@ -3241,10 +3320,6 @@ packages: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} - estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} @@ -3261,13 +3336,6 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -3284,168 +3352,220 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - expo-application@55.0.10: - resolution: {integrity: sha512-5ccf+S6hsQz+doi907TOJxKzV5AKgAgw004z4FoDWSoGhfab0LUPg6uyvOspuU4cbNvqw8EAy08hZbVO8nKc9Q==} + expo-asset@57.0.6: + resolution: {integrity: sha512-n3Yb1VxcP+BMRTyC4R1x2It4+m5EDkNXiVCHGWbnIREQUUkMs2Yeul7D5qfFWAYtIn2Z3hbGMndwU6Az1FPSEg==} + peerDependencies: + expo: '*' + react: '*' + react-native: '*' + + expo-atlas@0.4.3: + resolution: {integrity: sha512-nN2bouxFvMsqZqLl0ka+eI9Ofida0PcuoE4v+7fHlgyp95X2cCL8Acf0nRKXmmIBEYazdw0d7BAOZfC0b41oxA==} + hasBin: true + peerDependencies: + expo: '*' + + expo-build-properties@57.0.6: + resolution: {integrity: sha512-/iEpjjBgOc8twOqtaDdVcerBRbZGyOFhhpsBch2IfxNFazbrOIIPXc97s732Fia8HiNkhwaHk3GkBB7zi4WZ3g==} peerDependencies: expo: '*' - expo-asset@55.0.10: - resolution: {integrity: sha512-wxjNBKIaDyachq7oJgVlWVFzZ6SnNpJFJhkkcymXoTPt5O3XmDM+a6fT91xQQawCXTyZuCc1sNxKMetEofeYkg==} + expo-clipboard@57.0.1: + resolution: {integrity: sha512-HWICri4+1ao7S6QEfcorxVumXDiDnx1guGGewjZgGJWLGxFYs0RgH8ujBs+lkTzBkMmlwADaWSlaesR+nDJt5Q==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-clipboard@55.0.9: - resolution: {integrity: sha512-WJ9ougE8fEDu3/RV5Vz3gE5aNgnj1C0WByXCz3WUj8y/06bJyaIUDMq8FDLv3n0AO3guiErmkUunsD0EzSDUUw==} + expo-constants@57.0.6: + resolution: {integrity: sha512-OV+4XUshdO18TKNlo1cxUkXeJWgUOPgalvl8ofmc7kmPPHoyfz2hGJ94tyY/RND/GG5RREE+me9YHClNEzo+Ow==} peerDependencies: expo: '*' - react: '*' react-native: '*' - expo-constants@55.0.9: - resolution: {integrity: sha512-iBiXjZeuU5S/8docQeNzsVvtDy4w0zlmXBpFEi1ypwugceEpdQQab65TVRbusXAcwpNVxCPMpNlDssYp0Pli2g==} + expo-dev-client@57.0.7: + resolution: {integrity: sha512-Xj4KEzPuzpQO2JyWpue2wfSYgQgmP9y9MSsfapmBycCAqh55KN2hKeTRUhs7GuwfEgTsUvSsk8aqa6Sr8nG+UQ==} + peerDependencies: + expo: '*' + + expo-dev-launcher@57.0.7: + resolution: {integrity: sha512-BWitJkzWhRBUIPUjk36AWA0R9P4rKD5YFINeAAuUto27g6/cHcEmWQvd2EVMV/e2bX310yURTsb8VheMVwtQTw==} + peerDependencies: + expo: '*' + react-native: '*' + + expo-dev-menu-interface@57.0.0: + resolution: {integrity: sha512-F47VdzOHYc19FhI/jBgctpO8a5UskTIxG6a1E5t3W5gF8VImuvBQffdXXfLHhsuCl7dS3v3U0R45cleeVXO1Zg==} + peerDependencies: + expo: '*' + + expo-dev-menu@57.0.7: + resolution: {integrity: sha512-9Dv7PumsVhdRYrPAeSJEiPHnJsr/Q1TlQ4fqMfxYdS4YkOx/Oi5LWOPZ+kW6KhIDYoCYVBo5H6ey9tROGlsO2Q==} peerDependencies: expo: '*' react-native: '*' - expo-document-picker@55.0.9: - resolution: {integrity: sha512-XtkhmZ9alOj1n2Ok782lK9qtfk9TbaBoOJotSDK0pvq5oa+3UyHlLLTUmnC5UPMmFKoLGLJC3R2fLBFEiN5jCQ==} + expo-document-picker@57.0.1: + resolution: {integrity: sha512-qBwM5oxDZ3I9kwFD3pUE1oK/WNv9artoEKO6UpqhQgNRr0XA1ALRVWYjkF4+ge9lUNDRehjTm/jenINkzqg84g==} peerDependencies: expo: '*' - expo-file-system@55.0.12: - resolution: {integrity: sha512-MFN/3L3gm174nxP2HqKQsSsPbjAj92wuidKFGSbl3Lt6oJTS09EbTwszX5BhYeeVSprcsw8pnlxYSmhkSqGEFw==} + expo-file-system@57.0.1: + resolution: {integrity: sha512-w7/ERvQFrGP2apTO9lDtZ+O6JQIhfakL7+Xqzh+rfMO9B4LB4qwrz+YvLgir8KFRVX64JHBnRuYBVLY1oQZcqw==} peerDependencies: expo: '*' react-native: '*' - expo-font@55.0.4: - resolution: {integrity: sha512-ZKeGTFffPygvY5dM/9ATM2p7QDkhsaHopH7wFAWgP2lKzqUMS9B/RxCvw5CaObr9Ro7x9YptyeRKX2HmgmMfrg==} + expo-font@57.0.1: + resolution: {integrity: sha512-QyS9L1Kh9sKJg4gfU6rdbpxpmH+DyzBX8z6jVvXMUDoqLr1GqmkO/Wu379KCXjL///kWbhpNlbi7AgBuj4VdIQ==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-haptics@55.0.9: - resolution: {integrity: sha512-KCRyHr/uu4syXmoq3aIQ6ahuaX6FGtlPkWGlLlHJ836WF3nG+5+oCaCQiI7qMTpml+Tp/V/zP4ZaowM2KHgLNA==} + expo-haptics@57.0.1: + resolution: {integrity: sha512-8VhbnxlIrfXjP0syZr1JT197nafYicQu9119adOJnX62osU9Cw+PdDnAx/6LxuKJRzQdwxOMq7b7eWjhNL5zAQ==} + peerDependencies: + expo: '*' + + expo-image@57.0.1: + resolution: {integrity: sha512-EP0lisd2bUqtErry4weRcMW9bLMxtKsht/MLLK3/3do5u4ZMiJbWkY5zfYV+WYmeGab7x9G0sjbeFeEagYGjMw==} peerDependencies: expo: '*' + react: '*' + react-native: '*' + react-native-web: '*' + peerDependenciesMeta: + react-native-web: + optional: true + + expo-json-utils@57.0.1: + resolution: {integrity: sha512-cgTe1NqzQdYs/WN+3nIY5IZg8s0pb0xaTUbhYvxQDn137GbwRfHoGM2se3m3Vsl4Qu+B9G4RPEK5WJDEU2Do7g==} - expo-keep-awake@55.0.4: - resolution: {integrity: sha512-vwfdMtMS5Fxaon8gC0AiE70SpxTsHJ+rjeoVJl8kdfdbxczF7OIaVmfjFJ5Gfigd/WZiLqxhfZk34VAkXF4PNg==} + expo-keep-awake@57.0.1: + resolution: {integrity: sha512-28lkFImeXTS+bhAjuCFV7w7tW5bXg27BJVrxv+nC/nyYa86qEa0oFeHwqol6ha5k4pdVDQgBF09GM4A1k76Ssg==} peerDependencies: expo: '*' react: '*' - expo-linear-gradient@55.0.9: - resolution: {integrity: sha512-S82iF+CVoSBVHdwusLQGh6Th/kcWLHU47jZhBPwyTrYWnsHZtb0oCqU96YvhDYvhbTdsuOaKEi+Xu+r/I2R8ow==} + expo-linear-gradient@57.0.1: + resolution: {integrity: sha512-CpS8eMqoIWcHVGKV66zbDvzotCw9qYp3f8CuI9N+h1LaO0tMLUzBpkhAKePUsXlpN3yolYlHFSPkfVZ/uSh+iA==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-linking@55.0.9: - resolution: {integrity: sha512-QWEefQZUu7PuJzye19Hr6msqpO4VB4TiY4T/6AkISJzZnoZGxWg16s3JTZS7D/b3VMm8VQfhw9I5NF/7f8EPcA==} + expo-linking@57.0.3: + resolution: {integrity: sha512-uF5PFfIQ0cjEGijzFuN8gU5eLqWs6BgS29Zp4ddP79XPMobZmS6qzdGyCOZTnZz3UWR17rFAJst1UntuZg9/6Q==} peerDependencies: react: '*' react-native: '*' - expo-localization@55.0.9: - resolution: {integrity: sha512-ABRg4wEt15OCp9/3XOLC4ltPVvXmJgeCKNTJ4Nb8N6byuHITJHvZ3PwcC2YGpTzlAqfGbcs3rJdfg1ObT54PJQ==} + expo-localization@57.0.1: + resolution: {integrity: sha512-8Ffl4UTbOsQeGT0v5fxMbyPHyPMPnhSPDFQJa8p9rjJrthFoAtNi+fL6Ssmrvf1/7dmPq1mVY52MEt0TMEfgjA==} peerDependencies: expo: '*' react: '*' - expo-modules-autolinking@55.0.12: - resolution: {integrity: sha512-nZOPjpl4v5YInNftJpX10bYxDNNq2HM+hWTfr3FPE1/i0lES/cnvaB8v4XKpDTuAUdBwkGYadTfNwNG9k/Ftgw==} + expo-manifests@57.0.1: + resolution: {integrity: sha512-qB/mDG2dYdl+EvUeQuqP8KFYCFgFCQjJYdWIHo8SFBgDzMYmdF286DFY2M1M9Okr99wkb5M4tgA3aCcwv3aEQA==} + peerDependencies: + expo: '*' + + expo-modules-autolinking@57.0.8: + resolution: {integrity: sha512-YBDgbJHlhhhr3JaKErW6znsIL8zQsh206LaDpWrLHV/WluRwhYfZuF/fhkVN7b/DsRxIu0UeCPExaEPMxlC2Hw==} hasBin: true - expo-modules-core@55.0.18: - resolution: {integrity: sha512-Qwr3qCCZd/aMtenUo6KmPaFy/uFeNz0rLfRxv0tNsWFF27XS2wjDwb87A7lD2ii8iJhjYEHVetRvFkcDxCw8Lw==} + expo-modules-core@57.0.6: + resolution: {integrity: sha512-hePwOh2+i+EpWrVnv95sQeQ0OD5PYuEuIhmglVLzQVaVBV3zHcRvLAc1rV8ROqUL6YtAQUXjPkoSx1ly2ZdTuQ==} peerDependencies: react: '*' react-native: '*' + react-native-worklets: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 + peerDependenciesMeta: + react-native-worklets: + optional: true - expo-navigation-bar@55.0.9: - resolution: {integrity: sha512-9GF+HpfUrhQWZ2YgOrcb2T5Apt93XHxUbPbLYfJ9gvNdekwyzWw+2aVMCEBzFyo4V7p+jRB4CD4ybn+jS+SubA==} + expo-modules-jsi@57.0.3: + resolution: {integrity: sha512-B+iXJCC5OIXjFKkLLSY2DZ8BGS+hC3PBTrALpV43pHqhXqqZrRH3uTEylZgsoKQO8N8tjmjAua2ucTFHtr1o0w==} peerDependencies: - expo: '*' - react: '*' react-native: '*' - expo-notifications@55.0.14: - resolution: {integrity: sha512-fwWTd0OK82Yj2MLJJK0cIgaRtAu8OUcjGuucdtsp/dOsErqBsGQQWpotoEXoRPrDrCL4sHwSvg9QzGdeouJ/jQ==} + expo-navigation-bar@57.0.2: + resolution: {integrity: sha512-Ufe49dsTZjarA7gMMwwwnNJWpXo+oTyl5Ud0g3lsGVrj2HXjyw9cb9098N8+CoWpjfIa5PghuRYYXrcCrGQIHg==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-server@55.0.6: - resolution: {integrity: sha512-xI72FTm469FfuuBL2R5aNtthgH+GR7ygOpsx/KcPS0K8AZaZd7VjtEExbzn9/qyyYkWW3T+3dAmCDKOMX8gdmQ==} + expo-server@57.0.1: + resolution: {integrity: sha512-sBfVDH6dmKVHZxqUxbfkzS00PZELMZt1IpnHKxcOTMZtR/t7CtRAFrbXcisG+EyzeqHSVDacZT+1tbYfZt5D8w==} engines: {node: '>=20.16.0'} - expo-speech@55.0.9: - resolution: {integrity: sha512-xDUHeMb3Zp9ICHscSzwCtmeRMCYD/zSPoQn75yjEjgQUzyIwfthUpEiUGwIo1DUC728PdRSvBegnYqv+3MQJPA==} + expo-speech@57.0.1: + resolution: {integrity: sha512-xRI5FyAju4rK10xcVE+3SjdrF1ni24+Sj8ujSrylBsbrwFrrLO19H5DkG6EzXXVQ6lWdn+dC0JCr8OLaM7HI+Q==} peerDependencies: expo: '*' - expo-sqlite@16.0.10: - resolution: {integrity: sha512-tUOKxE9TpfneRG3eOfbNfhN9236SJ7IiUnP8gCqU7umd9DtgDGB/5PhYVVfl+U7KskgolgNoB9v9OZ9iwXN8Eg==} + expo-splash-screen@57.0.4: + resolution: {integrity: sha512-AspaOA5VZUl8yXb/BQORrRdOs0f7F/IgmvKNdCFaUDuz4X+j/4bPkkyssARFKwC5A5IQ3g7QS/Ve1XxDiDZ38Q==} peerDependencies: expo: '*' - react: '*' - react-native: '*' - expo-web-browser@55.0.10: - resolution: {integrity: sha512-2d6qVrg/nt0JvW5uAqOMDG/xITIXFe1Prkq1ri+I3PrC0QmV5cMYNSagU9ykfC8S7YKWxF1qO7Qsih9fxNa9dw==} + expo-updates-interface@57.0.1: + resolution: {integrity: sha512-+LUWwJ0gf/TEKMVdQAw/Gjih4dvrk+URgy24X9qEGKuuMDZqjBRm9T4yQyBVALGL5TTdPUaB6ILxx3lshm3pwQ==} + peerDependencies: + expo: '*' + + expo-web-browser@57.0.1: + resolution: {integrity: sha512-akqnOoJRzKXbFbiqTwxnmn8mVoSfPKEBxwDNxQJHAGElgneL28jlEPYaTDpOONz9azPWVfEHZ/i7kqQgN2ExXg==} peerDependencies: expo: '*' react-native: '*' - expo@55.0.9: - resolution: {integrity: sha512-bYDhqr2v2UtTf/9s493bUVRtxsYqXF4KXkaS3sSW827DmgxNJv0NuWKWwfqFdDxKvDELd488J5X9l9ogqUrwOA==} + expo@57.0.7: + resolution: {integrity: sha512-PJdE0EjoX878OqClmsigVKdT0jCNOAQDRLcvFkeBakxaVyEDhjcQ8baKq2YysYH2g0YNB1rEIpHUiKtluO918A==} hasBin: true peerDependencies: '@expo/dom-webview': '*' '@expo/metro-runtime': '*' react: '*' + react-dom: '*' react-native: '*' + react-native-web: '*' react-native-webview: '*' peerDependenciesMeta: '@expo/dom-webview': optional: true '@expo/metro-runtime': optional: true + react-dom: + optional: true + react-native-web: + optional: true react-native-webview: optional: true exponential-backoff@3.1.3: resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + express@4.22.2: + resolution: {integrity: sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==} + engines: {node: '>= 0.10.0'} + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-xml-builder@1.1.4: - resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} - - fast-xml-parser@5.5.9: - resolution: {integrity: sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g==} - hasBin: true - - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - fb-dotslash@0.5.8: resolution: {integrity: sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==} engines: {node: '>=20'} @@ -3466,9 +3586,9 @@ packages: fetch-nodeshim@0.4.10: resolution: {integrity: sha512-m6I8ALe4L4XpdETy7MJZWs6L1IVMbjs99bwbpIKphxX+0CTns4IKDWJY0LWfr4YsFjfg+z1TjzTMU8lKl8rG0w==} - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -3485,6 +3605,14 @@ packages: resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} engines: {node: '>= 0.8'} + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + find-babel-config@2.1.2: resolution: {integrity: sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg==} @@ -3500,13 +3628,13 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - find-up@7.0.0: - resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} - engines: {node: '>=18'} + find-up@8.0.0: + resolution: {integrity: sha512-JGG8pvDi2C+JxidYdIwQDyS/CgcrIdh18cvgxcBge3wSHRQOrooMD3GlFBcmMJAN9M42SAZjDp5zv1dglJjwww==} + engines: {node: '>=20'} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} @@ -3521,21 +3649,29 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} engines: {node: '>= 6'} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + freeport-async@2.0.0: + resolution: {integrity: sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==} + engines: {node: '>=8'} + fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -3547,8 +3683,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.8: - resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + function.prototype.name@1.2.0: + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} engines: {node: '>= 0.4'} functions-have-names@1.2.3: @@ -3566,6 +3702,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -3586,8 +3726,8 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.13.7: - resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} getenv@2.0.0: resolution: {integrity: sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==} @@ -3596,10 +3736,6 @@ packages: github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -3617,9 +3753,13 @@ packages: engines: {node: '>=16 || 14 >=14.17'} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} @@ -3632,9 +3772,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -3662,42 +3799,50 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - hermes-compiler@0.14.1: - resolution: {integrity: sha512-+RPPQlayoZ9n6/KXKt5SFILWXCGJ/LV5d24L5smXrvTDrPS4L6dSctPczXauuvzFP3QEJbD1YO7Z3Ra4a+4IhA==} + hermes-compiler@250829098.0.14: + resolution: {integrity: sha512-5meXwsZxgiqFaJjNzwjzI9IyUkuGGBisu+z9BvQWmGVpjH6nz11hgqkyxe4dl8UAdyIV4lTbz91+Dlnjz0VxqA==} - hermes-compiler@250829098.0.2: - resolution: {integrity: sha512-9+la7TeGGZg5Cj0UV5MbqhqyT/Hd/PUlQZ3Uhggy0jiaiG2qxFJbi/uGWALTqa7GwETyQa4A7ngus1A1aXA8QQ==} - - hermes-eslint@0.33.3: - resolution: {integrity: sha512-eGY0l6T5U9LDdC+uN88NrSOrvPPtXGPxN7EaD38hytWuBEVXypq0eQ1SNVsnQPBZLWi+b1jkF4F5aVtTCQC6wg==} + hermes-eslint@0.37.0: + resolution: {integrity: sha512-GOZ2xpOe28u7hN5e4NMBCEVZ9KwmigPtFZDXoSRgUWL6sFlppxB5T7WBiLRO00c+sW5R0YM4cbOmnWtTK+DQcQ==} + peerDependencies: + eslint: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} - hermes-estree@0.32.0: - resolution: {integrity: sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==} + hermes-estree@0.35.0: + resolution: {integrity: sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==} + + hermes-estree@0.36.0: + resolution: {integrity: sha512-A1+8zn5oss2CFP7pKsOaxorQG6FNIz1WU1VDqruLPPZl3LVgeE2C5xfFg8Ow6/Ow4mSslLLtYP1J3n38eKyW9w==} - hermes-estree@0.32.1: - resolution: {integrity: sha512-ne5hkuDxheNBAikDjqvCZCwihnz0vVu9YsBzAEO1puiyFR4F1+PAz/SiPHSsNTuOveCYGRMX8Xbx4LOubeC0Qg==} + hermes-estree@0.36.1: + resolution: {integrity: sha512-guv1nQ6IJ7S83NRFPWc3SA7IBZrdNC9kapwOq6uXvF4wP+sDCgjzQbKPCoyYmoyZRzztF/n/c36l/rccCZSiCw==} - hermes-estree@0.33.3: - resolution: {integrity: sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==} + hermes-estree@0.37.0: + resolution: {integrity: sha512-Frp4+A518C4zZgB2+Qo1srlM3y6nbrFbNNzBF3g7tEuQXHXTb2h39V25JQLNHotxclkD+AZTN/JCCpiTFlhIGg==} hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} - hermes-parser@0.32.0: - resolution: {integrity: sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==} + hermes-parser@0.35.0: + resolution: {integrity: sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==} - hermes-parser@0.32.1: - resolution: {integrity: sha512-175dz634X/W5AiwrpLdoMl/MOb17poLHyIqgyExlE8D9zQ1OPnoORnGMB5ltRKnpvQzBjMYvT2rN/sHeIfZW5Q==} + hermes-parser@0.36.0: + resolution: {integrity: sha512-GdpwMmH5x6IpC1cijvcvYnlPB60Mh6kTSF/NFdYV/j56gYdi+0RIakYs+eqOV+bbO0SW7mgVVGSsTJxyPQfo3w==} - hermes-parser@0.33.3: - resolution: {integrity: sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==} + hermes-parser@0.36.1: + resolution: {integrity: sha512-GApNk4zLHi2UWoWZZkx7LNCOSzLSc5lB55pZ/PhK7ycFeg7u5LcF88p/WbpIi1XUDtE0MpHE3uRR3u3KB7TjSQ==} + + hermes-parser@0.37.0: + resolution: {integrity: sha512-8PuWcyaF6VHQHL2dbU+QEIkbtHYQrTX/6+ep4VcBLJ3f/ehevowr5Zj8LQfYXQQGFG2O+df7hIMtt4PkkhfK3Q==} hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} @@ -3731,10 +3876,6 @@ packages: resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} engines: {node: '>= 6'} - http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} - https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -3755,12 +3896,16 @@ packages: i18n-js@4.5.3: resolution: {integrity: sha512-5/tT6R9t9qlYqGhxGq9I9Ap3WKUaAMq5aRuO1gqAcUqm6xGbL0jwTAjSFjgbx935BAV8QbEzvQOzE796dUlEfA==} + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.2: - resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} ieee754@1.2.1: @@ -3770,8 +3915,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} engines: {node: '>= 4'} image-size@1.2.1: @@ -3813,6 +3958,10 @@ packages: invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -3835,12 +3984,15 @@ packages: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} engines: {node: '>= 0.4'} is-data-view@1.0.2: @@ -3856,10 +4008,9 @@ packages: engines: {node: '>=8'} hasBin: true - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true + is-document.all@1.0.0: + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + engines: {node: '>= 0.4'} is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} @@ -3869,10 +4020,6 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} - is-fullwidth-code-point@2.0.0: - resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} - engines: {node: '>=4'} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -3893,15 +4040,6 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - - is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -3918,10 +4056,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - is-plain-object@5.0.0: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} @@ -3929,6 +4063,9 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -3957,10 +4094,6 @@ packages: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -3973,18 +4106,10 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} - is-wsl@1.1.0: - resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==} - engines: {node: '>=4'} - is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} - is-wsl@3.1.1: - resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} - engines: {node: '>=16'} - isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -4053,8 +4178,8 @@ packages: resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-diff@30.3.0: - resolution: {integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==} + jest-diff@30.4.1: + resolution: {integrity: sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-docblock@29.7.0: @@ -4078,14 +4203,17 @@ packages: resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-expo@55.0.11: - resolution: {integrity: sha512-dwOrmui4nYFuu6P3pMFXdJ9EzwpLsfTQJk4Fp3wuOmfredAgqOihljHUJwpzG1aGbSQc1+xhGgE7SmK21L4svw==} + jest-expo@57.0.2: + resolution: {integrity: sha512-xoKiYyu8c0fdBsFMkeFnxoTZ/0g4rLldA9isVb7VJSGBGesmhkVor7YkftkHqQ5rWiZ99IY+/uIrzTgb1nC/UA==} hasBin: true peerDependencies: + '@react-native/jest-preset': ^0.86.0 expo: '*' react-native: '*' react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4 peerDependenciesMeta: + expo: + optional: true react-server-dom-webpack: optional: true @@ -4105,8 +4233,8 @@ packages: resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-matcher-utils@30.3.0: - resolution: {integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==} + jest-matcher-utils@30.4.1: + resolution: {integrity: sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-message-util@29.7.0: @@ -4188,25 +4316,19 @@ packages: jimp-compact@0.16.1: resolution: {integrity: sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==} - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true - joi@17.13.3: - resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} - - js-md4@0.3.2: - resolution: {integrity: sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.2: - resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + js-yaml@3.15.0: + resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==} hasBin: true - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + js-yaml@4.3.0: + resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} hasBin: true jsbi@4.3.2: @@ -4241,28 +4363,19 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - - jsonwebtoken@9.0.3: - resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} - engines: {node: '>=12', npm: '>=6'} - jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} - jwa@2.0.1: - resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - - jws@4.0.1: - resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -4270,12 +4383,12 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - lan-network@0.2.0: - resolution: {integrity: sha512-EZgbsXMrGS+oK+Ta12mCjzBFse+SIewGdwrSTr5g+MSymnjpox2x05ceI20PQejJOFvOgzcXrfDk/SdY7dSCtw==} + lan-network@0.2.1: + resolution: {integrity: sha512-ONPnazC96VKDntab9j9JKwIWhZ4ZUceB4A9Epu4Ssg0hYFmtHZSeQ+n15nIwTFmcBUKtExOer8WTJ4GF9MO64A==} hasBin: true - launch-editor@2.13.2: - resolution: {integrity: sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg==} + launder@1.7.1: + resolution: {integrity: sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw==} leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} @@ -4323,24 +4436,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -4391,62 +4508,33 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - locate-path@7.2.0: - resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + locate-path@8.0.0: + resolution: {integrity: sha512-XT9ewWAC43tiAV7xDAPflMkG0qOPn2QjHqlgX8FOqmWa/rxnyYDulF9T0F7tRy1u+TVTmK/M//6VIOye+2zDXg==} + engines: {node: '>=20'} - lodash-es@4.17.23: - resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - lodash.includes@4.3.0: - resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} - - lodash.isboolean@3.0.3: - resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} - - lodash.isinteger@4.0.4: - resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} - - lodash.isnumber@3.0.3: - resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} - - lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - - lodash.isstring@4.0.1: - resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - lodash.throttle@4.1.1: resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} log-symbols@2.2.0: resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==} engines: {node: '>=4'} - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - log-update@4.0.0: resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} engines: {node: '>=10'} - logkitty@0.7.1: - resolution: {integrity: sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ==} - hasBin: true - long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} @@ -4454,28 +4542,24 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - lottie-ios@3.2.3: - resolution: {integrity: sha512-mubYMN6+1HXa8z3EJKBvNBkl4UoVM4McjESeB2PgvRMSngmJtC5yUMRdhbbrIAn5Liu3hFGao/14s5hQIgtkRQ==} - - lottie-ios@3.5.0: - resolution: {integrity: sha512-DM6BYLhHTzvUsK89AjY+K9RwVGkOBwbH/iytjyZUmFbXz8DVsoPEyy+c7L5NZmVouZHvLnOQp6NaYTkwMo+iOg==} - - lottie-react-native@5.1.6: - resolution: {integrity: sha512-vhdeZstXMfuVKwnddYWjJgQ/1whGL58IJEJu/iSf0XQ5gAb4pp/+vy91mdYQLezlb8Aw4Vu3fKnqErJL2hwchg==} + lottie-react-native@7.3.8: + resolution: {integrity: sha512-GAOl99TKi0c6xCcB1AJ+o70mgOPIAI/7K2G+Gs+o4po/qBhgvWijPNCKH8h6yNmlwFTg+RN3DmzRvHNFGxZMKQ==} peerDependencies: - lottie-ios: ^3.4.0 + '@lottiefiles/dotlottie-react': ^0.13.5 react: '*' react-native: '>=0.46' react-native-windows: '>=0.63.x' peerDependenciesMeta: + '@lottiefiles/dotlottie-react': + optional: true react-native-windows: optional: true lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.2.7: - resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -4498,134 +4582,90 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + media-typer@1.1.1: + resolution: {integrity: sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==} engines: {node: '>= 0.8'} memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - metro-babel-transformer@0.83.3: - resolution: {integrity: sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g==} - engines: {node: '>=20.19.4'} - - metro-babel-transformer@0.83.5: - resolution: {integrity: sha512-d9FfmgUEVejTiSb7bkQeLRGl6aeno2UpuPm3bo3rCYwxewj03ymvOn8s8vnS4fBqAPQ+cE9iQM40wh7nGXR+eA==} - engines: {node: '>=20.19.4'} - - metro-cache-key@0.83.3: - resolution: {integrity: sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw==} - engines: {node: '>=20.19.4'} - - metro-cache-key@0.83.5: - resolution: {integrity: sha512-Ycl8PBajB7bhbAI7Rt0xEyiF8oJ0RWX8EKkolV1KfCUlC++V/GStMSGpPLwnnBZXZWkCC5edBPzv1Hz1Yi0Euw==} - engines: {node: '>=20.19.4'} + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - metro-cache@0.83.3: - resolution: {integrity: sha512-3jo65X515mQJvKqK3vWRblxDEcgY55Sk3w4xa6LlfEXgQ9g1WgMh9m4qVZVwgcHoLy0a2HENTPCCX4Pk6s8c8Q==} - engines: {node: '>=20.19.4'} - - metro-cache@0.83.5: - resolution: {integrity: sha512-oH+s4U+IfZyg8J42bne2Skc90rcuESIYf86dYittcdWQtPfcaFXWpByPyTuWk3rR1Zz3Eh5HOrcVImfEhhJLng==} - engines: {node: '>=20.19.4'} - - metro-config@0.83.3: - resolution: {integrity: sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA==} - engines: {node: '>=20.19.4'} - - metro-config@0.83.5: - resolution: {integrity: sha512-JQ/PAASXH7yczgV6OCUSRhZYME+NU8NYjI2RcaG5ga4QfQ3T/XdiLzpSb3awWZYlDCcQb36l4Vl7i0Zw7/Tf9w==} - engines: {node: '>=20.19.4'} - - metro-core@0.83.3: - resolution: {integrity: sha512-M+X59lm7oBmJZamc96usuF1kusd5YimqG/q97g4Ac7slnJ3YiGglW5CsOlicTR5EWf8MQFxxjDoB6ytTqRe8Hw==} - engines: {node: '>=20.19.4'} + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} - metro-core@0.83.5: - resolution: {integrity: sha512-YcVcLCrf0ed4mdLa82Qob0VxYqfhmlRxUS8+TO4gosZo/gLwSvtdeOjc/Vt0pe/lvMNrBap9LlmvZM8FIsMgJQ==} - engines: {node: '>=20.19.4'} + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - metro-file-map@0.83.3: - resolution: {integrity: sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA==} - engines: {node: '>=20.19.4'} + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} - metro-file-map@0.83.5: - resolution: {integrity: sha512-ZEt8s3a1cnYbn40nyCD+CsZdYSlwtFh2kFym4lo+uvfM+UMMH+r/BsrC6rbNClSrt+B7rU9T+Te/sh/NL8ZZKQ==} - engines: {node: '>=20.19.4'} + metro-babel-transformer@0.84.4: + resolution: {integrity: sha512-rvCfz8snl9h20VcvpOHxZuHP1SlAkv4HXbzw7nyyVwu6Eqo5PRerbakQ9XmUCOsRy70spJ37O+G1TK8oMzo48g==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-minify-terser@0.83.3: - resolution: {integrity: sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ==} - engines: {node: '>=20.19.4'} + metro-cache-key@0.84.4: + resolution: {integrity: sha512-wVO79aGrkYImpnaVS4+d5RrRBRPX31QtvKB3wKGBuiNSznduZTQHzsrJZRroFJSwnygrzdsGUtDQPuqqFjFdvw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-minify-terser@0.83.5: - resolution: {integrity: sha512-Toe4Md1wS1PBqbvB0cFxBzKEVyyuYTUb0sgifAZh/mSvLH84qA1NAWik9sISWatzvfWf3rOGoUoO5E3f193a3Q==} - engines: {node: '>=20.19.4'} + metro-cache@0.84.4: + resolution: {integrity: sha512-gpcFQdSLUwUCk71saKoE64jLFbx2nwTfVCcPSULMNT8QYq0p1eZZE29Jvd0HtT/UlhC3ZOutLxJME5xqD2JUZg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-resolver@0.83.3: - resolution: {integrity: sha512-0js+zwI5flFxb1ktmR///bxHYg7OLpRpWZlBBruYG8OKYxeMP7SV0xQ/o/hUelrEMdK4LJzqVtHAhBm25LVfAQ==} - engines: {node: '>=20.19.4'} + metro-config@0.84.4: + resolution: {integrity: sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-resolver@0.83.5: - resolution: {integrity: sha512-7p3GtzVUpbAweJeCcUJihJeOQl1bDuimO5ueo1K0BUpUtR41q5EilbQ3klt16UTPPMpA+tISWBtsrqU556mY1A==} - engines: {node: '>=20.19.4'} + metro-core@0.84.4: + resolution: {integrity: sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-runtime@0.83.3: - resolution: {integrity: sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw==} - engines: {node: '>=20.19.4'} + metro-file-map@0.84.4: + resolution: {integrity: sha512-KSVDi/u60hKPx++NLu3MTIvyjzNoJnFAF8PQFxaj1jiSka/wjw+Ua6sNuJ0TDHQv+7AAoFQxeMgaRAe8Yic5wQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-runtime@0.83.5: - resolution: {integrity: sha512-f+b3ue9AWTVlZe2Xrki6TAoFtKIqw30jwfk7GQ1rDUBQaE0ZQ+NkiMEtb9uwH7uAjJ87U7Tdx1Jg1OJqUfEVlA==} - engines: {node: '>=20.19.4'} + metro-minify-terser@0.84.4: + resolution: {integrity: sha512-5qpbaVOMC7CPitIpuewzVeGw7E+C3ykbv2mqTjQLl85Z3annSVGlSCTcsZjqXZzjupfK4Ztj3dDc4kc44NZwtQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-source-map@0.83.3: - resolution: {integrity: sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg==} - engines: {node: '>=20.19.4'} + metro-resolver@0.84.4: + resolution: {integrity: sha512-1qLgbxQ5ZGhhutuPot1Yp348ofDsATL2WkrHF65TobqTT9K3P9qJXw38bomk7ncp5B7OYMfWwtyBZo1lCV792A==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-source-map@0.83.5: - resolution: {integrity: sha512-VT9bb2KO2/4tWY9Z2yeZqTUao7CicKAOps9LUg2aQzsz+04QyuXL3qgf1cLUVRjA/D6G5u1RJAlN1w9VNHtODQ==} - engines: {node: '>=20.19.4'} + metro-runtime@0.84.4: + resolution: {integrity: sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-symbolicate@0.83.3: - resolution: {integrity: sha512-F/YChgKd6KbFK3eUR5HdUsfBqVsanf5lNTwFd4Ca7uuxnHgBC3kR/Hba/RGkenR3pZaGNp5Bu9ZqqP52Wyhomw==} - engines: {node: '>=20.19.4'} - hasBin: true + metro-source-map@0.84.4: + resolution: {integrity: sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-symbolicate@0.83.5: - resolution: {integrity: sha512-EMIkrjNRz/hF+p0RDdxoE60+dkaTLPN3vaaGkFmX5lvFdO6HPfHA/Ywznzkev+za0VhPQ5KSdz49/MALBRteHA==} - engines: {node: '>=20.19.4'} + metro-symbolicate@0.84.4: + resolution: {integrity: sha512-OnfpacxUqGPZQ27t8qK9mFa7uqHIlVWeqRqkCbvMvreEBiamEeOn8krKtcwgP5M4cYDPwuSmCTopHMVthqG4zA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} hasBin: true - metro-transform-plugins@0.83.3: - resolution: {integrity: sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A==} - engines: {node: '>=20.19.4'} - - metro-transform-plugins@0.83.5: - resolution: {integrity: sha512-KxYKzZL+lt3Os5H2nx7YkbkWVduLZL5kPrE/Yq+Prm/DE1VLhpfnO6HtPs8vimYFKOa58ncl60GpoX0h7Wm0Vw==} - engines: {node: '>=20.19.4'} - - metro-transform-worker@0.83.3: - resolution: {integrity: sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA==} - engines: {node: '>=20.19.4'} - - metro-transform-worker@0.83.5: - resolution: {integrity: sha512-8N4pjkNXc6ytlP9oAM6MwqkvUepNSW39LKYl9NjUMpRDazBQ7oBpQDc8Sz4aI8jnH6AGhF7s1m/ayxkN1t04yA==} - engines: {node: '>=20.19.4'} + metro-transform-plugins@0.84.4: + resolution: {integrity: sha512-kehr6HbAecqD0/a3xLXobELdPaAmRAl8bel0qagPF4vhZtux93nS8S4eq2kgKt6J2GnQpVjSoW1PXdst04mwow==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro@0.83.3: - resolution: {integrity: sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q==} - engines: {node: '>=20.19.4'} - hasBin: true + metro-transform-worker@0.84.4: + resolution: {integrity: sha512-W1IYMvvXTu4MxYr7d9h7CeG2vpIr3bmLLIavkPY4O1ilzDrvS8z/NEe6y+pC44Ff7raMXQgYSfdqDUwN/i39gg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro@0.83.5: - resolution: {integrity: sha512-BgsXevY1MBac/3ZYv/RfNFf/4iuW9X7f4H8ZNkiH+r667HD9sVujxcmu4jvEzGCAm4/WyKdZCuyhAcyhTHOucQ==} - engines: {node: '>=20.19.4'} + metro@0.84.4: + resolution: {integrity: sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} hasBin: true micromatch@4.0.8: @@ -4653,11 +4693,6 @@ packages: engines: {node: '>=4'} hasBin: true - mime@2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true - mimic-fn@1.2.0: resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} engines: {node: '>=4'} @@ -4704,30 +4739,31 @@ packages: engines: {node: '>=10'} hasBin: true + morgan@1.11.0: + resolution: {integrity: sha512-zSkVu3t18r39pw4ixfBKvfZi3y2UOqr7d4WYwcj3m8nXpEQK4rPO6GLzs/CExoRgmX3y9EjmmcXqv6jq0SK46g==} + engines: {node: '>= 0.8.0'} + ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - mssql@11.0.1: - resolution: {integrity: sha512-KlGNsugoT90enKlR8/G36H0kTxPthDhmtNUCwEHvgRza5Cjpjoj+P2X6eMpFUDN7pFrJZsKadL4x990G8RBE1w==} - engines: {node: '>=18'} - hasBin: true - - multitars@0.2.4: - resolution: {integrity: sha512-XgLbg1HHchFauMCQPRwMj6MSyDd5koPlTA1hM3rUFkeXzGpjU/I9fP3to7yrObE9jcN8ChIOQGrM0tV0kUZaKg==} + multitars@1.0.0: + resolution: {integrity: sha512-H/J4fMLedtudftaYMOg7ajzLYgT3/rwbWVJbqr/iUgB8DQztn38ys5HOqI1CzSxx8QhXXwOOnnBvd4v3jG5+Mg==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} - native-duplexpair@1.0.0: - resolution: {integrity: sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==} + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -4744,16 +4780,16 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} - nocache@3.0.4: - resolution: {integrity: sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==} - engines: {node: '>=12.0.0'} + nitrogen@0.36.1: + resolution: {integrity: sha512-0Q/YQLyE1/iNRNfSEl6vSqXUJEaQQv0GboWYan+Kiy5Vr6kE/F3CrKnvrnDcR6cgFeIYN++eON+Qn3djDVHYvQ==} + hasBin: true - node-abi@3.89.0: - resolution: {integrity: sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==} + node-abi@3.94.0: + resolution: {integrity: sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==} engines: {node: '>=10'} - node-exports-info@1.6.0: - resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + node-exports-info@1.6.2: + resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==} engines: {node: '>= 0.4'} node-forge@1.4.0: @@ -4763,12 +4799,9 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.36: - resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} - - node-stream-zip@1.15.0: - resolution: {integrity: sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==} - engines: {node: '>=0.12.0'} + node-releases@2.0.51: + resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} + engines: {node: '>=18'} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -4788,16 +4821,12 @@ packages: nullthrows@1.1.1: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} - nwsapi@2.2.23: - resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} - - ob1@0.83.3: - resolution: {integrity: sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA==} - engines: {node: '>=20.19.4'} + nwsapi@2.2.24: + resolution: {integrity: sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==} - ob1@0.83.5: - resolution: {integrity: sha512-vNKPYC8L5ycVANANpF/S+WZHpfnRWKx/F3AYP4QMn6ZJTh+l2HOrId0clNkEmua58NB9vmI9Qh7YOoV/4folYg==} - engines: {node: '>=20.19.4'} + ob1@0.84.4: + resolution: {integrity: sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} @@ -4823,6 +4852,10 @@ packages: resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} engines: {node: '>= 0.4'} + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + object.values@1.2.1: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} @@ -4850,14 +4883,6 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - open@10.2.0: - resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} - engines: {node: '>=18'} - - open@6.4.0: - resolution: {integrity: sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==} - engines: {node: '>=8'} - open@7.4.2: resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} engines: {node: '>=8'} @@ -4874,10 +4899,6 @@ packages: resolution: {integrity: sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==} engines: {node: '>=6'} - ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} - own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} @@ -4943,6 +4964,9 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} @@ -4951,14 +4975,6 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-exists@5.0.0: - resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - path-expression-matcher@1.2.0: - resolution: {integrity: sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==} - engines: {node: '>=14.0.0'} - path-extra@1.0.3: resolution: {integrity: sha512-vYm3+GCkjUlT1rDvZnDVhNLXIRvwFPaN8ebHAFcuMJM/H0RBOPD7JrcldiNLd9AS3dhAyUHLa4Hny5wp1A+Ffw==} @@ -4981,6 +4997,12 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} + path-to-regexp@0.1.13: + resolution: {integrity: sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==} + + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -4988,8 +5010,8 @@ packages: resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} pidtree@0.5.0: @@ -5009,8 +5031,8 @@ packages: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} engines: {node: '>=8'} - plist@3.1.0: - resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} + plist@3.1.1: + resolution: {integrity: sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==} engines: {node: '>=10.4.0'} pngjs@3.4.0: @@ -5021,12 +5043,8 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - postcss@8.4.49: - resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + postcss@8.5.19: + resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} engines: {node: ^10 || ^12 || >=14} prebuild-install@7.1.3: @@ -5048,18 +5066,14 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - pretty-format@30.3.0: - resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==} + pretty-format@30.4.1: + resolution: {integrity: sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} proc-log@4.2.0: resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -5074,10 +5088,14 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - protobufjs@8.0.0: - resolution: {integrity: sha512-jx6+sE9h/UryaCZhsJWbJtTEy47yXoGNYI4z8ZaRncM0zBKeRqjO2JEcOUYwrYGb1WLhXM1FfMzW3annvFv0rw==} + protobufjs@8.7.1: + resolution: {integrity: sha512-agdGHrXNTv0IrYscJPDou/PlEJk1c/hBZ9o/B5NH2i/nSPtPqacNxzgwf1CebXxFMjMrZH5sqv9uQuw96aGt/A==} engines: {node: '>=12.0.0'} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + psl@1.15.0: resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} @@ -5091,8 +5109,8 @@ packages: pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - qs@6.15.0: - resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} + qs@6.15.3: + resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} engines: {node: '>=0.6'} query-string@7.1.3: @@ -5102,9 +5120,6 @@ packages: querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - queue@6.0.2: resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} @@ -5112,6 +5127,10 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} + raw-body@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + raw-body@3.0.2: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} @@ -5135,23 +5154,8 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-is@19.2.4: - resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==} - - react-native-background-actions@4.0.1: - resolution: {integrity: sha512-LADhnb4ag1oH5Lotq0j8K9e2cFmrafFyg2PCME88VkTjqDUgNcJonkNdMCTHN0N3fh+hwAA7nDR4Cxkj9Q8eCw==} - peerDependencies: - react-native: '>=0.47.0' - - react-native-config@1.6.1: - resolution: {integrity: sha512-HvKtxr6/Tq3iMdFx5REYZsjCtPi0RxQOMCs15+DqrUPTNFtWHuEuh+zw7fJp+dmuO79YMfdtlsPWIGTHtaXwjg==} - peerDependencies: - react: '*' - react-native: '*' - react-native-windows: '>=0.61' - peerDependenciesMeta: - react-native-windows: - optional: true + react-is@19.2.7: + resolution: {integrity: sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==} react-native-device-info@15.0.2: resolution: {integrity: sha512-dd71eXG2l3Cwp66IvKNadMTB8fhU3PEjyVddI97sYan+D4bgIAUmgGDhbSOFvHcGavksb2U17kiQYaDiK2WK2g==} @@ -5165,8 +5169,8 @@ packages: react-native-gesture-handler: '>=2.0.0' react-native-reanimated: '>=2.8.0' - react-native-drawer-layout@4.2.2: - resolution: {integrity: sha512-UG/PTTeyyr43KahbgoGyXri8LMO5USHY3/RUpeKBKwCc7xLVGnDLOVNSRrJw0dDc7YmPbmAyJ4oxp8nKboKKuw==} + react-native-drawer-layout@4.2.8: + resolution: {integrity: sha512-IswXoXEaQAnzUGo7tJyMPHP0qyUc/7zufpG+kpiStYegHpKQBBnlQoRdXkL1HbCa2o1ROWDhCSJ8XS0V4LNcjQ==} peerDependencies: react: '>= 18.2.0' react-native: '*' @@ -5186,24 +5190,8 @@ packages: react-native: '>=0.74.0' react-native-safe-area-context: '>=5.0.0' - react-native-file-access@3.2.0: - resolution: {integrity: sha512-3G0Ma3FvV99Ne7lbwHyZKqLMZVrpIr6fAlIYZQxkRDSZZJ1UtJlGi0VqbJXnJFB26a7C7PEPyVBoctmKr7DzaA==} - peerDependencies: - react: '*' - react-native: '*' - - react-native-file-access@4.0.2: - resolution: {integrity: sha512-6U+W9QiurFgrX0V3TOpQ99LOQ1JA9MO090keGD30Blz2mJXvkQ9urKYjybXwBaNja2JodLNq2okRVpYlf4yHww==} - peerDependencies: - react: '*' - react-native: '*' - react-native-macos: '*' - peerDependenciesMeta: - react-native-macos: - optional: true - - react-native-gesture-handler@2.30.1: - resolution: {integrity: sha512-xIUBDo5ktmJs++0fZlavQNvDEE4PsihWhSeJsJtoz4Q6p0MiTM9TgrTgfEgzRR36qGPytFoeq+ShLrVwGdpUdA==} + react-native-gesture-handler@2.32.0: + resolution: {integrity: sha512-uYIMOKlKENORq2SABE+jIjbPU+h5I/sQKcq2v16zRq848nwEp1fWRVwML4QWqijc8UcXJC25o54S8GQd4Mf2OA==} peerDependencies: react: '*' react-native: '*' @@ -5220,63 +5208,47 @@ packages: react: '*' react-native: '*' - react-native-lottie-splash-screen@1.1.2: - resolution: {integrity: sha512-5V4sk46UlU2MbRDlncT0O3qtBdqjxKfcW5/VXceU82CPcO0z0ItcaX34L74Zt5fqkR80OW1VCccrsIGly1y0lw==} - peerDependencies: - react-native: '>=0.57.0' - - react-native-mmkv@4.3.0: - resolution: {integrity: sha512-D1wB2ViMrm+0rs7FcbLoct/BV+qugASi+XAZT8MzXy5yl0CI0qxToh2LPnw9UENHrNefpfDZgE5FpMhIB37I5Q==} + react-native-mmkv@4.3.2: + resolution: {integrity: sha512-49OAyfkg0/TMWiWELZN6VuVQPZPhizwL4DTmp8b7B1md3dB/s3LH3mGfC3T+lp9W0y/rqxZMEnotLFTIbOAenQ==} peerDependencies: react: '*' react-native: '*' react-native-nitro-modules: '*' - react-native-nitro-modules@0.35.2: - resolution: {integrity: sha512-97cZcCh3ZAuWAfutel2Q3qLfc45XXh7F9Ei5tEjahP0kV3q8hQelwLIulKXmjN+f0JI5Zf/wCsfwwdVWYU2tKA==} + react-native-nitro-modules@0.36.1: + resolution: {integrity: sha512-kBv/VvKqAmkXAvP1DxJMC9b/fRhh7JdSO4EUnPP46hJjrIFeFR8AwKm8mYaKZEuF014M/TVdv2vomVUW0umsQQ==} peerDependencies: react: '*' react-native: '*' - react-native-pager-view@8.0.0: - resolution: {integrity: sha512-oAwlWT1lhTkIs9HhODnjNNl/owxzn9DP1MbP+az6OTUdgbmzA16Up83sBH8NRKwrH8rNm7iuWnX1qMqiiWOLhg==} + react-native-pager-view@8.0.4: + resolution: {integrity: sha512-9ASQA/gdQv63pESFbzyODgsY07kZoBjLqBRvZ/HNYn+h29XyOX2BICuvVmhemxw/pz3hKGqGEU31DDhDArwaRg==} peerDependencies: react: '*' react-native: '*' - react-native-paper@5.15.0: - resolution: {integrity: sha512-I/1CQLfW9VM0Oo5I5dQI/hjgf1I6q2S1wwgzAdsv6whAQ3zO97GWHwtgNh9se9j8zBOJ86afPTQKxxUL0IJd9A==} + react-native-paper@5.15.3: + resolution: {integrity: sha512-GEyNTmWElIZgnYw09AjjCNupRYzCmP79uAAyGSyCEUZz7KBz1wtJcC0wVUkozR1Rn3PK/td/9LlR6+F1hzmYvA==} peerDependencies: react: '*' react-native: '*' react-native-safe-area-context: '*' - react-native-reanimated@4.3.0: - resolution: {integrity: sha512-HOTTPdKtddXTOsmQxDASXEwLS3lqEHrKERD3XOgzSqWJ7L3x81Pnx7mTcKx1FKdkgomMug/XSmm1C6Z7GIowxA==} + react-native-reanimated@4.5.2: + resolution: {integrity: sha512-2XF4a2VyKTy3bxugm9teClxwQ9X4pIkFmoKxIwMQqJdhoFQvjN7In/faKWtjQ8bxTYG+uIJyl6F7oxEwkngjKA==} peerDependencies: react: '*' - react-native: 0.81 - 0.85 - react-native-worklets: 0.8.x + react-native: 0.83 - 0.86 + react-native-worklets: 0.10.x - 0.11.x - react-native-saf-x@2.2.3: - resolution: {integrity: sha512-aPQbUfuHy8txZ/+t8Daoy8G3aRZ5H+0/SH9mJdb6quvxvt48jeIjq2n82anV77tSCXyX0du955PrtWpRzYliPQ==} + react-native-safe-area-context@5.8.0: + resolution: {integrity: sha512-t+ZsAVzY/wWzzx34vqGbo3/as9EEESJdbyZNL7Yg5EYX+toYMtMqFoDDCvqZUi35eeGVsXc6pAaEk4edMwbuCQ==} peerDependencies: react: '*' react-native: '*' - react-native-safe-area-context@5.7.0: - resolution: {integrity: sha512-/9/MtQz8ODphjsLdZ+GZAIcC/RtoqW9EeShf7Uvnfgm/pzYrJ75y3PV/J1wuAV1T5Dye5ygq4EAW20RoBq0ABQ==} - peerDependencies: - react: '*' - react-native: '*' - - react-native-safe-modules@1.0.3: - resolution: {integrity: sha512-DUxti4Z+AgJ/ZsO5U7p3uSCUBko8JT8GvFlCeOXk9bMd+4qjpoDvMYpfbixXKgL88M+HwmU/KI1YFN6gsQZyBA==} - peerDependencies: - react-native: '*' - - react-native-screens@4.24.0: - resolution: {integrity: sha512-SyoiGaDofiyGPFrUkn1oGsAzkRuX1JUvTD9YQQK3G1JGQ5VWkvHgYSsc1K9OrLsDQxN7NmV71O0sHCAh8cBetA==} + react-native-screens@4.26.2: + resolution: {integrity: sha512-2XnWsZToKj76trGtEZzx5ELD/qOICFEprEeUntImmitQFVUkea27fiWdUSITArI356Y1qynpXZINW+Unbhky/A==} peerDependencies: react: '*' react-native: '*' @@ -5287,8 +5259,14 @@ packages: prop-types: '>=15.6.0' react-native-linear-gradient: '>=2.4.0' - react-native-tab-view@4.3.0: - resolution: {integrity: sha512-qPMF75uz/7+MuVG2g+YETdGMzlWZnhC6iI4h/7EBbwIBwNBIBi2z4OA6KhY3IOOBwGHXEIz5IyA6doDqifYBHg==} + react-native-svg@15.15.4: + resolution: {integrity: sha512-boT/vIRgj6zZKBpfTPJJiYWMbZE9duBMOwPK6kCSTgxsS947IFMOq9OgIFkpWZTB7t229H24pDRkh3W9ZK/J1A==} + peerDependencies: + react: '*' + react-native: '*' + + react-native-tab-view@4.3.2: + resolution: {integrity: sha512-QCHX9bTGMeME5pt4jJ1/H7oshX7Z2nA/Ts3m7FpKUbWfPPIL8rlNLZwkedgV273Eqau8jj8deRtfSZl9LCctCw==} peerDependencies: react: '>= 18.2.0' react-native: '*' @@ -5299,40 +5277,31 @@ packages: peerDependencies: react-native: '*' - react-native-webview@13.16.1: - resolution: {integrity: sha512-If0eHhoEdOYDcHsX+xBFwHMbWBGK1BvGDQDQdVkwtSIXiq1uiqjkpWVP2uQ1as94J0CzvFE9PUNDuhiX0Z6ubw==} + react-native-webview@13.17.0: + resolution: {integrity: sha512-nu0OCC4xa9K5nihup2l2eDlOtQZ4mgMf0LN0EuP0/0JsLtqmMjGihIEO3NZ/rDHZ8uErORYUTp73naVeH0tG1g==} peerDependencies: react: '*' react-native: '*' - react-native-worklets@0.8.1: - resolution: {integrity: sha512-oWP/lStsAHU6oYCaWDXrda/wOHVdhusQJz1e6x9gPnXdFf4ndNDAOtWCmk2zGrAnlapfyA3rM6PCQq94mPg9cw==} + react-native-worklets@0.10.2: + resolution: {integrity: sha512-LX27ejYI8veeDp59Z3rjo2pYyPa9euzSH8GUlem7cnNqfsDtGum8PQpkbzrqhLsWH0CjdeHR7p3sncCyYbwaVw==} peerDependencies: '@babel/core': '*' '@react-native/metro-config': '*' react: '*' - react-native: 0.81 - 0.85 - - react-native-zip-archive@6.1.2: - resolution: {integrity: sha512-LcJomSY/6O3KHy/LF6Gb7F/yRJiZJ0lTlPQPbfeOHBQzfvqNJFJZ8x6HrdeYeokFf/UGB5bY7jfh4es6Y/PhBA==} - peerDependencies: - react: '>=16.8.6' - react-native: '>=0.60.0' - - react-native-zip-archive@7.0.2: - resolution: {integrity: sha512-msCRJMcwH6NVZ2/zoC+1nvA0wlpYRnMxteQywS9nt4BzXn48tZpaVtE519QEZn0xe3ygvgsWx5cdPoE9Jx3bsg==} - peerDependencies: - react: '>=16.8.6' - react-native: '>=0.60.0' + react-native: 0.83 - 0.86 - react-native@0.83.4: - resolution: {integrity: sha512-H5Wco3UJyY6zZsjoBayY8RM9uiAEQ3FeG4G2NAt+lr9DO43QeqPlVe9xxxYEukMkEmeIhNjR70F6bhXuWArOMQ==} - engines: {node: '>= 20.19.4'} + react-native@0.86.0: + resolution: {integrity: sha512-17ALh/dd6AO4pgOVmOO5Axll5PbErEo3XFyLokyzW6usyi+OShIEPwUW26wLPlhVifgSOIfECCH0WN+0IqtJ1w==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} hasBin: true peerDependencies: + '@react-native/jest-preset': 0.86.0 '@types/react': ^19.1.1 - react: ^19.2.0 + react: ^19.2.3 peerDependenciesMeta: + '@react-native/jest-preset': + optional: true '@types/react': optional: true @@ -5340,28 +5309,19 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} - react-test-renderer@19.2.0: - resolution: {integrity: sha512-zLCFMHFE9vy/w3AxO0zNxy6aAupnCuLSVOJYDe/Tp+ayGI1f2PLQsFVPANSD42gdSbmYx5oN+1VWDhcXtq7hAQ==} - peerDependencies: - react: ^19.2.0 - - react-test-renderer@19.2.4: - resolution: {integrity: sha512-Ttl5D7Rnmi6JGMUpri4UjB4BAN0FPs4yRDnu2XSsigCWOLm11o8GwRlVsh27ER+4WFqsGtrBuuv5zumUaRCmKw==} + react-test-renderer@19.2.3: + resolution: {integrity: sha512-TMR1LnSFiWZMJkCgNf5ATSvAheTT2NvKIwiVwdBPHxjBI7n/JbWd4gaZ16DVd9foAXdvDz+sB5yxZTwMjPRxpw==} peerDependencies: - react: ^19.2.4 + react: ^19.2.3 - react@19.2.4: - resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - readable-stream@4.7.0: - resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -5391,17 +5351,14 @@ packages: regjsgen@0.8.0: resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} - regjsparser@0.13.0: - resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} + regjsparser@0.13.2: + resolution: {integrity: sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==} hasBin: true require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - require-resolve@0.0.2: resolution: {integrity: sha512-eafQVaxdQsWUB8HybwognkdcIdKdQdQBwTxH48FuE6WI0owZGKp63QYr1MRp73PoX0AcyB7MDapZThYUY8FD0A==} @@ -5433,13 +5390,13 @@ packages: resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} engines: {node: '>=10'} - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true - resolve@2.0.0-next.6: - resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} + resolve@2.0.0-next.7: + resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==} engines: {node: '>= 0.4'} hasBin: true @@ -5451,35 +5408,26 @@ packages: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} rtl-detect@1.1.2: resolution: {integrity: sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==} - run-applescript@7.1.0: - resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} - engines: {node: '>=18'} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - safe-array-concat@1.1.3: - resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -5494,8 +5442,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sanitize-html@2.17.2: - resolution: {integrity: sha512-EnffJUl46VE9uvZ0XeWzObHLurClLlT12gsOk1cHyP2Ol1P0BnBnsXmShlBmWVJM+dKieQI68R0tsPY5m/B+Jg==} + sanitize-html@2.17.6: + resolution: {integrity: sha512-M4bo9tfv1yfhQZZKkc6dL07ALrGJtfvNOuhX3hU9AVPR/uPQ+nKOJBqTYc7LfMQblTW04mtSWDJWEyLvygJsLA==} + engines: {node: '>=22.12.0'} sax@1.6.0: resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} @@ -5512,8 +5461,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true @@ -5521,6 +5470,10 @@ packages: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + serialize-error@2.1.0: resolution: {integrity: sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==} engines: {node: '>=0.10.0'} @@ -5529,12 +5482,13 @@ packages: resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} engines: {node: '>= 0.8.0'} + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} - set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -5562,12 +5516,12 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.3: - resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + shell-quote@1.10.0: + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} engines: {node: '>= 0.4'} - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} side-channel-map@1.0.1: @@ -5578,8 +5532,8 @@ packages: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} signal-exit@3.0.7: @@ -5608,10 +5562,6 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} - slice-ansi@2.1.0: - resolution: {integrity: sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==} - engines: {node: '>=6'} - slice-ansi@3.0.0: resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} engines: {node: '>=8'} @@ -5624,8 +5574,8 @@ packages: resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} engines: {node: '>=12'} - slugify@1.6.8: - resolution: {integrity: sha512-HVk9X1E0gz3mSpoi60h/saazLKXKaZThMLU3u/aNwoYn8/xQyX2MGxL0ui2eaokkD7tF+Zo+cKTHUbe1mmmGzA==} + slugify@1.6.9: + resolution: {integrity: sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==} engines: {node: '>=8.0.0'} source-map-js@1.2.1: @@ -5657,8 +5607,8 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} stack-generator@2.0.10: resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} @@ -5680,6 +5630,11 @@ packages: resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} engines: {node: '>=6'} + standard-navigation@0.0.8: + resolution: {integrity: sha512-TyVbo7INUDWtsUWDFn8RR7kwR87U0S4xHfLfbbnyeC581TmmyqQ+eM+nPw8rQTSD8QitRVcYfPaSHr/QJiUy1g==} + peerDependencies: + react: '*' + statuses@1.5.0: resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} engines: {node: '>= 0.6'} @@ -5696,13 +5651,16 @@ packages: resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} engines: {node: '>= 0.10.0'} + stream-chain@2.2.5: + resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} + + stream-json@1.9.1: + resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==} + strict-uri-encode@2.0.0: resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} engines: {node: '>=4'} - strict-url-sanitise@0.0.1: - resolution: {integrity: sha512-nuFtF539K8jZg3FjaWH/L8eocCR6gegz5RDOsaWxfdbF5Jqr2VXWxZayjTwUzsWJDC91k2EbnJXp6FuWW+Z4hg==} - string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -5726,6 +5684,10 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} @@ -5733,12 +5695,12 @@ packages: string.prototype.repeat@1.0.0: resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} - string.prototype.trim@1.2.10: - resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} engines: {node: '>= 0.4'} - string.prototype.trimend@1.0.9: - resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: @@ -5760,6 +5722,10 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} @@ -5780,9 +5746,6 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - strnum@2.2.2: - resolution: {integrity: sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==} - structured-headers@0.4.1: resolution: {integrity: sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==} @@ -5813,31 +5776,19 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tar-fs@2.1.4: - resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} + tar-fs@2.1.5: + resolution: {integrity: sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==} tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} - tarn@3.0.2: - resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==} - engines: {node: '>=8.0.0'} - - tedious@18.6.2: - resolution: {integrity: sha512-g7jC56o3MzLkE3lHkaFe2ZdOVFBahq5bsB60/M4NYUbocw/MCrS89IOEQUFr+ba6pb8ZHczZ/VqCyYeYq0xBAg==} - engines: {node: '>=18'} - - tedious@19.2.1: - resolution: {integrity: sha512-pk1Q16Yl62iocuQB+RWbg6rFUFkIyzqOFQ6NfysCltRvQqKwfurgj8v/f2X+CKvDhSL4IJ0cCOfCHDg9PWEEYA==} - engines: {node: '>=18.17'} - terminal-link@2.1.1: resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} engines: {node: '>=8'} - terser@5.46.1: - resolution: {integrity: sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==} + terser@5.49.0: + resolution: {integrity: sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==} engines: {node: '>=10'} hasBin: true @@ -5845,17 +5796,14 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - throat@5.0.0: resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==} through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} tmpl@1.0.5: @@ -5886,6 +5834,12 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-morph@28.0.0: + resolution: {integrity: sha512-Wp3tnZ2bzwxyTZMtgWVzXDfm7lB1Drz+y9DmmYH/L702PQhPyVrp3pkou3yIz4qjS14GY9kcpmLiOOMvl8oG1g==} + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -5900,10 +5854,6 @@ packages: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -5912,10 +5862,14 @@ packages: resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} engines: {node: '>=8'} - type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} + type-is@2.1.0: + resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} + engines: {node: '>= 18'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -5928,12 +5882,12 @@ packages: resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} engines: {node: '>= 0.4'} - typed-array-length@1.0.7: - resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} engines: {node: '>= 0.4'} - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true @@ -5941,8 +5895,12 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - undici-types@7.18.2: - resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + undici@6.28.0: + resolution: {integrity: sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==} + engines: {node: '>=18.17'} unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} @@ -5960,14 +5918,10 @@ packages: resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} engines: {node: '>=4'} - unicorn-magic@0.1.0: - resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} @@ -5976,6 +5930,9 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unrs-resolver@1.12.2: + resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -6010,10 +5967,7 @@ packages: uuid@7.0.3: resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==} - hasBin: true - - uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true v8-to-istanbul@9.3.0: @@ -6064,8 +6018,8 @@ packages: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} engines: {node: '>=12'} - whatwg-url-minimum@0.1.1: - resolution: {integrity: sha512-u2FNVjFVFZhdjb502KzXy1gKn1mEisQRJssmSJT8CPhZdZa0AP6VCbWlXERKyGu0l09t0k50FiDiralpGhBxgA==} + whatwg-url-minimum@0.1.2: + resolution: {integrity: sha512-XPEm0XFQWNVG292lII1PrRRJl3sItrs7CettZ4ncYxuDVpLyy+NwlGyut2hXI0JswcJUxeCH+CyOJK0ZzAXD6A==} whatwg-url-without-unicode@8.0.0-3: resolution: {integrity: sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==} @@ -6087,11 +6041,8 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} - which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} engines: {node: '>= 0.4'} which@2.0.2: @@ -6111,6 +6062,10 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -6118,19 +6073,8 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - ws@6.2.3: - resolution: {integrity: sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@7.5.10: - resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + ws@7.5.13: + resolution: {integrity: sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==} engines: {node: '>=8.3.0'} peerDependencies: bufferutil: ^4.0.1 @@ -6141,8 +6085,8 @@ packages: utf-8-validate: optional: true - ws@8.20.0: - resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} + ws@8.21.1: + resolution: {integrity: sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -6153,10 +6097,6 @@ packages: utf-8-validate: optional: true - wsl-utils@0.1.0: - resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} - engines: {node: '>=18'} - x-path@0.0.2: resolution: {integrity: sha512-zQ4WFI0XfJN1uEkkrB19Y4TuXOlHqKSxUJo0Yt+axPjRm8tCG6SJ6+Wo3/+Kjg4c2c8IvBXuJ0uYoshxNn4qMw==} @@ -6183,9 +6123,6 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -6197,27 +6134,27 @@ packages: resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} engines: {node: '>= 6'} - yaml@2.8.3: - resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} hasBin: true - yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} engines: {node: '>=12'} + yargs@18.0.0: + resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -6235,174 +6172,48 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} - -snapshots: + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} - '@azure-rest/core-client@2.5.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-rest-pipeline': 1.23.0 - '@azure/core-tracing': 1.3.1 - '@typespec/ts-http-runtime': 0.3.4 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/abort-controller@2.1.2': - dependencies: - tslib: 2.8.1 - - '@azure/core-auth@1.10.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-util': 1.13.1 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-client@1.10.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-rest-pipeline': 1.23.0 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-http-compat@2.3.2(@azure/core-client@1.10.1)(@azure/core-rest-pipeline@1.23.0)': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.23.0 - - '@azure/core-lro@2.7.2': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-paging@1.6.2': - dependencies: - tslib: 2.8.1 - - '@azure/core-rest-pipeline@1.23.0': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - '@typespec/ts-http-runtime': 0.3.4 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-tracing@1.3.1': - dependencies: - tslib: 2.8.1 - - '@azure/core-util@1.13.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@typespec/ts-http-runtime': 0.3.4 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/identity@4.13.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.23.0 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - '@azure/msal-browser': 5.6.2 - '@azure/msal-node': 5.1.1 - open: 10.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/keyvault-common@2.0.0': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.23.0 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/keyvault-keys@4.10.0(@azure/core-client@1.10.1)': - dependencies: - '@azure-rest/core-client': 2.5.1 - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-http-compat': 2.3.2(@azure/core-client@1.10.1)(@azure/core-rest-pipeline@1.23.0) - '@azure/core-lro': 2.7.2 - '@azure/core-paging': 1.6.2 - '@azure/core-rest-pipeline': 1.23.0 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/keyvault-common': 2.0.0 - '@azure/logger': 1.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - '@azure/core-client' - - supports-color - - '@azure/logger@1.3.0': - dependencies: - '@typespec/ts-http-runtime': 0.3.4 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/msal-browser@5.6.2': - dependencies: - '@azure/msal-common': 16.4.0 - - '@azure/msal-common@16.4.0': {} + zustand@5.0.14: + resolution: {integrity: sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==} + engines: {node: '>=12.20.0'} + 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 - '@azure/msal-node@5.1.1': - dependencies: - '@azure/msal-common': 16.4.0 - jsonwebtoken: 9.0.3 - uuid: 8.3.2 +snapshots: - '@babel/code-frame@7.29.0': + '@babel/code-frame@7.29.7': dependencies: - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.29.0': {} + '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.0': + '@babel/core@7.29.7(supports-color@9.4.0)': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.29.2 - '@babel/parser': 7.29.2 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@9.4.0) + '@babel/types': 7.29.7 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3(supports-color@9.4.0) @@ -6412,871 +6223,863 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/eslint-parser@7.28.6(@babel/core@7.29.0)(eslint@8.57.1)': + '@babel/generator@7.29.7': dependencies: - '@babel/core': 7.29.0 - '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 - eslint: 8.57.1 - eslint-visitor-keys: 2.1.0 - semver: 6.3.1 - - '@babel/generator@7.29.1': - dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.27.3': + '@babel/helper-annotate-as-pure@7.29.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 - '@babel/helper-compilation-targets@7.28.6': + '@babel/helper-compilation-targets@7.29.7': dependencies: - '@babel/compat-data': 7.29.0 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.2 + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.6 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)': + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@9.4.0) + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@9.4.0) + '@babel/traverse': 7.29.7(supports-color@9.4.0) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0)': + '@babel/helper-create-regexp-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-annotate-as-pure': 7.29.7 regexpu-core: 6.4.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.0)': + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 debug: 4.4.3(supports-color@9.4.0) lodash.debounce: 4.0.8 - resolve: 1.22.11 + resolve: 1.22.12 transitivePeerDependencies: - supports-color - '@babel/helper-globals@7.28.0': {} + '@babel/helper-globals@7.29.7': {} - '@babel/helper-member-expression-to-functions@7.28.5': + '@babel/helper-member-expression-to-functions@7.29.7(supports-color@9.4.0)': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.29.7(supports-color@9.4.0) + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.28.6': + '@babel/helper-module-imports@7.29.7(supports-color@9.4.0)': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.29.7(supports-color@9.4.0) + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-module-imports': 7.29.7(supports-color@9.4.0) + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/helper-optimise-call-expression@7.27.1': + '@babel/helper-optimise-call-expression@7.29.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 - '@babel/helper-plugin-utils@7.28.6': {} + '@babel/helper-plugin-utils@7.29.7': {} - '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0)': + '@babel/helper-remap-async-to-generator@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-wrap-function': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-wrap-function': 7.29.7(supports-color@9.4.0) + '@babel/traverse': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@9.4.0) + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + '@babel/helper-skip-transparent-expression-wrappers@7.29.7(supports-color@9.4.0)': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.29.7(supports-color@9.4.0) + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-validator-option@7.29.7': {} - '@babel/helper-wrap-function@7.28.6': + '@babel/helper-wrap-function@7.29.7(supports-color@9.4.0)': dependencies: - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@9.4.0) + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helpers@7.29.2': + '@babel/helpers@7.29.7': dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 - '@babel/parser@7.29.2': + '@babel/parser@7.29.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': + dependencies: + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@9.4.0) + transitivePeerDependencies: + - supports-color - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@9.4.0) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-decorators@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-proposal-decorators@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-decorators': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-decorators': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-export-default-from@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-proposal-export-default-from@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-decorators@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-decorators@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-export-default-from@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-export-default-from@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-flow@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-flow@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-assertions@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-arrow-functions@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-async-generator-functions@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/traverse': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-async-to-generator@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-module-imports': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-block-scoped-functions@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-block-scoping@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-class-properties@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-class-static-block@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-classes@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-globals': 7.28.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/traverse': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-computed-properties@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/template': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/template': 7.29.7 - '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0)': + '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-dotall-regex@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-duplicate-keys@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-dynamic-import@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-explicit-resource-management@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-exponentiation-operator@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-export-namespace-from@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-flow-strip-types@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-flow': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) - '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-for-of@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-function-name@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-json-strings@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-literals@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-logical-assignment-operators@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-member-expression-literals@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-amd@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-systemjs@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-umd@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-new-target@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-numeric-separator@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-object-rest-spread@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) - '@babel/traverse': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/traverse': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-object-super@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-optional-catch-binding@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-optional-chaining@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.0)': + '@babel/plugin-transform-parameters@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-private-methods@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-private-property-in-object@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-property-literals@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.29.0)': + '@babel/plugin-transform-react-display-name@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx-development@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/types': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-module-imports': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-react-pure-annotations@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-regenerator@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-regexp-modifiers@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-reserved-words@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-runtime@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) - babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.0) - babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-module-imports': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-shorthand-properties@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-spread@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - - '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/preset-env@7.29.2(@babel/core@7.29.0)': - dependencies: - '@babel/compat-data': 7.29.0 - '@babel/core': 7.29.0 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0) - '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.0) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) - '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) - '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0) - babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) - babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.0) - babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) + + '@babel/plugin-transform-sticky-regex@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': + dependencies: + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-template-literals@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': + dependencies: + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-typeof-symbol@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': + dependencies: + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': + dependencies: + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@9.4.0) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': + dependencies: + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-property-regex@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': + dependencies: + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-regex@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': + dependencies: + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-sets-regex@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))': + dependencies: + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-import-assertions': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-block-scoped-functions': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-computed-properties': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-dotall-regex': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-duplicate-keys': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-dynamic-import': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-explicit-resource-management': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-exponentiation-operator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-function-name': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-json-strings': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-literals': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-member-expression-literals': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-modules-amd': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-modules-systemjs': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-modules-umd': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-new-target': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-numeric-separator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-object-super': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-property-literals': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-regexp-modifiers': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-reserved-words': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-spread': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-sticky-regex': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-typeof-symbol': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-unicode-escapes': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-unicode-property-regex': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-unicode-sets-regex': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.7(supports-color@9.4.0)) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) core-js-compat: 3.49.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/types': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/types': 7.29.7 esutils: 2.0.3 - '@babel/preset-react@7.28.5(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.29.0) - transitivePeerDependencies: - - supports-color - - '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': + '@babel/preset-typescript@7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/runtime@7.29.2': {} + '@babel/runtime@7.29.7': {} - '@babel/template@7.28.6': + '@babel/template@7.29.7': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 - '@babel/traverse@7.29.0': + '@babel/traverse@7.29.7(supports-color@9.4.0)': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.2 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 debug: 4.4.3(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@babel/types@7.29.0': + '@babel/types@7.29.7': dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 '@bcoe/v8-coverage@0.2.3': {} - '@callstack/react-theme-provider@3.0.9(react@19.2.4)': + '@callstack/react-theme-provider@3.0.9(react@19.2.3)': dependencies: deepmerge: 3.3.0 hoist-non-react-statics: 3.3.2 - react: 19.2.4 + react: 19.2.3 + + '@drizzle-team/brocli@0.11.0': {} - '@cd-z/epub-constructor@3.0.3': + '@egjs/hammerjs@2.0.17': dependencies: - sanitize-html: 2.17.2 + '@types/hammerjs': 2.0.46 - '@cd-z/react-native-epub-creator@3.0.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@emnapi/core@1.10.0': dependencies: - '@cd-z/epub-constructor': 3.0.3 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-file-access: 3.2.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-saf-x: 2.2.3(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-zip-archive: 6.1.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true - '@drizzle-team/brocli@0.11.0': {} + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true - '@egjs/hammerjs@2.0.17': + '@emnapi/wasi-threads@1.2.1': dependencies: - '@types/hammerjs': 2.0.46 + tslib: 2.8.1 + optional: true '@esbuild/aix-ppc64@0.25.12': optional: true @@ -7356,91 +7159,115 @@ snapshots: '@esbuild/win32-x64@0.25.12': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))': dependencies: - eslint: 8.57.1 + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/eslintrc@2.1.4': + '@eslint/config-array@0.21.2(supports-color@9.4.0)': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3(supports-color@9.4.0) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': dependencies: - ajv: 6.14.0 + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.6(supports-color@9.4.0)': + dependencies: + ajv: 6.15.0 debug: 4.4.3(supports-color@9.4.0) - espree: 9.6.1 - globals: 13.24.0 + espree: 10.4.0 + globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.1 + js-yaml: 4.3.0 minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@8.57.1': {} + '@eslint/js@9.39.5': {} + + '@eslint/object-schema@2.1.7': {} - '@expo/cli@55.0.19(@expo/dom-webview@55.0.3)(expo-constants@55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3))(expo-font@55.0.4(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@expo/cli@57.0.9(2d3b22c2103703c17c2fb47b72790f37)': dependencies: '@expo/code-signing-certificates': 0.0.6 - '@expo/config': 55.0.11(typescript@5.9.3) - '@expo/config-plugins': 55.0.7 - '@expo/devcert': 1.2.1 - '@expo/env': 2.1.1 - '@expo/image-utils': 0.8.12 - '@expo/json-file': 10.0.12 - '@expo/log-box': 55.0.8(@expo/dom-webview@55.0.3)(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@expo/metro': 54.2.0 - '@expo/metro-config': 55.0.11(expo@55.0.9)(typescript@5.9.3) - '@expo/osascript': 2.4.2 - '@expo/package-manager': 1.10.3 - '@expo/plist': 0.5.2 - '@expo/prebuild-config': 55.0.11(expo@55.0.9)(typescript@5.9.3) - '@expo/require-utils': 55.0.3(typescript@5.9.3) - '@expo/router-server': 55.0.11(expo-constants@55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3))(expo-font@55.0.4(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(expo-server@55.0.6)(expo@55.0.9)(react@19.2.4) - '@expo/schema-utils': 55.0.2 - '@expo/spawn-async': 1.7.2 - '@expo/ws-tunnel': 1.0.6 - '@expo/xcpretty': 4.4.1 - '@react-native/dev-middleware': 0.83.4 + '@expo/config': 57.0.5(supports-color@9.4.0)(typescript@6.0.3) + '@expo/config-plugins': 57.0.5(supports-color@9.4.0)(typescript@6.0.3) + '@expo/devcert': 1.2.1(supports-color@9.4.0) + '@expo/env': 2.4.2(supports-color@9.4.0) + '@expo/image-utils': 0.11.3(supports-color@9.4.0)(typescript@6.0.3) + '@expo/inline-modules': 0.1.3(supports-color@9.4.0)(typescript@6.0.3) + '@expo/json-file': 11.0.1 + '@expo/log-box': 57.0.1(@expo/dom-webview@57.0.1)(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + '@expo/metro': 56.0.0(supports-color@9.4.0) + '@expo/metro-config': 57.0.6(expo@57.0.7)(supports-color@9.4.0)(typescript@6.0.3) + '@expo/metro-file-map': 57.0.1(supports-color@9.4.0) + '@expo/osascript': 2.7.1 + '@expo/package-manager': 1.13.1 + '@expo/plist': 0.8.1 + '@expo/prebuild-config': 57.0.8(supports-color@9.4.0)(typescript@6.0.3) + '@expo/require-utils': 57.0.3(supports-color@9.4.0)(typescript@6.0.3) + '@expo/router-server': 57.0.3(expo-constants@57.0.6(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(supports-color@9.4.0))(expo-font@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(expo-server@57.0.1)(expo@57.0.7)(react@19.2.3)(supports-color@9.4.0) + '@expo/schema-utils': 57.0.2 + '@expo/spawn-async': 1.8.0 + '@expo/ws-tunnel': 2.0.0(ws@8.21.1) + '@expo/xcpretty': 4.4.4 + '@react-native/dev-middleware': 0.86.0(supports-color@9.4.0) accepts: 1.3.8 + agent-cli-detector: 0.1.3 arg: 5.0.2 - better-opn: 3.0.2 bplist-creator: 0.1.0 bplist-parser: 0.3.2 chalk: 4.1.2 ci-info: 3.9.0 - compression: 1.8.1 - connect: 3.7.0 + compression: 1.8.1(supports-color@9.4.0) + connect: 3.7.0(supports-color@9.4.0) debug: 4.4.3(supports-color@9.4.0) - dnssd-advertise: 1.1.4 - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - expo-server: 55.0.6 + dnssd-advertise: 1.1.6 + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + expo-server: 57.0.1 fetch-nodeshim: 0.4.10 getenv: 2.0.0 glob: 13.0.6 - lan-network: 0.2.0 - multitars: 0.2.4 + lan-network: 0.2.1 + multitars: 1.0.0 node-forge: 1.4.0 npm-package-arg: 11.0.3 ora: 3.4.0 - picomatch: 4.0.4 + picomatch: 4.0.5 pretty-format: 29.7.0 progress: 2.0.3 prompts: 2.4.2 resolve-from: 5.0.0 - semver: 7.7.4 - send: 0.19.2 - slugify: 1.6.8 - source-map-support: 0.5.21 + semver: 7.8.5 + send: 0.19.2(supports-color@9.4.0) + slugify: 1.6.9 stacktrace-parser: 0.1.11 structured-headers: 0.4.1 terminal-link: 2.1.1 toqr: 0.1.1 wrap-ansi: 7.0.0 - ws: 8.20.0 + ws: 8.21.1 zod: 3.25.76 optionalDependencies: - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) transitivePeerDependencies: - '@expo/dom-webview' - '@expo/metro-runtime' @@ -7458,64 +7285,64 @@ snapshots: dependencies: node-forge: 1.4.0 - '@expo/config-plugins@55.0.7': + '@expo/config-plugins@57.0.5(supports-color@9.4.0)(typescript@6.0.3)': dependencies: - '@expo/config-types': 55.0.5 - '@expo/json-file': 10.0.12 - '@expo/plist': 0.5.2 + '@expo/config-types': 57.0.2 + '@expo/json-file': 11.0.1 + '@expo/plist': 0.8.1 + '@expo/require-utils': 57.0.3(supports-color@9.4.0)(typescript@6.0.3) '@expo/sdk-runtime-versions': 1.0.0 chalk: 4.1.2 debug: 4.4.3(supports-color@9.4.0) getenv: 2.0.0 glob: 13.0.6 - resolve-from: 5.0.0 - semver: 7.7.4 - slugify: 1.6.8 + semver: 7.8.5 + slugify: 1.6.9 xcode: 3.0.1 xml2js: 0.6.0 transitivePeerDependencies: - supports-color + - typescript - '@expo/config-types@55.0.5': {} + '@expo/config-types@57.0.2': {} - '@expo/config@55.0.11(typescript@5.9.3)': + '@expo/config@57.0.5(supports-color@9.4.0)(typescript@6.0.3)': dependencies: - '@expo/config-plugins': 55.0.7 - '@expo/config-types': 55.0.5 - '@expo/json-file': 10.0.12 - '@expo/require-utils': 55.0.3(typescript@5.9.3) + '@expo/config-plugins': 57.0.5(supports-color@9.4.0)(typescript@6.0.3) + '@expo/config-types': 57.0.2 + '@expo/json-file': 11.0.1 + '@expo/require-utils': 57.0.3(supports-color@9.4.0)(typescript@6.0.3) deepmerge: 4.3.1 getenv: 2.0.0 glob: 13.0.6 - resolve-from: 5.0.0 resolve-workspace-root: 2.0.1 - semver: 7.7.4 - slugify: 1.6.8 + semver: 7.8.5 + slugify: 1.6.9 transitivePeerDependencies: - supports-color - typescript - '@expo/devcert@1.2.1': + '@expo/devcert@1.2.1(supports-color@9.4.0)': dependencies: '@expo/sudo-prompt': 9.3.2 - debug: 3.2.7 + debug: 3.2.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color - '@expo/devtools@55.0.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@expo/devtools@57.0.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: chalk: 4.1.2 optionalDependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - '@expo/dom-webview@55.0.3(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@expo/dom-webview@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - '@expo/env@2.1.1': + '@expo/env@2.4.2(supports-color@9.4.0)': dependencies: chalk: 4.1.2 debug: 4.4.3(supports-color@9.4.0) @@ -7523,10 +7350,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/fingerprint@0.16.6': + '@expo/expo-modules-macros-plugin@0.6.1': {} + + '@expo/fingerprint@0.20.5(supports-color@9.4.0)': dependencies: - '@expo/env': 2.1.1 - '@expo/spawn-async': 1.7.2 + '@expo/env': 2.4.2(supports-color@9.4.0) + '@expo/spawn-async': 1.8.0 arg: 5.0.2 chalk: 4.1.2 debug: 4.4.3(supports-color@9.4.0) @@ -7535,207 +7364,234 @@ snapshots: ignore: 5.3.2 minimatch: 10.2.5 resolve-from: 5.0.0 - semver: 7.7.4 + semver: 7.8.5 transitivePeerDependencies: - supports-color - '@expo/image-utils@0.8.12': + '@expo/image-utils@0.11.3(supports-color@9.4.0)(typescript@6.0.3)': dependencies: - '@expo/spawn-async': 1.7.2 + '@expo/require-utils': 57.0.3(supports-color@9.4.0)(typescript@6.0.3) + '@expo/spawn-async': 1.8.0 chalk: 4.1.2 getenv: 2.0.0 jimp-compact: 0.16.1 parse-png: 2.1.0 - resolve-from: 5.0.0 - semver: 7.7.4 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + - typescript - '@expo/json-file@10.0.12': + '@expo/inline-modules@0.1.3(supports-color@9.4.0)(typescript@6.0.3)': dependencies: - '@babel/code-frame': 7.29.0 + '@expo/config-plugins': 57.0.5(supports-color@9.4.0)(typescript@6.0.3) + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/json-file@11.0.1': + dependencies: + '@babel/code-frame': 7.29.7 json5: 2.2.3 - '@expo/local-build-cache-provider@55.0.7(typescript@5.9.3)': + '@expo/local-build-cache-provider@57.0.4(supports-color@9.4.0)(typescript@6.0.3)': dependencies: - '@expo/config': 55.0.11(typescript@5.9.3) + '@expo/config': 57.0.5(supports-color@9.4.0)(typescript@6.0.3) chalk: 4.1.2 transitivePeerDependencies: - supports-color - typescript - '@expo/log-box@55.0.8(@expo/dom-webview@55.0.3)(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@expo/log-box@57.0.1(@expo/dom-webview@57.0.1)(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: - '@expo/dom-webview': 55.0.3(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@expo/dom-webview': 57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) anser: 1.4.10 - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) stacktrace-parser: 0.1.11 - '@expo/metro-config@55.0.11(expo@55.0.9)(typescript@5.9.3)': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@expo/config': 55.0.11(typescript@5.9.3) - '@expo/env': 2.1.1 - '@expo/json-file': 10.0.12 - '@expo/metro': 54.2.0 - '@expo/spawn-async': 1.7.2 - browserslist: 4.28.2 + '@expo/metro-config@57.0.6(expo@57.0.7)(supports-color@9.4.0)(typescript@6.0.3)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/generator': 7.29.7 + '@expo/config': 57.0.5(supports-color@9.4.0)(typescript@6.0.3) + '@expo/env': 2.4.2(supports-color@9.4.0) + '@expo/json-file': 11.0.1 + '@expo/metro': 56.0.0(supports-color@9.4.0) + '@expo/require-utils': 57.0.3(supports-color@9.4.0)(typescript@6.0.3) + '@expo/spawn-async': 1.8.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + browserslist: 4.28.6 chalk: 4.1.2 debug: 4.4.3(supports-color@9.4.0) getenv: 2.0.0 glob: 13.0.6 - hermes-parser: 0.32.1 + hermes-parser: 0.36.1 jsc-safe-url: 0.2.4 lightningcss: 1.32.0 - picomatch: 4.0.4 - postcss: 8.4.49 + picomatch: 4.0.5 + postcss: 8.5.19 resolve-from: 5.0.0 optionalDependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) transitivePeerDependencies: - bufferutil - supports-color - typescript - utf-8-validate - '@expo/metro@54.2.0': - dependencies: - metro: 0.83.3 - metro-babel-transformer: 0.83.3 - metro-cache: 0.83.3 - metro-cache-key: 0.83.3 - metro-config: 0.83.3 - metro-core: 0.83.3 - metro-file-map: 0.83.3 - metro-minify-terser: 0.83.3 - metro-resolver: 0.83.3 - metro-runtime: 0.83.3 - metro-source-map: 0.83.3 - metro-symbolicate: 0.83.3 - metro-transform-plugins: 0.83.3 - metro-transform-worker: 0.83.3 + '@expo/metro-file-map@57.0.1(supports-color@9.4.0)': + dependencies: + debug: 4.4.3(supports-color@9.4.0) + fb-watchman: 2.0.2 + invariant: 2.2.4 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + transitivePeerDependencies: + - supports-color + + '@expo/metro@56.0.0(supports-color@9.4.0)': + dependencies: + metro: 0.84.4(supports-color@9.4.0) + metro-babel-transformer: 0.84.4(supports-color@9.4.0) + metro-cache: 0.84.4(supports-color@9.4.0) + metro-cache-key: 0.84.4 + metro-config: 0.84.4(supports-color@9.4.0) + metro-core: 0.84.4 + metro-file-map: 0.84.4(supports-color@9.4.0) + metro-minify-terser: 0.84.4 + metro-resolver: 0.84.4 + metro-runtime: 0.84.4 + metro-source-map: 0.84.4(supports-color@9.4.0) + metro-symbolicate: 0.84.4(supports-color@9.4.0) + metro-transform-plugins: 0.84.4(supports-color@9.4.0) + metro-transform-worker: 0.84.4(supports-color@9.4.0) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@expo/osascript@2.4.2': + '@expo/osascript@2.7.1': dependencies: - '@expo/spawn-async': 1.7.2 + '@expo/spawn-async': 1.8.0 - '@expo/package-manager@1.10.3': + '@expo/package-manager@1.13.1': dependencies: - '@expo/json-file': 10.0.12 - '@expo/spawn-async': 1.7.2 + '@expo/json-file': 11.0.1 + '@expo/spawn-async': 1.8.0 chalk: 4.1.2 npm-package-arg: 11.0.3 ora: 3.4.0 resolve-workspace-root: 2.0.1 - '@expo/plist@0.5.2': + '@expo/plist@0.8.1': dependencies: - '@xmldom/xmldom': 0.8.12 + '@xmldom/xmldom': 0.8.13 base64-js: 1.5.1 xmlbuilder: 15.1.1 - '@expo/prebuild-config@55.0.11(expo@55.0.9)(typescript@5.9.3)': + '@expo/prebuild-config@57.0.8(supports-color@9.4.0)(typescript@6.0.3)': dependencies: - '@expo/config': 55.0.11(typescript@5.9.3) - '@expo/config-plugins': 55.0.7 - '@expo/config-types': 55.0.5 - '@expo/image-utils': 0.8.12 - '@expo/json-file': 10.0.12 - '@react-native/normalize-colors': 0.83.4 + '@expo/config': 57.0.5(supports-color@9.4.0)(typescript@6.0.3) + '@expo/config-plugins': 57.0.5(supports-color@9.4.0)(typescript@6.0.3) + '@expo/config-types': 57.0.2 + '@expo/image-utils': 0.11.3(supports-color@9.4.0)(typescript@6.0.3) + '@expo/json-file': 11.0.1 + '@react-native/normalize-colors': 0.86.0 debug: 4.4.3(supports-color@9.4.0) - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo-modules-autolinking: 57.0.8(supports-color@9.4.0)(typescript@6.0.3) resolve-from: 5.0.0 - semver: 7.7.4 - xml2js: 0.6.0 + semver: 7.8.5 transitivePeerDependencies: - supports-color - typescript - '@expo/require-utils@55.0.3(typescript@5.9.3)': + '@expo/require-utils@57.0.3(supports-color@9.4.0)(typescript@6.0.3)': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/core': 7.29.0 - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@expo/router-server@55.0.11(expo-constants@55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3))(expo-font@55.0.4(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(expo-server@55.0.6)(expo@55.0.9)(react@19.2.4)': + '@expo/router-server@57.0.3(expo-constants@57.0.6(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(supports-color@9.4.0))(expo-font@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(expo-server@57.0.1)(expo@57.0.7)(react@19.2.3)(supports-color@9.4.0)': dependencies: debug: 4.4.3(supports-color@9.4.0) - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - expo-constants: 55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3) - expo-font: 55.0.4(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - expo-server: 55.0.6 - react: 19.2.4 + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + expo-constants: 57.0.6(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(supports-color@9.4.0) + expo-font: 57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + expo-server: 57.0.1 + react: 19.2.3 transitivePeerDependencies: - supports-color - '@expo/schema-utils@55.0.2': {} + '@expo/schema-utils@57.0.2': {} '@expo/sdk-runtime-versions@1.0.0': {} - '@expo/spawn-async@1.7.2': + '@expo/server@0.5.3(supports-color@9.4.0)': + dependencies: + abort-controller: 3.0.0 + debug: 4.4.3(supports-color@9.4.0) + source-map-support: 0.5.21 + undici: 6.28.0 + transitivePeerDependencies: + - supports-color + + '@expo/spawn-async@1.8.0': dependencies: cross-spawn: 7.0.6 '@expo/sudo-prompt@9.3.2': {} - '@expo/vector-icons@15.1.1(expo-font@55.0.4(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@expo/ws-tunnel@2.0.0(ws@8.21.1)': dependencies: - expo-font: 55.0.4(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - - '@expo/ws-tunnel@1.0.6': {} + ws: 8.21.1 - '@expo/xcpretty@4.4.1': + '@expo/xcpretty@4.4.4': dependencies: - '@babel/code-frame': 7.29.0 + '@babel/code-frame': 7.29.7 chalk: 4.1.2 - js-yaml: 4.1.1 + js-yaml: 4.3.0 - '@gorhom/bottom-sheet@5.2.8(@types/react@19.2.14)(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-reanimated@4.3.0(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@gorhom/bottom-sheet@5.2.14(24fcb93f20601afa4f41878e40cdc0fb)': dependencies: - '@gorhom/portal': 1.0.14(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@gorhom/portal': 1.0.14(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) invariant: 2.2.4 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-gesture-handler: 2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-reanimated: 4.3.0(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-native-gesture-handler: 2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + react-native-reanimated: 4.5.2(react-native-worklets@0.10.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@gorhom/portal@1.0.14(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@gorhom/portal@1.0.14(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: - nanoid: 3.3.11 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + nanoid: 3.3.16 + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - '@hapi/hoek@9.3.0': {} - - '@hapi/topo@5.1.0': + '@humanfs/core@0.19.2': dependencies: - '@hapi/hoek': 9.3.0 + '@humanfs/types': 0.15.0 - '@humanwhocodes/config-array@0.13.0': + '@humanfs/node@0.16.8': dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.3(supports-color@9.4.0) - minimatch: 3.1.5 - transitivePeerDependencies: - - supports-color + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/object-schema@2.0.3': {} + '@humanwhocodes/retry@0.4.3': {} '@isaacs/ttlcache@1.4.1': {} @@ -7744,43 +7600,43 @@ snapshots: camelcase: 5.3.1 find-up: 4.1.0 get-package-type: 0.1.0 - js-yaml: 3.14.2 + js-yaml: 3.15.0 resolve-from: 5.0.0 - '@istanbuljs/schema@0.1.3': {} + '@istanbuljs/schema@0.1.6': {} '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 25.5.0 + '@types/node': 26.1.1 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0': + '@jest/core@29.7.0(supports-color@9.4.0)': dependencies: '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 + '@jest/reporters': 29.7.0(supports-color@9.4.0) '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 + '@jest/transform': 29.7.0(supports-color@9.4.0) '@jest/types': 29.6.3 - '@types/node': 25.5.0 + '@types/node': 26.1.1 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@25.5.0) + jest-config: 29.7.0(@types/node@26.1.1)(supports-color@9.4.0) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 + jest-resolve-dependencies: 29.7.0(supports-color@9.4.0) + jest-runner: 29.7.0(supports-color@9.4.0) + jest-runtime: 29.7.0(supports-color@9.4.0) + jest-snapshot: 29.7.0(supports-color@9.4.0) jest-util: 29.7.0 jest-validate: 29.7.0 jest-watcher: 29.7.0 @@ -7797,23 +7653,23 @@ snapshots: dependencies: '@jest/types': 29.6.3 - '@jest/diff-sequences@30.3.0': {} + '@jest/diff-sequences@30.4.0': {} '@jest/environment@29.7.0': dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.5.0 + '@types/node': 26.1.1 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': dependencies: jest-get-type: 29.6.3 - '@jest/expect@29.7.0': + '@jest/expect@29.7.0(supports-color@9.4.0)': dependencies: expect: 29.7.0 - jest-snapshot: 29.7.0 + jest-snapshot: 29.7.0(supports-color@9.4.0) transitivePeerDependencies: - supports-color @@ -7821,40 +7677,40 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 25.5.0 + '@types/node': 26.1.1 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 '@jest/get-type@30.1.0': {} - '@jest/globals@29.7.0': + '@jest/globals@29.7.0(supports-color@9.4.0)': dependencies: '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 + '@jest/expect': 29.7.0(supports-color@9.4.0) '@jest/types': 29.6.3 jest-mock: 29.7.0 transitivePeerDependencies: - supports-color - '@jest/reporters@29.7.0': + '@jest/reporters@29.7.0(supports-color@9.4.0)': dependencies: '@bcoe/v8-coverage': 0.2.3 '@jest/console': 29.7.0 '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 + '@jest/transform': 29.7.0(supports-color@9.4.0) '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 25.5.0 + '@types/node': 26.1.1 chalk: 4.1.2 collect-v8-coverage: 1.0.3 exit: 0.1.2 glob: 7.2.3 graceful-fs: 4.2.11 istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 + istanbul-lib-instrument: 6.0.3(supports-color@9.4.0) istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 + istanbul-lib-source-maps: 4.0.1(supports-color@9.4.0) istanbul-reports: 3.2.0 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -7868,11 +7724,11 @@ snapshots: '@jest/schemas@29.6.3': dependencies: - '@sinclair/typebox': 0.27.10 + '@sinclair/typebox': 0.27.12 - '@jest/schemas@30.0.5': + '@jest/schemas@30.4.1': dependencies: - '@sinclair/typebox': 0.34.49 + '@sinclair/typebox': 0.34.52 '@jest/source-map@29.6.3': dependencies: @@ -7894,12 +7750,12 @@ snapshots: jest-haste-map: 29.7.0 slash: 3.0.0 - '@jest/transform@29.7.0': + '@jest/transform@29.7.0(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.31 - babel-plugin-istanbul: 6.1.1 + babel-plugin-istanbul: 6.1.1(supports-color@9.4.0) chalk: 4.1.2 convert-source-map: 2.0.0 fast-json-stable-stringify: 2.1.0 @@ -7919,7 +7775,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.5.0 + '@types/node': 26.1.1 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -7947,494 +7803,387 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@js-joda/core@5.7.0': {} - '@js-temporal/polyfill@0.5.1': dependencies: jsbi: 4.3.2 - '@legendapp/list@2.0.19(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': - dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - use-sync-external-store: 1.6.0(react@19.2.4) - - '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': - dependencies: - eslint-scope: 5.1.1 - - '@noble/ciphers@2.1.1': {} - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 - - '@op-engineering/op-sqlite@15.2.9(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': - dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - - '@preeternal/react-native-cookie-manager@6.3.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': - dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - - '@protobufjs/aspromise@1.1.2': {} - - '@protobufjs/base64@1.1.2': {} - - '@protobufjs/codegen@2.0.4': {} - - '@protobufjs/eventemitter@1.1.0': {} - - '@protobufjs/fetch@1.1.0': - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/inquire': 1.1.0 - - '@protobufjs/float@1.0.2': {} - - '@protobufjs/inquire@1.1.0': {} - - '@protobufjs/path@1.1.2': {} - - '@protobufjs/pool@1.1.0': {} - - '@protobufjs/utf8@1.1.0': {} - - '@react-native-community/cli-clean@20.1.3': - dependencies: - '@react-native-community/cli-tools': 20.1.3 - execa: 5.1.1 - fast-glob: 3.3.3 - picocolors: 1.1.1 - - '@react-native-community/cli-config-android@20.1.3': - dependencies: - '@react-native-community/cli-tools': 20.1.3 - fast-glob: 3.3.3 - fast-xml-parser: 5.5.9 - picocolors: 1.1.1 - - '@react-native-community/cli-config-apple@20.1.3': + '@legendapp/list@3.3.3(patch_hash=37a9c8d7d9c1bd33c957ba6ea78291c7b08532dc32de4925f3d612cb41f1a659)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: - '@react-native-community/cli-tools': 20.1.3 - execa: 5.1.1 - fast-glob: 3.3.3 - picocolors: 1.1.1 - - '@react-native-community/cli-config@20.1.3(typescript@5.9.3)': - dependencies: - '@react-native-community/cli-tools': 20.1.3 - cosmiconfig: 9.0.1(typescript@5.9.3) - deepmerge: 4.3.1 - fast-glob: 3.3.3 - joi: 17.13.3 - picocolors: 1.1.1 - transitivePeerDependencies: - - typescript - - '@react-native-community/cli-doctor@20.1.3(typescript@5.9.3)': - dependencies: - '@react-native-community/cli-config': 20.1.3(typescript@5.9.3) - '@react-native-community/cli-platform-android': 20.1.3 - '@react-native-community/cli-platform-apple': 20.1.3 - '@react-native-community/cli-platform-ios': 20.1.3 - '@react-native-community/cli-tools': 20.1.3 - command-exists: 1.2.9 - deepmerge: 4.3.1 - envinfo: 7.21.0 - execa: 5.1.1 - node-stream-zip: 1.15.0 - ora: 5.4.1 - picocolors: 1.1.1 - semver: 7.7.4 - wcwidth: 1.0.1 - yaml: 2.8.3 - transitivePeerDependencies: - - typescript + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) + optionalDependencies: + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - '@react-native-community/cli-platform-android@20.1.3': - dependencies: - '@react-native-community/cli-config-android': 20.1.3 - '@react-native-community/cli-tools': 20.1.3 - execa: 5.1.1 - logkitty: 0.7.1 - picocolors: 1.1.1 + '@material/material-color-utilities@0.3.0': {} - '@react-native-community/cli-platform-apple@20.1.3': + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: - '@react-native-community/cli-config-apple': 20.1.3 - '@react-native-community/cli-tools': 20.1.3 - execa: 5.1.1 - fast-xml-parser: 5.5.9 - picocolors: 1.1.1 + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.3 + optional: true - '@react-native-community/cli-platform-ios@20.1.3': - dependencies: - '@react-native-community/cli-platform-apple': 20.1.3 + '@noble/ciphers@2.2.0': {} - '@react-native-community/cli-server-api@20.1.3': - dependencies: - '@react-native-community/cli-tools': 20.1.3 - body-parser: 2.2.2 - compression: 1.8.1 - connect: 3.7.0 - errorhandler: 1.5.2 - nocache: 3.0.4 - open: 6.4.0 - pretty-format: 29.7.0 - serve-static: 1.16.3 - strict-url-sanitise: 0.0.1 - ws: 6.2.3 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate + '@nolyfill/is-core-module@1.0.39': {} - '@react-native-community/cli-tools@20.1.3': + '@op-engineering/op-sqlite@15.2.14(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: - '@vscode/sudo-prompt': 9.3.2 - appdirsjs: 1.2.7 - execa: 5.1.1 - find-up: 5.0.0 - launch-editor: 2.13.2 - mime: 2.6.0 - ora: 5.4.1 - picocolors: 1.1.1 - prompts: 2.4.2 - semver: 7.7.4 + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - '@react-native-community/cli-types@20.1.3': + '@pchmn/expo-material3-theme@1.4.0(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: - joi: 17.13.3 + '@material/material-color-utilities': 0.3.0 + color: 4.2.3 + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - '@react-native-community/cli@20.1.3(typescript@5.9.3)': + '@preeternal/react-native-cookie-manager@6.3.3(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: - '@react-native-community/cli-clean': 20.1.3 - '@react-native-community/cli-config': 20.1.3(typescript@5.9.3) - '@react-native-community/cli-doctor': 20.1.3(typescript@5.9.3) - '@react-native-community/cli-server-api': 20.1.3 - '@react-native-community/cli-tools': 20.1.3 - '@react-native-community/cli-types': 20.1.3 - commander: 9.5.0 - deepmerge: 4.3.1 - execa: 5.1.1 - find-up: 5.0.0 - fs-extra: 8.1.0 - graceful-fs: 4.2.11 - picocolors: 1.1.1 - prompts: 2.4.2 - semver: 7.7.4 - transitivePeerDependencies: - - bufferutil - - supports-color - - typescript - - utf-8-validate + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - '@react-native-community/slider@5.1.2': {} - - '@react-native-documents/picker@12.0.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@react-native-documents/picker@12.0.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - '@react-native-google-signin/google-signin@16.1.2(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@react-native-google-signin/google-signin@16.1.2(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) optionalDependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) - '@react-native-vector-icons/common@13.0.0(@react-native/assets-registry@0.83.4)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@react-native-vector-icons/common@13.0.1(@react-native/assets-registry@0.86.0)(expo-font@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: - find-up: 7.0.0 + find-up: 8.0.0 picocolors: 1.1.1 - plist: 3.1.0 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + plist: 3.1.1 + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) optionalDependencies: - '@react-native/assets-registry': 0.83.4 + '@react-native/assets-registry': 0.86.0 + expo-font: 57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) - '@react-native-vector-icons/material-design-icons@13.0.0(@react-native/assets-registry@0.83.4)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@react-native-vector-icons/material-design-icons@13.1.2(@expo/config-plugins@57.0.5(supports-color@9.4.0)(typescript@6.0.3))(@react-native/assets-registry@0.86.0)(expo-font@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: - '@react-native-vector-icons/common': 13.0.0(@react-native/assets-registry@0.83.4)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + '@react-native-vector-icons/common': 13.0.1(@react-native/assets-registry@0.86.0)(expo-font@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + optionalDependencies: + '@expo/config-plugins': 57.0.5(supports-color@9.4.0)(typescript@6.0.3) transitivePeerDependencies: - '@react-native-vector-icons/get-image' - '@react-native/assets-registry' + - expo-font - '@react-native/assets-registry@0.83.4': {} + '@react-native/assets-registry@0.86.0': {} - '@react-native/babel-plugin-codegen@0.83.4(@babel/core@7.29.0)': + '@react-native/babel-plugin-codegen@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/traverse': 7.29.0 - '@react-native/codegen': 0.83.4(@babel/core@7.29.0) + '@babel/traverse': 7.29.7(supports-color@9.4.0) + '@react-native/codegen': 0.86.0(@babel/core@7.29.7(supports-color@9.4.0)) transitivePeerDependencies: - '@babel/core' - supports-color - '@react-native/babel-preset@0.83.4(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) - '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) - '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) - '@babel/template': 7.28.6 - '@react-native/babel-plugin-codegen': 0.83.4(@babel/core@7.29.0) - babel-plugin-syntax-hermes-parser: 0.32.0 - babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0) + '@react-native/babel-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': + dependencies: + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-export-default-from': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-flow-strip-types': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@react-native/babel-plugin-codegen': 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + babel-plugin-syntax-hermes-parser: 0.36.0 + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.7(supports-color@9.4.0)) react-refresh: 0.14.2 transitivePeerDependencies: - supports-color - '@react-native/codegen@0.83.4(@babel/core@7.29.0)': + '@react-native/codegen@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.2 - glob: 7.2.3 - hermes-parser: 0.32.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/parser': 7.29.7 + hermes-parser: 0.36.0 invariant: 2.2.4 nullthrows: 1.1.1 - yargs: 17.7.2 + tinyglobby: 0.2.17 + yargs: 17.7.3 - '@react-native/community-cli-plugin@0.83.4(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))': + '@react-native/community-cli-plugin@0.86.0(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@react-native/dev-middleware': 0.83.4 + '@react-native/dev-middleware': 0.86.0(supports-color@9.4.0) debug: 4.4.3(supports-color@9.4.0) invariant: 2.2.4 - metro: 0.83.5 - metro-config: 0.83.5 - metro-core: 0.83.5 - semver: 7.7.4 + metro: 0.84.4(supports-color@9.4.0) + metro-config: 0.84.4(supports-color@9.4.0) + metro-core: 0.84.4 + semver: 7.8.5 optionalDependencies: - '@react-native-community/cli': 20.1.3(typescript@5.9.3) - '@react-native/metro-config': 0.83.4(@babel/core@7.29.0) + '@react-native/metro-config': 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@react-native/debugger-frontend@0.83.4': {} + '@react-native/debugger-frontend@0.86.0': {} - '@react-native/debugger-shell@0.83.4': + '@react-native/debugger-shell@0.86.0(supports-color@9.4.0)': dependencies: cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@9.4.0) fb-dotslash: 0.5.8 + transitivePeerDependencies: + - supports-color - '@react-native/dev-middleware@0.83.4': + '@react-native/dev-middleware@0.86.0(supports-color@9.4.0)': dependencies: '@isaacs/ttlcache': 1.4.1 - '@react-native/debugger-frontend': 0.83.4 - '@react-native/debugger-shell': 0.83.4 - chrome-launcher: 0.15.2 - chromium-edge-launcher: 0.2.0 - connect: 3.7.0 + '@react-native/debugger-frontend': 0.86.0 + '@react-native/debugger-shell': 0.86.0(supports-color@9.4.0) + chrome-launcher: 0.15.2(supports-color@9.4.0) + chromium-edge-launcher: 0.3.0(supports-color@9.4.0) + connect: 3.7.0(supports-color@9.4.0) debug: 4.4.3(supports-color@9.4.0) invariant: 2.2.4 nullthrows: 1.1.1 open: 7.4.2 - serve-static: 1.16.3 - ws: 7.5.10 + serve-static: 1.16.3(supports-color@9.4.0) + ws: 7.5.13 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@react-native/eslint-config@0.83.4(eslint@8.57.1)(jest@29.7.0(@types/node@25.5.0))(prettier@2.8.8)(typescript@5.9.3)': - dependencies: - '@babel/core': 7.29.0 - '@babel/eslint-parser': 7.28.6(@babel/core@7.29.0)(eslint@8.57.1) - '@react-native/eslint-plugin': 0.83.4 - '@typescript-eslint/eslint-plugin': 8.58.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/parser': 8.58.0(eslint@8.57.1)(typescript@5.9.3) - eslint: 8.57.1 - eslint-config-prettier: 8.10.2(eslint@8.57.1) - eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.1) - eslint-plugin-ft-flow: 2.0.3(@babel/eslint-parser@7.28.6(@babel/core@7.29.0)(eslint@8.57.1))(eslint@8.57.1) - eslint-plugin-jest: 29.15.1(@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@29.7.0(@types/node@25.5.0))(typescript@5.9.3) - eslint-plugin-react: 7.37.5(eslint@8.57.1) - eslint-plugin-react-hooks: 7.0.1(eslint@8.57.1) - eslint-plugin-react-native: 4.1.0(eslint@8.57.1) - prettier: 2.8.8 + '@react-native/gradle-plugin@0.86.0': {} + + '@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0)': + dependencies: + '@jest/create-cache-key-function': 29.7.0 + '@react-native/js-polyfills': 0.86.0 + babel-jest: 29.7.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + jest-environment-node: 29.7.0 + react: 19.2.3 + regenerator-runtime: 0.13.11 transitivePeerDependencies: - - jest + - '@babel/core' - supports-color - - typescript - - '@react-native/eslint-plugin@0.83.4': {} - '@react-native/gradle-plugin@0.83.4': {} + '@react-native/js-polyfills@0.86.0': {} - '@react-native/js-polyfills@0.83.4': {} - - '@react-native/metro-babel-transformer@0.83.4(@babel/core@7.29.0)': + '@react-native/metro-babel-transformer@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@babel/core': 7.29.0 - '@react-native/babel-preset': 0.83.4(@babel/core@7.29.0) - hermes-parser: 0.32.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@react-native/babel-preset': 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + hermes-parser: 0.36.0 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - '@react-native/metro-config@0.83.4(@babel/core@7.29.0)': + '@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0)': dependencies: - '@react-native/js-polyfills': 0.83.4 - '@react-native/metro-babel-transformer': 0.83.4(@babel/core@7.29.0) - metro-config: 0.83.5 - metro-runtime: 0.83.5 + '@react-native/js-polyfills': 0.86.0 + '@react-native/metro-babel-transformer': 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + metro-config: 0.84.4(supports-color@9.4.0) + metro-runtime: 0.84.4 transitivePeerDependencies: - '@babel/core' - bufferutil - supports-color - utf-8-validate - '@react-native/normalize-colors@0.83.4': {} - - '@react-native/typescript-config@0.83.4': {} + '@react-native/normalize-colors@0.86.0': {} - '@react-native/virtualized-lists@0.83.4(@types/react@19.2.14)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@react-native/virtualized-lists@0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@react-navigation/bottom-tabs@7.15.9(@react-navigation/native@7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-screens@4.24.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@react-navigation/bottom-tabs@7.18.11(b07337b648742f75a968ee96fc7e36e1)': dependencies: - '@react-navigation/elements': 2.9.14(@react-navigation/native@7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@react-navigation/native': 7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@react-navigation/elements': 2.9.33(6a714dfbe98a0f61d11f6605543bef0b) + '@react-navigation/native': 7.3.11(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) color: 4.2.3 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-safe-area-context: 5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-screens: 4.24.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-native-safe-area-context: 5.8.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + react-native-screens: 4.26.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) sf-symbols-typescript: 2.2.0 transitivePeerDependencies: - '@react-native-masked-view/masked-view' - '@react-navigation/core@7.17.2(react@19.2.4)': + '@react-navigation/core@7.21.8(react@19.2.3)': dependencies: - '@react-navigation/routers': 7.5.3 + '@react-navigation/routers': 7.6.2 escape-string-regexp: 4.0.0 fast-deep-equal: 3.1.3 - nanoid: 3.3.11 + nanoid: 3.3.16 query-string: 7.1.3 - react: 19.2.4 - react-is: 19.2.4 - use-latest-callback: 0.2.6(react@19.2.4) - use-sync-external-store: 1.6.0(react@19.2.4) + react: 19.2.3 + react-is: 19.2.7 + use-latest-callback: 0.2.6(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.3) - '@react-navigation/elements@2.9.14(@react-navigation/native@7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@react-navigation/elements@2.9.33(6a714dfbe98a0f61d11f6605543bef0b)': dependencies: - '@react-navigation/native': 7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@react-navigation/native': 7.3.11(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) color: 4.2.3 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-safe-area-context: 5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - use-latest-callback: 0.2.6(react@19.2.4) - use-sync-external-store: 1.6.0(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-native-safe-area-context: 5.8.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + use-latest-callback: 0.2.6(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.3) - '@react-navigation/native-stack@7.14.10(@react-navigation/native@7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-screens@4.24.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@react-navigation/native-stack@7.18.3(b07337b648742f75a968ee96fc7e36e1)': dependencies: - '@react-navigation/elements': 2.9.14(@react-navigation/native@7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@react-navigation/native': 7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@react-navigation/elements': 2.9.33(6a714dfbe98a0f61d11f6605543bef0b) + '@react-navigation/native': 7.3.11(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) color: 4.2.3 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-safe-area-context: 5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-screens: 4.24.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-native-safe-area-context: 5.8.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + react-native-screens: 4.26.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) sf-symbols-typescript: 2.2.0 warn-once: 0.1.1 transitivePeerDependencies: - '@react-native-masked-view/masked-view' - '@react-navigation/native@7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@react-navigation/native@7.3.11(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': dependencies: - '@react-navigation/core': 7.17.2(react@19.2.4) + '@react-navigation/core': 7.21.8(react@19.2.3) escape-string-regexp: 4.0.0 fast-deep-equal: 3.1.3 - nanoid: 3.3.11 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - use-latest-callback: 0.2.6(react@19.2.4) + nanoid: 3.3.16 + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + standard-navigation: 0.0.8(react@19.2.3) + use-latest-callback: 0.2.6(react@19.2.3) - '@react-navigation/routers@7.5.3': + '@react-navigation/routers@7.6.2': dependencies: - nanoid: 3.3.11 + nanoid: 3.3.16 - '@react-navigation/stack@7.8.9(05167d8527e2c9f68e950b4e3fb9e48b)': + '@react-navigation/stack@7.10.14(84780aa2bc277522b3a415ae0584f553)': dependencies: - '@react-navigation/elements': 2.9.14(@react-navigation/native@7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@react-navigation/native': 7.2.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@react-navigation/elements': 2.9.33(6a714dfbe98a0f61d11f6605543bef0b) + '@react-navigation/native': 7.3.11(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) color: 4.2.3 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-gesture-handler: 2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-safe-area-context: 5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-screens: 4.24.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - use-latest-callback: 0.2.6(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-native-gesture-handler: 2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + react-native-safe-area-context: 5.8.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + react-native-screens: 4.26.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + use-latest-callback: 0.2.6(react@19.2.3) transitivePeerDependencies: - '@react-native-masked-view/masked-view' - '@sideway/address@4.1.5': + '@rozenite/agent-bridge@1.13.0(react@19.2.3)': + dependencies: + '@rozenite/agent-shared': 1.13.0 + '@rozenite/plugin-bridge': 1.13.0(react@19.2.3) + react: 19.2.3 + tslib: 2.8.1 + + '@rozenite/agent-shared@1.13.0': + dependencies: + tslib: 2.8.1 + + '@rozenite/expo-atlas-plugin@1.13.0(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0)': dependencies: - '@hapi/hoek': 9.3.0 + '@rozenite/plugin-bridge': 1.13.0(react@19.2.3) + connect: 3.7.0(supports-color@9.4.0) + expo-atlas: 0.4.3(expo@57.0.7)(supports-color@9.4.0) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + transitivePeerDependencies: + - expo + - supports-color - '@sideway/formula@3.0.1': {} + '@rozenite/metro@1.13.0(supports-color@9.4.0)': + dependencies: + '@rozenite/middleware': 1.13.0(supports-color@9.4.0) + '@rozenite/tools': 1.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate - '@sideway/pinpoint@2.0.0': {} + '@rozenite/middleware@1.13.0(supports-color@9.4.0)': + dependencies: + '@rozenite/agent-shared': 1.13.0 + '@rozenite/runtime': 1.13.0 + '@rozenite/tools': 1.13.0 + express: 5.2.1(supports-color@9.4.0) + semver: 7.8.5 + tslib: 2.8.1 + ws: 8.21.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate - '@sinclair/typebox@0.27.10': {} + '@rozenite/plugin-bridge@1.13.0(react@19.2.3)': + dependencies: + react: 19.2.3 + tslib: 2.8.1 - '@sinclair/typebox@0.34.49': {} + '@rozenite/runtime@1.13.0': + dependencies: + tslib: 2.8.1 + + '@rozenite/sqlite-plugin@1.13.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': + dependencies: + '@rozenite/agent-bridge': 1.13.0(react@19.2.3) + '@rozenite/plugin-bridge': 1.13.0(react@19.2.3) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + + '@rozenite/tools@1.13.0': {} + + '@rtsao/scc@1.1.0': {} + + '@shopify/flash-list@2.3.2(@babel/runtime@7.29.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.29.7 + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + + '@sinclair/typebox@0.27.12': {} + + '@sinclair/typebox@0.34.52': {} '@sinonjs/commons@3.0.1': dependencies: @@ -8444,46 +8193,55 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@tediousjs/connection-string@0.5.0': {} - - '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@25.5.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react-test-renderer@19.2.4(react@19.2.4))(react@19.2.4)': + '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@26.1.1)(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - jest-matcher-utils: 30.3.0 + jest-matcher-utils: 30.4.1 picocolors: 1.1.1 - pretty-format: 30.3.0 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-test-renderer: 19.2.4(react@19.2.4) + pretty-format: 30.4.1 + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-test-renderer: 19.2.3(react@19.2.3) redent: 3.0.0 optionalDependencies: - jest: 29.7.0(@types/node@25.5.0) + jest: 29.7.0(@types/node@26.1.1)(supports-color@9.4.0) + + '@tootallnate/once@2.0.1': {} - '@tootallnate/once@2.0.0': {} + '@ts-morph/common@0.29.0': + dependencies: + minimatch: 10.2.5 + path-browserify: 1.0.1 + tinyglobby: 0.2.17 + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 '@types/better-sqlite3@7.6.13': dependencies: - '@types/node': 25.5.0 + '@types/node': 26.1.1 '@types/color-convert@2.0.4': dependencies: @@ -8495,9 +8253,11 @@ snapshots: dependencies: '@types/color-convert': 2.0.4 + '@types/estree@1.0.9': {} + '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 25.5.0 + '@types/node': 26.1.1 '@types/hammerjs@2.0.46': {} @@ -8518,37 +8278,32 @@ snapshots: '@types/jsdom@20.0.1': dependencies: - '@types/node': 25.5.0 + '@types/node': 26.1.1 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + '@types/lodash-es@4.17.12': dependencies: '@types/lodash': 4.17.24 '@types/lodash@4.17.24': {} - '@types/mssql@9.1.9(@azure/core-client@1.10.1)': + '@types/node@26.1.1': dependencies: - '@types/node': 25.5.0 - tarn: 3.0.2 - tedious: 19.2.1(@azure/core-client@1.10.1) - transitivePeerDependencies: - - '@azure/core-client' - - supports-color + undici-types: 8.3.0 - '@types/node@25.5.0': + '@types/react-test-renderer@19.1.0': dependencies: - undici-types: 7.18.2 + '@types/react': 19.2.17 - '@types/react@19.2.14': + '@types/react@19.2.17': dependencies: csstype: 3.2.3 - '@types/readable-stream@4.0.23': - dependencies: - '@types/node': 25.5.0 - '@types/sanitize-html@2.16.1': dependencies: htmlparser2: 10.1.0 @@ -8563,110 +8318,203 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.58.0(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.58.0 - '@typescript-eslint/type-utils': 8.58.0(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/utils': 8.58.0(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.58.0 - eslint: 8.57.1 - ignore: 7.0.5 + '@typescript-eslint/parser': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.64.0 + '@typescript-eslint/type-utils': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.64.0 + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) + ignore: 7.0.6 natural-compare: 1.4.0 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.58.0 - '@typescript-eslint/types': 8.58.0 - '@typescript-eslint/typescript-estree': 8.58.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.58.0 + '@typescript-eslint/scope-manager': 8.64.0 + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/typescript-estree': 8.64.0(supports-color@9.4.0)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.64.0 debug: 4.4.3(supports-color@9.4.0) - eslint: 8.57.1 - typescript: 5.9.3 + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.58.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.64.0(supports-color@9.4.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.58.0(typescript@5.9.3) - '@typescript-eslint/types': 8.58.0 + '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@6.0.3) + '@typescript-eslint/types': 8.64.0 debug: 4.4.3(supports-color@9.4.0) - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.58.0': + '@typescript-eslint/scope-manager@8.64.0': dependencies: - '@typescript-eslint/types': 8.58.0 - '@typescript-eslint/visitor-keys': 8.58.0 + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/visitor-keys': 8.64.0 - '@typescript-eslint/tsconfig-utils@8.58.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.64.0(typescript@6.0.3)': dependencies: - typescript: 5.9.3 + typescript: 6.0.3 - '@typescript-eslint/type-utils@8.58.0(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.58.0 - '@typescript-eslint/typescript-estree': 8.58.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.58.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/typescript-estree': 8.64.0(supports-color@9.4.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) debug: 4.4.3(supports-color@9.4.0) - eslint: 8.57.1 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.58.0': {} + '@typescript-eslint/types@8.64.0': {} - '@typescript-eslint/typescript-estree@8.58.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.64.0(supports-color@9.4.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.58.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.58.0(typescript@5.9.3) - '@typescript-eslint/types': 8.58.0 - '@typescript-eslint/visitor-keys': 8.58.0 + '@typescript-eslint/project-service': 8.64.0(supports-color@9.4.0)(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@6.0.3) + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/visitor-keys': 8.64.0 debug: 4.4.3(supports-color@9.4.0) minimatch: 10.2.5 - semver: 7.7.4 - tinyglobby: 0.2.15 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.58.0(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/utils@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) - '@typescript-eslint/scope-manager': 8.58.0 - '@typescript-eslint/types': 8.58.0 - '@typescript-eslint/typescript-estree': 8.58.0(typescript@5.9.3) - eslint: 8.57.1 - typescript: 5.9.3 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0)) + '@typescript-eslint/scope-manager': 8.64.0 + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/typescript-estree': 8.64.0(supports-color@9.4.0)(typescript@6.0.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.58.0': + '@typescript-eslint/visitor-keys@8.64.0': dependencies: - '@typescript-eslint/types': 8.58.0 + '@typescript-eslint/types': 8.64.0 eslint-visitor-keys: 5.0.1 - '@typespec/ts-http-runtime@0.3.4': + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260707.2': + optional: true + + '@typescript/native-preview-darwin-x64@7.0.0-dev.20260707.2': + optional: true + + '@typescript/native-preview-linux-arm64@7.0.0-dev.20260707.2': + optional: true + + '@typescript/native-preview-linux-arm@7.0.0-dev.20260707.2': + optional: true + + '@typescript/native-preview-linux-x64@7.0.0-dev.20260707.2': + optional: true + + '@typescript/native-preview-win32-arm64@7.0.0-dev.20260707.2': + optional: true + + '@typescript/native-preview-win32-x64@7.0.0-dev.20260707.2': + optional: true + + '@typescript/native-preview@7.0.0-dev.20260707.2': + optionalDependencies: + '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260707.2 + '@typescript/native-preview-darwin-x64': 7.0.0-dev.20260707.2 + '@typescript/native-preview-linux-arm': 7.0.0-dev.20260707.2 + '@typescript/native-preview-linux-arm64': 7.0.0-dev.20260707.2 + '@typescript/native-preview-linux-x64': 7.0.0-dev.20260707.2 + '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260707.2 + '@typescript/native-preview-win32-x64': 7.0.0-dev.20260707.2 + + '@ungap/structured-clone@1.3.3': {} + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + optional: true + + '@unrs/resolver-binding-android-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': dependencies: - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true - '@ungap/structured-clone@1.3.0': {} + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + optional: true - '@vscode/sudo-prompt@9.3.2': {} + '@xmldom/xmldom@0.8.13': {} - '@xmldom/xmldom@0.8.12': {} + '@xmldom/xmldom@0.9.10': {} abab@2.0.6: {} @@ -8686,20 +8534,20 @@ snapshots: acorn-globals@7.0.1: dependencies: - acorn: 8.16.0 + acorn: 8.17.0 acorn-walk: 8.3.5 - acorn-jsx@5.3.2(acorn@8.16.0): + acorn-jsx@5.3.2(acorn@8.17.0): dependencies: - acorn: 8.16.0 + acorn: 8.17.0 acorn-walk@8.3.5: dependencies: - acorn: 8.16.0 + acorn: 8.17.0 - acorn@8.16.0: {} + acorn@8.17.0: {} - agent-base@6.0.2: + agent-base@6.0.2(supports-color@9.4.0): dependencies: debug: 4.4.3(supports-color@9.4.0) transitivePeerDependencies: @@ -8707,12 +8555,14 @@ snapshots: agent-base@7.1.4: {} + agent-cli-detector@0.1.3: {} + aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 - ajv@6.14.0: + ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 @@ -8727,12 +8577,6 @@ snapshots: ansi-escapes@6.2.1: {} - ansi-fragments@0.2.1: - dependencies: - colorette: 1.4.0 - slice-ansi: 2.1.0 - strip-ansi: 5.2.0 - ansi-regex@4.1.1: {} ansi-regex@5.0.1: {} @@ -8756,8 +8600,6 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.2 - appdirsjs@1.2.7: {} - arg@5.0.2: {} argparse@1.0.10: @@ -8771,84 +8613,89 @@ snapshots: call-bound: 1.0.4 is-array-buffer: 3.0.5 + array-flatten@1.1.1: {} + array-includes@3.1.9: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.1 - es-object-atoms: 1.1.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 is-string: 1.1.1 math-intrinsics: 1.1.0 array.prototype.findlast@1.2.5: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 es-shim-unscopables: 1.1.0 array.prototype.flat@1.3.3: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-shim-unscopables: 1.1.0 array.prototype.tosorted@1.1.4: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-errors: 1.3.0 es-shim-unscopables: 1.1.0 arraybuffer.prototype.slice@1.0.4: dependencies: array-buffer-byte-length: 1.0.2 - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 asap@2.0.6: {} - astral-regex@1.0.0: {} - astral-regex@2.0.0: {} async-function@1.0.0: {} - async-limiter@1.0.1: {} - asynckit@0.4.0: {} available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 - await-lock@2.2.2: - optional: true - - babel-jest@29.7.0(@babel/core@7.29.0): + babel-jest@29.7.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0): dependencies: - '@babel/core': 7.29.0 - '@jest/transform': 29.7.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@jest/transform': 29.7.0(supports-color@9.4.0) '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.29.0) + babel-plugin-istanbul: 6.1.1(supports-color@9.4.0) + babel-preset-jest: 29.6.3(@babel/core@7.29.7(supports-color@9.4.0)) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -8859,20 +8706,20 @@ snapshots: dependencies: require-resolve: 0.0.2 - babel-plugin-istanbul@6.1.1: + babel-plugin-istanbul@6.1.1(supports-color@9.4.0): dependencies: - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-plugin-utils': 7.29.7 '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-instrument: 5.2.1(supports-color@9.4.0) test-exclude: 6.0.0 transitivePeerDependencies: - supports-color babel-plugin-jest-hoist@29.6.3: dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.28.0 @@ -8882,119 +8729,136 @@ snapshots: glob: 9.3.5 pkg-up: 3.1.0 reselect: 4.1.8 - resolve: 1.22.11 + resolve: 1.22.12 - babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0): + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0): dependencies: - '@babel/compat-data': 7.29.0 - '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.0): + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0): dependencies: - '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) core-js-compat: 3.49.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.0): + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0): dependencies: - '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) core-js-compat: 3.49.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.0): + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0): dependencies: - '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) transitivePeerDependencies: - supports-color babel-plugin-react-compiler@1.0.0: dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 babel-plugin-react-native-web@0.21.2: {} - babel-plugin-syntax-hermes-parser@0.32.0: + babel-plugin-syntax-hermes-parser@0.36.0: dependencies: - hermes-parser: 0.32.0 + hermes-parser: 0.36.0 - babel-plugin-syntax-hermes-parser@0.32.1: + babel-plugin-syntax-hermes-parser@0.36.1: dependencies: - hermes-parser: 0.32.1 + hermes-parser: 0.36.1 - babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.29.0): + babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.29.7(supports-color@9.4.0)): dependencies: - '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-flow': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) transitivePeerDependencies: - '@babel/core' - babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) - - babel-preset-expo@55.0.13(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@55.0.9)(react-refresh@0.14.2): - dependencies: - '@babel/generator': 7.29.1 - '@babel/helper-module-imports': 7.28.6 - '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) - '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0) - '@babel/preset-react': 7.28.5(@babel/core@7.29.0) - '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@react-native/babel-preset': 0.83.4(@babel/core@7.29.0) + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.7(supports-color@9.4.0)): + dependencies: + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.7(supports-color@9.4.0)) + + babel-preset-expo@57.0.3(@babel/core@7.29.7(supports-color@9.4.0))(@babel/runtime@7.29.7)(expo@57.0.7)(react-refresh@0.14.2)(supports-color@9.4.0): + dependencies: + '@babel/generator': 7.29.7 + '@babel/helper-module-imports': 7.29.7(supports-color@9.4.0) + '@babel/plugin-proposal-decorators': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-export-default-from': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-flow-strip-types': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-react-pure-annotations': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@react-native/babel-plugin-codegen': 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) babel-plugin-react-compiler: 1.0.0 babel-plugin-react-native-web: 0.21.2 - babel-plugin-syntax-hermes-parser: 0.32.1 - babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0) + babel-plugin-syntax-hermes-parser: 0.36.1 + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.7(supports-color@9.4.0)) debug: 4.4.3(supports-color@9.4.0) react-refresh: 0.14.2 - resolve-from: 5.0.0 optionalDependencies: - '@babel/runtime': 7.29.2 - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@babel/runtime': 7.29.7 + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) transitivePeerDependencies: - '@babel/core' - supports-color - babel-preset-jest@29.6.3(@babel/core@7.29.0): + babel-preset-jest@29.6.3(@babel/core@7.29.7(supports-color@9.4.0)): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) - - badgin@1.2.3: {} + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7(supports-color@9.4.0)) balanced-match@1.0.2: {} @@ -9002,20 +8866,20 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.13: {} + baseline-browser-mapping@2.10.43: {} - better-opn@3.0.2: + basic-auth@2.0.1: dependencies: - open: 8.4.2 + safe-buffer: 5.1.2 - better-sqlite3@12.8.0: + better-sqlite3@12.11.1: dependencies: bindings: 1.5.0 prebuild-install: 7.1.3 big-integer@1.6.52: {} - bignumber.js@10.0.2: {} + bignumber.js@11.1.5: {} bindings@1.5.0: dependencies: @@ -9027,24 +8891,34 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - bl@6.1.6: + body-parser@1.20.6(supports-color@9.4.0): dependencies: - '@types/readable-stream': 4.0.23 - buffer: 6.0.3 - inherits: 2.0.4 - readable-stream: 4.7.0 + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9(supports-color@9.4.0) + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.15.3 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color - body-parser@2.2.2: + body-parser@2.3.0(supports-color@9.4.0): dependencies: bytes: 3.1.2 - content-type: 1.0.5 + content-type: 2.0.0 debug: 4.4.3(supports-color@9.4.0) http-errors: 2.0.1 - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 on-finished: 2.4.1 - qs: 6.15.0 + qs: 6.15.3 raw-body: 3.0.2 - type-is: 2.0.1 + type-is: 2.1.0 transitivePeerDependencies: - supports-color @@ -9062,16 +8936,16 @@ snapshots: dependencies: big-integer: 1.6.52 - brace-expansion@1.1.13: + brace-expansion@1.1.16: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.3: + brace-expansion@2.1.2: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.5: + brace-expansion@5.0.7: dependencies: balanced-match: 4.0.4 @@ -9079,20 +8953,18 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.28.2: + browserslist@4.28.6: dependencies: - baseline-browser-mapping: 2.10.13 - caniuse-lite: 1.0.30001784 - electron-to-chromium: 1.5.330 - node-releases: 2.0.36 - update-browserslist-db: 1.2.3(browserslist@4.28.2) + baseline-browser-mapping: 2.10.43 + caniuse-lite: 1.0.30001806 + electron-to-chromium: 1.5.393 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.6) bser@2.1.1: dependencies: node-int64: 0.4.0 - buffer-equal-constant-time@1.0.1: {} - buffer-from@1.1.2: {} buffer@5.7.1: @@ -9100,15 +8972,6 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - bundle-name@4.1.0: - dependencies: - run-applescript: 7.1.0 - bytes@3.1.2: {} call-bind-apply-helpers@1.0.2: @@ -9116,7 +8979,7 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.8: + call-bind@1.0.9: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 @@ -9134,7 +8997,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001784: {} + caniuse-lite@1.0.30001806: {} chalk@2.4.2: dependencies: @@ -9152,6 +9015,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.6.2: {} + char-regex@1.0.2: {} char-regex@2.0.2: {} @@ -9177,23 +9042,22 @@ snapshots: chownr@1.1.4: {} - chrome-launcher@0.15.2: + chrome-launcher@0.15.2(supports-color@9.4.0): dependencies: - '@types/node': 25.5.0 + '@types/node': 26.1.1 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 - lighthouse-logger: 1.4.2 + lighthouse-logger: 1.4.2(supports-color@9.4.0) transitivePeerDependencies: - supports-color - chromium-edge-launcher@0.2.0: + chromium-edge-launcher@0.3.0(supports-color@9.4.0): dependencies: - '@types/node': 25.5.0 + '@types/node': 26.1.1 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 - lighthouse-logger: 1.4.2 + lighthouse-logger: 1.4.2(supports-color@9.4.0) mkdirp: 1.0.4 - rimraf: 3.0.2 transitivePeerDependencies: - supports-color @@ -9225,22 +9089,24 @@ snapshots: slice-ansi: 5.0.0 string-width: 5.1.2 - cliui@6.0.0: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + cliui@9.0.1: + dependencies: + string-width: 7.2.0 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + clone@1.0.4: {} co@4.6.0: {} + code-block-writer@13.0.3: {} + collect-v8-coverage@1.0.3: {} color-convert@1.9.3: @@ -9285,18 +9151,12 @@ snapshots: color-convert: 3.1.3 color-string: 2.1.4 - colorette@1.4.0: {} - colorette@2.0.20: {} combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - command-exists@1.2.9: {} - - commander@11.1.0: {} - commander@12.1.0: {} commander@2.20.3: {} @@ -9309,11 +9169,11 @@ snapshots: dependencies: mime-db: 1.54.0 - compression@1.8.1: + compression@1.8.1(supports-color@9.4.0): dependencies: bytes: 3.1.2 compressible: 2.0.18 - debug: 2.6.9 + debug: 2.6.9(supports-color@9.4.0) negotiator: 0.6.4 on-headers: 1.1.0 safe-buffer: 5.2.1 @@ -9323,39 +9183,44 @@ snapshots: concat-map@0.0.1: {} - connect@3.7.0: + connect@3.7.0(supports-color@9.4.0): dependencies: - debug: 2.6.9 - finalhandler: 1.1.2 + debug: 2.6.9(supports-color@9.4.0) + finalhandler: 1.1.2(supports-color@9.4.0) parseurl: 1.3.3 utils-merge: 1.0.1 transitivePeerDependencies: - supports-color + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-disposition@1.1.0: {} + content-type@1.0.5: {} + content-type@2.0.0: {} + convert-source-map@2.0.0: {} - core-js-compat@3.49.0: - dependencies: - browserslist: 4.28.2 + cookie-signature@1.0.7: {} + + cookie-signature@1.2.2: {} - cosmiconfig@9.0.1(typescript@5.9.3): + cookie@0.7.2: {} + + core-js-compat@3.49.0: dependencies: - env-paths: 2.2.1 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - parse-json: 5.2.0 - optionalDependencies: - typescript: 5.9.3 + browserslist: 4.28.6 - create-jest@29.7.0(@types/node@25.5.0): + create-jest@29.7.0(@types/node@26.1.1)(supports-color@9.4.0): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@25.5.0) + jest-config: 29.7.0(@types/node@26.1.1)(supports-color@9.4.0) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -9378,6 +9243,11 @@ snapshots: domutils: 3.2.2 nth-check: 2.1.1 + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + css-what@6.2.2: {} cssom@0.3.8: {} @@ -9414,15 +9284,19 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 - dayjs@1.11.20: {} + dayjs@1.11.21: {} - debug@2.6.9: + debug@2.6.9(supports-color@9.4.0): dependencies: ms: 2.0.0 + optionalDependencies: + supports-color: 9.4.0 - debug@3.2.7: + debug@3.2.7(supports-color@9.4.0): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 9.4.0 debug@4.4.3(supports-color@9.4.0): dependencies: @@ -9430,8 +9304,6 @@ snapshots: optionalDependencies: supports-color: 9.4.0 - decamelize@1.2.0: {} - decimal.js@10.6.0: {} decode-uri-component@0.2.2: {} @@ -9440,8 +9312,6 @@ snapshots: dependencies: mimic-response: 3.1.0 - dedent@0.6.0: {} - dedent@1.7.2: {} deep-extend@0.6.0: {} @@ -9452,13 +9322,6 @@ snapshots: deepmerge@4.3.1: {} - default-browser-id@5.0.1: {} - - default-browser@5.5.0: - dependencies: - bundle-name: 4.1.0 - default-browser-id: 5.0.1 - defaults@1.0.4: dependencies: clone: 1.0.4 @@ -9471,8 +9334,6 @@ snapshots: define-lazy-prop@2.0.0: {} - define-lazy-prop@3.0.0: {} - define-properties@1.2.1: dependencies: define-data-property: 1.1.4 @@ -9491,23 +9352,19 @@ snapshots: diff-sequences@29.6.3: {} - dnssd-advertise@1.1.4: {} + dnssd-advertise@1.1.6: {} doctrine@2.1.0: dependencies: esutils: 2.0.3 - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 entities: 4.5.0 - dom-serializer@3.0.0: + dom-serializer@3.1.1: dependencies: domelementtype: 3.0.0 domhandler: 6.0.1 @@ -9537,7 +9394,7 @@ snapshots: domutils@4.0.2: dependencies: - dom-serializer: 3.0.0 + dom-serializer: 3.1.1 domelementtype: 3.0.0 domhandler: 6.0.1 @@ -9546,20 +9403,16 @@ snapshots: '@drizzle-team/brocli': 0.11.0 '@js-temporal/polyfill': 0.5.1 esbuild: 0.25.12 - get-tsconfig: 4.13.7 - jiti: 2.6.1 + get-tsconfig: 4.14.0 + jiti: 2.7.0 - drizzle-orm@1.0.0-beta.20(@op-engineering/op-sqlite@15.2.9(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(@sinclair/typebox@0.34.49)(@types/better-sqlite3@7.6.13)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(better-sqlite3@12.8.0)(expo-sqlite@16.0.10(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(mssql@11.0.1(@azure/core-client@1.10.1))(zod@4.3.6): - dependencies: - '@types/mssql': 9.1.9(@azure/core-client@1.10.1) - mssql: 11.0.1(@azure/core-client@1.10.1) + drizzle-orm@1.0.0-beta.22(@op-engineering/op-sqlite@15.2.14(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(@sinclair/typebox@0.34.52)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.11.1)(zod@4.4.3): optionalDependencies: - '@op-engineering/op-sqlite': 15.2.9(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@sinclair/typebox': 0.34.49 + '@op-engineering/op-sqlite': 15.2.14(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + '@sinclair/typebox': 0.34.52 '@types/better-sqlite3': 7.6.13 - better-sqlite3: 12.8.0 - expo-sqlite: 16.0.10(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - zod: 4.3.6 + better-sqlite3: 12.11.1 + zod: 4.4.3 dunder-proto@1.0.1: dependencies: @@ -9569,16 +9422,14 @@ snapshots: eastasianwidth@0.2.0: {} - ecdsa-sig-formatter@1.0.11: - dependencies: - safe-buffer: 5.2.1 - ee-first@1.1.1: {} - electron-to-chromium@1.5.330: {} + electron-to-chromium@1.5.393: {} emittery@0.13.1: {} + emoji-regex@10.6.0: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -9599,10 +9450,6 @@ snapshots: entities@8.0.0: {} - env-paths@2.2.1: {} - - envinfo@7.21.0: {} - error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -9611,27 +9458,29 @@ snapshots: dependencies: stackframe: 1.3.4 - errorhandler@1.5.2: + es-abstract-get@1.0.0: dependencies: - accepts: 1.3.8 - escape-html: 1.0.3 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 - es-abstract@1.24.1: + es-abstract@1.24.2: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 data-view-buffer: 1.0.2 data-view-byte-length: 1.0.2 data-view-byte-offset: 1.0.1 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 - function.prototype.name: 1.1.8 + es-to-primitive: 1.3.4 + function.prototype.name: 1.2.0 get-intrinsic: 1.3.0 get-proto: 1.0.1 get-symbol-description: 1.1.0 @@ -9640,7 +9489,7 @@ snapshots: has-property-descriptors: 1.0.2 has-proto: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.2 + hasown: 2.0.4 internal-slot: 1.1.0 is-array-buffer: 3.0.5 is-callable: 1.2.7 @@ -9658,31 +9507,31 @@ snapshots: object.assign: 4.1.7 own-keys: 1.0.1 regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 + safe-array-concat: 1.1.4 safe-push-apply: 1.0.0 safe-regex-test: 1.1.0 set-proto: 1.0.0 stop-iteration-iterator: 1.1.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 string.prototype.trimstart: 1.0.8 typed-array-buffer: 1.0.3 typed-array-byte-length: 1.0.3 typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 + typed-array-length: 1.0.8 unbox-primitive: 1.1.0 - which-typed-array: 1.1.20 + which-typed-array: 1.1.22 es-define-property@1.0.1: {} es-errors@1.3.0: {} - es-iterator-helpers@1.3.1: + es-iterator-helpers@1.4.0: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-errors: 1.3.0 es-set-tostringtag: 2.1.0 function-bind: 1.1.2 @@ -9695,9 +9544,8 @@ snapshots: internal-slot: 1.1.0 iterator.prototype: 1.1.5 math-intrinsics: 1.1.0 - safe-array-concat: 1.1.3 - es-object-atoms@1.1.1: + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 @@ -9706,14 +9554,17 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 es-shim-unscopables@1.1.0: dependencies: - hasown: 2.0.2 + hasown: 2.0.4 - es-to-primitive@1.3.0: + es-to-primitive@1.3.4: dependencies: + es-abstract-get: 1.0.0 + es-define-property: 1.0.1 + es-errors: 1.3.0 is-callable: 1.2.7 is-date-object: 1.1.0 is-symbol: 1.1.1 @@ -9765,159 +9616,212 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-prettier@8.10.2(eslint@8.57.1): - dependencies: - eslint: 8.57.1 - - eslint-plugin-eslint-comments@3.2.0(eslint@8.57.1): + eslint-config-expo@57.0.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3): dependencies: - escape-string-regexp: 1.0.5 - eslint: 8.57.1 - ignore: 5.3.2 + '@typescript-eslint/eslint-plugin': 8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0) + eslint-plugin-expo: 1.1.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0) + eslint-plugin-react: 7.37.5(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0)) + eslint-plugin-react-hooks: 7.1.1(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0) + globals: 16.5.0 + transitivePeerDependencies: + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + - typescript - eslint-plugin-ft-flow@2.0.3(@babel/eslint-parser@7.28.6(@babel/core@7.29.0)(eslint@8.57.1))(eslint@8.57.1): + eslint-import-resolver-node@0.3.10(supports-color@9.4.0): dependencies: - '@babel/eslint-parser': 7.28.6(@babel/core@7.29.0)(eslint@8.57.1) - eslint: 8.57.1 - lodash: 4.17.23 - string-natural-compare: 3.0.1 + debug: 3.2.7(supports-color@9.4.0) + is-core-module: 2.16.2 + resolve: 2.0.0-next.7 + transitivePeerDependencies: + - supports-color - eslint-plugin-ft-flow@3.0.11(eslint@8.57.1)(hermes-eslint@0.33.3): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0): dependencies: - eslint: 8.57.1 - hermes-eslint: 0.33.3 - lodash: 4.17.23 - string-natural-compare: 3.0.1 + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3(supports-color@9.4.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) + get-tsconfig: 4.14.0 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.17 + unrs-resolver: 1.12.2 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0) + transitivePeerDependencies: + - supports-color - eslint-plugin-jest@29.15.1(@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@29.7.0(@types/node@25.5.0))(typescript@5.9.3): + eslint-module-utils@2.14.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10(supports-color@9.4.0))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0): dependencies: - '@typescript-eslint/utils': 8.58.0(eslint@8.57.1)(typescript@5.9.3) - eslint: 8.57.1 + debug: 3.2.7(supports-color@9.4.0) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.58.0(@typescript-eslint/parser@8.58.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) - jest: 29.7.0(@types/node@25.5.0) - typescript: 5.9.3 + '@typescript-eslint/parser': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) + eslint-import-resolver-node: 0.3.10(supports-color@9.4.0) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0) transitivePeerDependencies: - supports-color - eslint-plugin-react-hooks@7.0.1(eslint@8.57.1): + eslint-plugin-expo@1.1.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3): dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.2 - eslint: 8.57.1 - hermes-parser: 0.25.1 - zod: 4.3.6 - zod-validation-error: 4.0.2(zod@4.3.6) + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) transitivePeerDependencies: - supports-color + - typescript + + eslint-plugin-ft-flow@3.0.11(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(hermes-eslint@0.37.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))): + dependencies: + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) + hermes-eslint: 0.37.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0)) + lodash: 4.18.1 + string-natural-compare: 3.0.1 - eslint-plugin-react-native-globals@0.1.2: {} + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7(supports-color@9.4.0) + doctrine: 2.1.0 + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) + eslint-import-resolver-node: 0.3.10(supports-color@9.4.0) + eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10(supports-color@9.4.0))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0) + hasown: 2.0.4 + is-core-module: 2.16.2 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.10 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color - eslint-plugin-react-native@4.1.0(eslint@8.57.1): + eslint-plugin-jest@29.15.4(@typescript-eslint/eslint-plugin@8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(jest@29.7.0(@types/node@26.1.1)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3): dependencies: - eslint: 8.57.1 - eslint-plugin-react-native-globals: 0.1.2 + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) + optionalDependencies: + '@typescript-eslint/eslint-plugin': 8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) + jest: 29.7.0(@types/node@26.1.1)(supports-color@9.4.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color - eslint-plugin-react-native@5.0.0(eslint@8.57.1): + eslint-plugin-react-hooks@7.1.1(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0): dependencies: - eslint: 8.57.1 - eslint-plugin-react-native-globals: 0.1.2 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/parser': 7.29.7 + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) + hermes-parser: 0.25.1 + zod: 4.4.3 + zod-validation-error: 4.0.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color - eslint-plugin-react@7.37.5(eslint@8.57.1): + eslint-plugin-react@7.37.5(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0)): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.3.1 - eslint: 8.57.1 + es-iterator-helpers: 1.4.0 + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) estraverse: 5.3.0 - hasown: 2.0.2 + hasown: 2.0.4 jsx-ast-utils: 3.3.5 minimatch: 3.1.5 object.entries: 1.1.9 object.fromentries: 2.0.8 object.values: 1.2.1 prop-types: 15.8.1 - resolve: 2.0.0-next.6 + resolve: 2.0.0-next.7 semver: 6.3.1 string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-testing-library@7.16.2(eslint@8.57.1)(typescript@5.9.3): + eslint-plugin-testing-library@7.16.2(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3): dependencies: - '@typescript-eslint/scope-manager': 8.58.0 - '@typescript-eslint/utils': 8.58.0(eslint@8.57.1)(typescript@5.9.3) - eslint: 8.57.1 + '@typescript-eslint/scope-manager': 8.64.0 + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0))(supports-color@9.4.0)(typescript@6.0.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) transitivePeerDependencies: - supports-color - typescript - eslint-scope@5.1.1: - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - - eslint-scope@7.2.2: + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 - eslint-visitor-keys@2.1.0: {} - eslint-visitor-keys@3.4.3: {} + eslint-visitor-keys@4.2.1: {} + eslint-visitor-keys@5.0.1: {} - eslint@8.57.1: + eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0)) '@eslint-community/regexpp': 4.12.2 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.1 - '@humanwhocodes/config-array': 0.13.0 + '@eslint/config-array': 0.21.2(supports-color@9.4.0) + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.6(supports-color@9.4.0) + '@eslint/js': 9.39.5 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.3.0 - ajv: 6.14.0 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3(supports-color@9.4.0) - doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 + file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.1 json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 lodash.merge: 4.6.2 minimatch: 3.1.5 natural-compare: 1.4.0 optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 + optionalDependencies: + jiti: 2.7.0 transitivePeerDependencies: - supports-color - espree@9.6.1: + espree@10.4.0: dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) - eslint-visitor-keys: 3.4.3 + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 4.2.1 esprima@4.0.1: {} @@ -9929,8 +9833,6 @@ snapshots: dependencies: estraverse: 5.3.0 - estraverse@4.3.0: {} - estraverse@5.3.0: {} esutils@2.0.3: {} @@ -9939,10 +9841,6 @@ snapshots: event-target-shim@5.0.1: {} - eventemitter3@4.0.7: {} - - events@3.3.0: {} - execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -9967,180 +9865,239 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - expo-application@55.0.10(expo@55.0.9): + expo-asset@57.0.6(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0)(typescript@6.0.3): dependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@expo/image-utils': 0.11.3(supports-color@9.4.0)(typescript@6.0.3) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + expo-constants: 57.0.6(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(supports-color@9.4.0) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + transitivePeerDependencies: + - supports-color + - typescript - expo-asset@55.0.10(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3): + expo-atlas@0.4.3(expo@57.0.7)(supports-color@9.4.0): dependencies: - '@expo/image-utils': 0.8.12 - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - expo-constants: 55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3) - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + '@expo/server': 0.5.3(supports-color@9.4.0) + arg: 5.0.2 + chalk: 4.1.2 + compression: 1.8.1(supports-color@9.4.0) + connect: 3.7.0(supports-color@9.4.0) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + express: 4.22.2(supports-color@9.4.0) + freeport-async: 2.0.0 + getenv: 2.0.0 + morgan: 1.11.0(supports-color@9.4.0) + open: 8.4.2 + serve-static: 1.16.3(supports-color@9.4.0) + stream-json: 1.9.1 transitivePeerDependencies: - supports-color - - typescript - expo-clipboard@55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + expo-build-properties@57.0.6(expo@57.0.7): + dependencies: + '@expo/schema-utils': 57.0.2 + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + resolve-from: 5.0.0 + semver: 7.8.5 + + expo-clipboard@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - expo-constants@55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3): + expo-constants@57.0.6(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(supports-color@9.4.0): dependencies: - '@expo/config': 55.0.11(typescript@5.9.3) - '@expo/env': 2.1.1 - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + '@expo/env': 2.4.2(supports-color@9.4.0) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) transitivePeerDependencies: - supports-color - - typescript - expo-document-picker@55.0.9(expo@55.0.9): + expo-dev-client@57.0.7(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)): + dependencies: + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + expo-dev-launcher: 57.0.7(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)) + expo-dev-menu: 57.0.7(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)) + expo-dev-menu-interface: 57.0.0(expo@57.0.7) + expo-manifests: 57.0.1(expo@57.0.7) + expo-updates-interface: 57.0.1(expo@57.0.7) + transitivePeerDependencies: + - react-native + + expo-dev-launcher@57.0.7(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)): + dependencies: + '@expo/schema-utils': 57.0.2 + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + expo-dev-menu: 57.0.7(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)) + expo-manifests: 57.0.1(expo@57.0.7) + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + + expo-dev-menu-interface@57.0.0(expo@57.0.7): + dependencies: + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + + expo-dev-menu@57.0.7(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)): dependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + expo-dev-menu-interface: 57.0.0(expo@57.0.7) + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - expo-file-system@55.0.12(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)): + expo-document-picker@57.0.1(expo@57.0.7): dependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) - expo-font@55.0.4(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + expo-file-system@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)): dependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + + expo-font@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): + dependencies: + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) fontfaceobserver: 2.3.0 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + + expo-haptics@57.0.1(expo@57.0.7): + dependencies: + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) - expo-haptics@55.0.9(expo@55.0.9): + expo-image@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + sf-symbols-typescript: 2.2.0 + + expo-json-utils@57.0.1: {} - expo-keep-awake@55.0.4(expo@55.0.9)(react@19.2.4): + expo-keep-awake@57.0.1(expo@57.0.7)(react@19.2.3): dependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react: 19.2.4 + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + react: 19.2.3 - expo-linear-gradient@55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + expo-linear-gradient@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - expo-linking@55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3): + expo-linking@57.0.3(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0): dependencies: - expo-constants: 55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3) + expo-constants: 57.0.6(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(supports-color@9.4.0) invariant: 2.2.4 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) transitivePeerDependencies: - expo - supports-color - - typescript - expo-localization@55.0.9(expo@55.0.9)(react@19.2.4): + expo-localization@57.0.1(expo@57.0.7)(react@19.2.3): dependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react: 19.2.4 + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + react: 19.2.3 rtl-detect: 1.1.2 - expo-modules-autolinking@55.0.12(typescript@5.9.3): + expo-manifests@57.0.1(expo@57.0.7): + dependencies: + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + expo-json-utils: 57.0.1 + + expo-modules-autolinking@57.0.8(supports-color@9.4.0)(typescript@6.0.3): dependencies: - '@expo/require-utils': 55.0.3(typescript@5.9.3) - '@expo/spawn-async': 1.7.2 + '@expo/require-utils': 57.0.3(supports-color@9.4.0)(typescript@6.0.3) + '@expo/spawn-async': 1.8.0 chalk: 4.1.2 commander: 7.2.0 transitivePeerDependencies: - supports-color - typescript - expo-modules-core@55.0.18(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + expo-modules-core@57.0.6(react-native-worklets@0.10.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: + '@expo/expo-modules-macros-plugin': 0.6.1 + expo-modules-jsi: 57.0.3(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)) invariant: 2.2.4 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + optionalDependencies: + react-native-worklets: 0.10.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0) - expo-navigation-bar@55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + expo-modules-jsi@57.0.3(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)): dependencies: - debug: 4.4.3(supports-color@9.4.0) - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-is-edge-to-edge: 1.3.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - transitivePeerDependencies: - - supports-color + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - expo-notifications@55.0.14(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3): + expo-navigation-bar@57.0.2(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0): dependencies: - '@expo/image-utils': 0.8.12 - abort-controller: 3.0.0 - badgin: 1.2.3 - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - expo-application: 55.0.10(expo@55.0.9) - expo-constants: 55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3) - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + debug: 4.4.3(supports-color@9.4.0) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) transitivePeerDependencies: - supports-color - - typescript - expo-server@55.0.6: {} + expo-server@57.0.1: {} - expo-speech@55.0.9(expo@55.0.9): + expo-speech@57.0.1(expo@57.0.7): dependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) - expo-sqlite@16.0.10(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + expo-splash-screen@57.0.4(expo@57.0.7)(supports-color@9.4.0)(typescript@6.0.3): dependencies: - await-lock: 2.2.2 - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - optional: true + '@expo/config-plugins': 57.0.5(supports-color@9.4.0)(typescript@6.0.3) + '@expo/image-utils': 0.11.3(supports-color@9.4.0)(typescript@6.0.3) + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + xml2js: 0.6.0 + transitivePeerDependencies: + - supports-color + - typescript - expo-web-browser@55.0.10(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)): - dependencies: - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - - expo@55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3): - dependencies: - '@babel/runtime': 7.29.2 - '@expo/cli': 55.0.19(@expo/dom-webview@55.0.3)(expo-constants@55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3))(expo-font@55.0.4(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@expo/config': 55.0.11(typescript@5.9.3) - '@expo/config-plugins': 55.0.7 - '@expo/devtools': 55.0.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@expo/fingerprint': 0.16.6 - '@expo/local-build-cache-provider': 55.0.7(typescript@5.9.3) - '@expo/log-box': 55.0.8(@expo/dom-webview@55.0.3)(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@expo/metro': 54.2.0 - '@expo/metro-config': 55.0.11(expo@55.0.9)(typescript@5.9.3) - '@expo/vector-icons': 15.1.1(expo-font@55.0.4(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@ungap/structured-clone': 1.3.0 - babel-preset-expo: 55.0.13(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@55.0.9)(react-refresh@0.14.2) - expo-asset: 55.0.10(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - expo-constants: 55.0.9(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3) - expo-file-system: 55.0.12(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)) - expo-font: 55.0.4(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - expo-keep-awake: 55.0.4(expo@55.0.9)(react@19.2.4) - expo-modules-autolinking: 55.0.12(typescript@5.9.3) - expo-modules-core: 55.0.18(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + expo-updates-interface@57.0.1(expo@57.0.7): + dependencies: + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + + expo-web-browser@57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)): + dependencies: + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + + expo@57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213): + dependencies: + '@babel/runtime': 7.29.7 + '@expo/cli': 57.0.9(2d3b22c2103703c17c2fb47b72790f37) + '@expo/config': 57.0.5(supports-color@9.4.0)(typescript@6.0.3) + '@expo/config-plugins': 57.0.5(supports-color@9.4.0)(typescript@6.0.3) + '@expo/devtools': 57.0.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + '@expo/fingerprint': 0.20.5(supports-color@9.4.0) + '@expo/local-build-cache-provider': 57.0.4(supports-color@9.4.0)(typescript@6.0.3) + '@expo/log-box': 57.0.1(@expo/dom-webview@57.0.1)(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + '@expo/metro': 56.0.0(supports-color@9.4.0) + '@expo/metro-config': 57.0.6(expo@57.0.7)(supports-color@9.4.0)(typescript@6.0.3) + '@ungap/structured-clone': 1.3.3 + babel-preset-expo: 57.0.3(@babel/core@7.29.7(supports-color@9.4.0))(@babel/runtime@7.29.7)(expo@57.0.7)(react-refresh@0.14.2)(supports-color@9.4.0) + expo-asset: 57.0.6(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0)(typescript@6.0.3) + expo-constants: 57.0.6(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(supports-color@9.4.0) + expo-file-system: 57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)) + expo-font: 57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + expo-keep-awake: 57.0.1(expo@57.0.7)(react@19.2.3) + expo-modules-autolinking: 57.0.8(supports-color@9.4.0)(typescript@6.0.3) + expo-modules-core: 57.0.6(react-native-worklets@0.10.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) pretty-format: 29.7.0 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) react-refresh: 0.14.2 - whatwg-url-minimum: 0.1.1 + whatwg-url-minimum: 0.1.2 optionalDependencies: - '@expo/dom-webview': 55.0.3(expo@55.0.9)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-webview: 13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@expo/dom-webview': 57.0.1(expo@57.0.7)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + react-native-webview: 13.17.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) transitivePeerDependencies: - '@babel/core' - bufferutil - expo-router - expo-widgets - - react-dom + - react-native-worklets - react-server-dom-webpack - supports-color - typescript @@ -10148,49 +10105,96 @@ snapshots: exponential-backoff@3.1.3: {} - fast-deep-equal@3.1.3: {} + express@4.22.2(supports-color@9.4.0): + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.6(supports-color@9.4.0) + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9(supports-color@9.4.0) + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2(supports-color@9.4.0) + fresh: 0.5.2 + http-errors: 2.0.1 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.13 + proxy-addr: 2.0.7 + qs: 6.15.3 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2(supports-color@9.4.0) + serve-static: 1.16.3(supports-color@9.4.0) + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color - fast-glob@3.3.3: + express@5.2.1(supports-color@9.4.0): dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 + accepts: 2.0.0 + body-parser: 2.3.0(supports-color@9.4.0) + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3(supports-color@9.4.0) + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1(supports-color@9.4.0) + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.3 + range-parser: 1.2.1 + router: 2.2.0(supports-color@9.4.0) + send: 1.2.1(supports-color@9.4.0) + serve-static: 2.2.1(supports-color@9.4.0) + statuses: 2.0.2 + type-is: 2.1.0 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} - fast-xml-builder@1.1.4: - dependencies: - path-expression-matcher: 1.2.0 - - fast-xml-parser@5.5.9: - dependencies: - fast-xml-builder: 1.1.4 - path-expression-matcher: 1.2.0 - strnum: 2.2.2 - - fastq@1.20.1: - dependencies: - reusify: 1.1.0 - fb-dotslash@0.5.8: {} fb-watchman@2.0.2: dependencies: bser: 2.1.1 - fdir@6.5.0(picomatch@4.0.4): + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: - picomatch: 4.0.4 + picomatch: 4.0.5 fetch-nodeshim@0.4.10: {} - file-entry-cache@6.0.1: + file-entry-cache@8.0.0: dependencies: - flat-cache: 3.2.0 + flat-cache: 4.0.1 file-uri-to-path@1.0.0: {} @@ -10200,9 +10204,9 @@ snapshots: filter-obj@1.1.0: {} - finalhandler@1.1.2: + finalhandler@1.1.2(supports-color@9.4.0): dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@9.4.0) encodeurl: 1.0.2 escape-html: 1.0.3 on-finished: 2.3.0 @@ -10212,6 +10216,29 @@ snapshots: transitivePeerDependencies: - supports-color + finalhandler@1.3.2(supports-color@9.4.0): + dependencies: + debug: 2.6.9(supports-color@9.4.0) + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + finalhandler@2.1.1(supports-color@9.4.0): + dependencies: + debug: 4.4.3(supports-color@9.4.0) + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + find-babel-config@2.1.2: dependencies: json5: 2.2.3 @@ -10230,17 +10257,15 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - find-up@7.0.0: + find-up@8.0.0: dependencies: - locate-path: 7.2.0 - path-exists: 5.0.0 - unicorn-magic: 0.1.0 + locate-path: 8.0.0 + unicorn-magic: 0.3.0 - flat-cache@3.2.0: + flat-cache@4.0.1: dependencies: flatted: 3.4.2 keyv: 4.5.4 - rimraf: 3.0.2 flatted@3.4.2: {} @@ -10252,23 +10277,23 @@ snapshots: dependencies: is-callable: 1.2.7 - form-data@4.0.5: + form-data@4.0.6: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 - hasown: 2.0.2 + hasown: 2.0.4 mime-types: 2.1.35 + forwarded@0.2.0: {} + + freeport-async@2.0.0: {} + fresh@0.5.2: {} - fs-constants@1.0.0: {} + fresh@2.0.0: {} - fs-extra@8.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 + fs-constants@1.0.0: {} fs.realpath@1.0.0: {} @@ -10277,14 +10302,17 @@ snapshots: function-bind@1.1.2: {} - function.prototype.name@1.1.8: + function.prototype.name@1.2.0: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 - define-properties: 1.2.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 functions-have-names: 1.2.3 - hasown: 2.0.2 + has-property-descriptors: 1.0.2 + hasown: 2.0.4 is-callable: 1.2.7 + is-document.all: 1.0.0 functions-have-names@1.2.3: {} @@ -10294,17 +10322,19 @@ snapshots: get-caller-file@2.0.5: {} + get-east-asian-width@1.6.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.2 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-package-type@0.1.0: {} @@ -10312,7 +10342,7 @@ snapshots: get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-stream@6.0.1: {} @@ -10322,7 +10352,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - get-tsconfig@4.13.7: + get-tsconfig@4.14.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -10330,10 +10360,6 @@ snapshots: github-from-package@0.0.0: {} - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -10360,9 +10386,9 @@ snapshots: minipass: 4.2.8 path-scurry: 1.11.1 - globals@13.24.0: - dependencies: - type-fest: 0.20.2 + globals@14.0.0: {} + + globals@16.5.0: {} globalthis@1.0.4: dependencies: @@ -10373,8 +10399,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - has-bigints@1.1.0: {} has-flag@3.0.0: {} @@ -10395,43 +10419,48 @@ snapshots: dependencies: has-symbols: 1.1.0 - hasown@2.0.2: + hasown@2.0.4: dependencies: function-bind: 1.1.2 - hermes-compiler@0.14.1: {} + hermes-compiler@250829098.0.14: {} - hermes-compiler@250829098.0.2: {} - - hermes-eslint@0.33.3: + hermes-eslint@0.37.0(eslint@9.39.5(jiti@2.7.0)(supports-color@9.4.0)): dependencies: - esrecurse: 4.3.0 - hermes-estree: 0.33.3 - hermes-parser: 0.33.3 + hermes-estree: 0.37.0 + hermes-parser: 0.37.0 + optionalDependencies: + eslint: 9.39.5(jiti@2.7.0)(supports-color@9.4.0) hermes-estree@0.25.1: {} - hermes-estree@0.32.0: {} + hermes-estree@0.35.0: {} + + hermes-estree@0.36.0: {} - hermes-estree@0.32.1: {} + hermes-estree@0.36.1: {} - hermes-estree@0.33.3: {} + hermes-estree@0.37.0: {} hermes-parser@0.25.1: dependencies: hermes-estree: 0.25.1 - hermes-parser@0.32.0: + hermes-parser@0.35.0: + dependencies: + hermes-estree: 0.35.0 + + hermes-parser@0.36.0: dependencies: - hermes-estree: 0.32.0 + hermes-estree: 0.36.0 - hermes-parser@0.32.1: + hermes-parser@0.36.1: dependencies: - hermes-estree: 0.32.1 + hermes-estree: 0.36.1 - hermes-parser@0.33.3: + hermes-parser@0.37.0: dependencies: - hermes-estree: 0.33.3 + hermes-estree: 0.37.0 hoist-non-react-statics@3.3.2: dependencies: @@ -10476,29 +10505,22 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 - http-proxy-agent@5.0.0: - dependencies: - '@tootallnate/once': 2.0.0 - agent-base: 6.0.2 - debug: 4.4.3(supports-color@9.4.0) - transitivePeerDependencies: - - supports-color - - http-proxy-agent@7.0.2: + http-proxy-agent@5.0.0(supports-color@9.4.0): dependencies: - agent-base: 7.1.4 + '@tootallnate/once': 2.0.1 + agent-base: 6.0.2(supports-color@9.4.0) debug: 4.4.3(supports-color@9.4.0) transitivePeerDependencies: - supports-color - https-proxy-agent@5.0.1: + https-proxy-agent@5.0.1(supports-color@9.4.0): dependencies: - agent-base: 6.0.2 + agent-base: 6.0.2(supports-color@9.4.0) debug: 4.4.3(supports-color@9.4.0) transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.6: + https-proxy-agent@7.0.6(supports-color@9.4.0): dependencies: agent-base: 7.1.4 debug: 4.4.3(supports-color@9.4.0) @@ -10511,15 +10533,19 @@ snapshots: i18n-js@4.5.3: dependencies: - bignumber.js: 10.0.2 - lodash: 4.17.23 + bignumber.js: 11.1.5 + lodash: 4.18.1 make-plural: 7.5.0 + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.2: + iconv-lite@0.7.3: dependencies: safer-buffer: 2.1.2 @@ -10527,7 +10553,7 @@ snapshots: ignore@5.3.2: {} - ignore@7.0.5: {} + ignore@7.0.6: {} image-size@1.2.1: dependencies: @@ -10559,16 +10585,18 @@ snapshots: internal-slot@1.1.0: dependencies: es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 + hasown: 2.0.4 + side-channel: 1.1.1 invariant@2.2.4: dependencies: loose-envify: 1.4.0 + ipaddr.js@1.9.1: {} + is-array-buffer@3.0.5: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 get-intrinsic: 1.3.0 @@ -10593,11 +10621,15 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-bun-module@2.0.0: + dependencies: + semver: 7.8.5 + is-callable@1.2.7: {} - is-core-module@2.16.1: + is-core-module@2.16.2: dependencies: - hasown: 2.0.2 + hasown: 2.0.4 is-data-view@1.0.2: dependencies: @@ -10612,7 +10644,9 @@ snapshots: is-docker@2.2.1: {} - is-docker@3.0.0: {} + is-document.all@1.0.0: + dependencies: + call-bound: 1.0.4 is-extglob@2.1.1: {} @@ -10620,8 +10654,6 @@ snapshots: dependencies: call-bound: 1.0.4 - is-fullwidth-code-point@2.0.0: {} - is-fullwidth-code-point@3.0.0: {} is-fullwidth-code-point@4.0.0: {} @@ -10640,12 +10672,6 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 - - is-interactive@1.0.0: {} - is-map@2.0.3: {} is-negative-zero@2.0.3: {} @@ -10657,18 +10683,18 @@ snapshots: is-number@7.0.0: {} - is-path-inside@3.0.3: {} - is-plain-object@5.0.0: {} is-potential-custom-element-name@1.0.1: {} + is-promise@4.0.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 gopd: 1.2.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 is-set@2.0.3: {} @@ -10691,9 +10717,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.20 - - is-unicode-supported@0.1.0: {} + which-typed-array: 1.1.22 is-weakmap@2.0.2: {} @@ -10706,39 +10730,33 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 - is-wsl@1.1.0: {} - is-wsl@2.2.0: dependencies: is-docker: 2.2.1 - is-wsl@3.1.1: - dependencies: - is-inside-container: 1.0.0 - isarray@2.0.5: {} isexe@2.0.0: {} istanbul-lib-coverage@3.2.2: {} - istanbul-lib-instrument@5.2.1: + istanbul-lib-instrument@5.2.1(supports-color@9.4.0): dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.2 - '@istanbuljs/schema': 0.1.3 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/parser': 7.29.7 + '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 transitivePeerDependencies: - supports-color - istanbul-lib-instrument@6.0.3: + istanbul-lib-instrument@6.0.3(supports-color@9.4.0): dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.2 - '@istanbuljs/schema': 0.1.3 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/parser': 7.29.7 + '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 - semver: 7.7.4 + semver: 7.8.5 transitivePeerDependencies: - supports-color @@ -10748,7 +10766,7 @@ snapshots: make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@4.0.1: + istanbul-lib-source-maps@4.0.1(supports-color@9.4.0): dependencies: debug: 4.4.3(supports-color@9.4.0) istanbul-lib-coverage: 3.2.2 @@ -10764,7 +10782,7 @@ snapshots: iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 get-proto: 1.0.1 has-symbols: 1.1.0 @@ -10776,13 +10794,13 @@ snapshots: jest-util: 29.7.0 p-limit: 3.1.0 - jest-circus@29.7.0: + jest-circus@29.7.0(supports-color@9.4.0): dependencies: '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 + '@jest/expect': 29.7.0(supports-color@9.4.0) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.5.0 + '@types/node': 26.1.1 chalk: 4.1.2 co: 4.6.0 dedent: 1.7.2 @@ -10790,8 +10808,8 @@ snapshots: jest-each: 29.7.0 jest-matcher-utils: 29.7.0 jest-message-util: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 + jest-runtime: 29.7.0(supports-color@9.4.0) + jest-snapshot: 29.7.0(supports-color@9.4.0) jest-util: 29.7.0 p-limit: 3.1.0 pretty-format: 29.7.0 @@ -10802,42 +10820,42 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@25.5.0): + jest-cli@29.7.0(@types/node@26.1.1)(supports-color@9.4.0): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(supports-color@9.4.0) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@25.5.0) + create-jest: 29.7.0(@types/node@26.1.1)(supports-color@9.4.0) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@25.5.0) + jest-config: 29.7.0(@types/node@26.1.1)(supports-color@9.4.0) jest-util: 29.7.0 jest-validate: 29.7.0 - yargs: 17.7.2 + yargs: 17.7.3 transitivePeerDependencies: - '@types/node' - babel-plugin-macros - supports-color - ts-node - jest-config@29.7.0(@types/node@25.5.0): + jest-config@29.7.0(@types/node@26.1.1)(supports-color@9.4.0): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.29.0) + babel-jest: 29.7.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) chalk: 4.1.2 ci-info: 3.9.0 deepmerge: 4.3.1 glob: 7.2.3 graceful-fs: 4.2.11 - jest-circus: 29.7.0 + jest-circus: 29.7.0(supports-color@9.4.0) jest-environment-node: 29.7.0 jest-get-type: 29.6.3 jest-regex-util: 29.6.3 jest-resolve: 29.7.0 - jest-runner: 29.7.0 + jest-runner: 29.7.0(supports-color@9.4.0) jest-util: 29.7.0 jest-validate: 29.7.0 micromatch: 4.0.8 @@ -10846,7 +10864,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 25.5.0 + '@types/node': 26.1.1 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -10858,12 +10876,12 @@ snapshots: jest-get-type: 29.6.3 pretty-format: 29.7.0 - jest-diff@30.3.0: + jest-diff@30.4.1: dependencies: - '@jest/diff-sequences': 30.3.0 + '@jest/diff-sequences': 30.4.0 '@jest/get-type': 30.1.0 chalk: 4.1.2 - pretty-format: 30.3.0 + pretty-format: 30.4.1 jest-docblock@29.7.0: dependencies: @@ -10877,16 +10895,16 @@ snapshots: jest-util: 29.7.0 pretty-format: 29.7.0 - jest-environment-jsdom@29.7.0: + jest-environment-jsdom@29.7.0(supports-color@9.4.0): dependencies: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 25.5.0 + '@types/node': 26.1.1 jest-mock: 29.7.0 jest-util: 29.7.0 - jsdom: 20.0.3 + jsdom: 20.0.3(supports-color@9.4.0) transitivePeerDependencies: - bufferutil - supports-color @@ -10897,28 +10915,28 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.5.0 + '@types/node': 26.1.1 jest-mock: 29.7.0 jest-util: 29.7.0 - jest-expo@55.0.11(@babel/core@7.29.0)(expo@55.0.9)(jest@29.7.0(@types/node@25.5.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3): + jest-expo@57.0.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(expo@57.0.7)(jest@29.7.0(@types/node@26.1.1)(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0): dependencies: - '@expo/config': 55.0.11(typescript@5.9.3) - '@expo/json-file': 10.0.12 '@jest/create-cache-key-function': 29.7.0 - '@jest/globals': 29.7.0 - babel-jest: 29.7.0(@babel/core@7.29.0) - expo: 55.0.9(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - jest-environment-jsdom: 29.7.0 - jest-snapshot: 29.7.0 + '@jest/globals': 29.7.0(supports-color@9.4.0) + '@react-native/jest-preset': 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0) + babel-jest: 29.7.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + jest-environment-jsdom: 29.7.0(supports-color@9.4.0) + jest-snapshot: 29.7.0(supports-color@9.4.0) jest-watch-select-projects: 2.0.0 - jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@25.5.0)) + jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@26.1.1)(supports-color@9.4.0)) json5: 2.2.3 - lodash: 4.17.23 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-test-renderer: 19.2.0(react@19.2.4) + lodash: 4.18.1 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-test-renderer: 19.2.3(react@19.2.3) server-only: 0.0.1 stacktrace-js: 2.0.2 + optionalDependencies: + expo: 57.0.7(09a92d6f4cb27d968ea2a7d0b76b3213) transitivePeerDependencies: - '@babel/core' - bufferutil @@ -10926,7 +10944,6 @@ snapshots: - jest - react - supports-color - - typescript - utf-8-validate jest-get-type@29.6.3: {} @@ -10935,7 +10952,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 25.5.0 + '@types/node': 26.1.1 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -10959,16 +10976,16 @@ snapshots: jest-get-type: 29.6.3 pretty-format: 29.7.0 - jest-matcher-utils@30.3.0: + jest-matcher-utils@30.4.1: dependencies: '@jest/get-type': 30.1.0 chalk: 4.1.2 - jest-diff: 30.3.0 - pretty-format: 30.3.0 + jest-diff: 30.4.1 + pretty-format: 30.4.1 jest-message-util@29.7.0: dependencies: - '@babel/code-frame': 7.29.0 + '@babel/code-frame': 7.29.7 '@jest/types': 29.6.3 '@types/stack-utils': 2.0.3 chalk: 4.1.2 @@ -10981,7 +10998,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 25.5.0 + '@types/node': 26.1.1 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -10990,10 +11007,10 @@ snapshots: jest-regex-util@29.6.3: {} - jest-resolve-dependencies@29.7.0: + jest-resolve-dependencies@29.7.0(supports-color@9.4.0): dependencies: jest-regex-util: 29.6.3 - jest-snapshot: 29.7.0 + jest-snapshot: 29.7.0(supports-color@9.4.0) transitivePeerDependencies: - supports-color @@ -11005,18 +11022,18 @@ snapshots: jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) jest-util: 29.7.0 jest-validate: 29.7.0 - resolve: 1.22.11 + resolve: 1.22.12 resolve.exports: 2.0.3 slash: 3.0.0 - jest-runner@29.7.0: + jest-runner@29.7.0(supports-color@9.4.0): dependencies: '@jest/console': 29.7.0 '@jest/environment': 29.7.0 '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 + '@jest/transform': 29.7.0(supports-color@9.4.0) '@jest/types': 29.6.3 - '@types/node': 25.5.0 + '@types/node': 26.1.1 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -11026,7 +11043,7 @@ snapshots: jest-leak-detector: 29.7.0 jest-message-util: 29.7.0 jest-resolve: 29.7.0 - jest-runtime: 29.7.0 + jest-runtime: 29.7.0(supports-color@9.4.0) jest-util: 29.7.0 jest-watcher: 29.7.0 jest-worker: 29.7.0 @@ -11035,16 +11052,16 @@ snapshots: transitivePeerDependencies: - supports-color - jest-runtime@29.7.0: + jest-runtime@29.7.0(supports-color@9.4.0): dependencies: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.7.0 + '@jest/globals': 29.7.0(supports-color@9.4.0) '@jest/source-map': 29.6.3 '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 + '@jest/transform': 29.7.0(supports-color@9.4.0) '@jest/types': 29.6.3 - '@types/node': 25.5.0 + '@types/node': 26.1.1 chalk: 4.1.2 cjs-module-lexer: 1.4.3 collect-v8-coverage: 1.0.3 @@ -11055,24 +11072,24 @@ snapshots: jest-mock: 29.7.0 jest-regex-util: 29.6.3 jest-resolve: 29.7.0 - jest-snapshot: 29.7.0 + jest-snapshot: 29.7.0(supports-color@9.4.0) jest-util: 29.7.0 slash: 3.0.0 strip-bom: 4.0.0 transitivePeerDependencies: - supports-color - jest-snapshot@29.7.0: + jest-snapshot@29.7.0(supports-color@9.4.0): dependencies: - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) - '@babel/types': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/generator': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/types': 7.29.7 '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 + '@jest/transform': 29.7.0(supports-color@9.4.0) '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7(supports-color@9.4.0)) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -11083,14 +11100,14 @@ snapshots: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.7.4 + semver: 7.8.5 transitivePeerDependencies: - supports-color jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 25.5.0 + '@types/node': 26.1.1 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -11111,11 +11128,11 @@ snapshots: chalk: 3.0.0 prompts: 2.4.2 - jest-watch-typeahead@2.2.1(jest@29.7.0(@types/node@25.5.0)): + jest-watch-typeahead@2.2.1(jest@29.7.0(@types/node@26.1.1)(supports-color@9.4.0)): dependencies: ansi-escapes: 6.2.1 chalk: 4.1.2 - jest: 29.7.0(@types/node@25.5.0) + jest: 29.7.0(@types/node@26.1.1)(supports-color@9.4.0) jest-regex-util: 29.6.3 jest-watcher: 29.7.0 slash: 5.1.0 @@ -11126,7 +11143,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.5.0 + '@types/node': 26.1.1 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -11135,17 +11152,17 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 25.5.0 + '@types/node': 26.1.1 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@25.5.0): + jest@29.7.0(@types/node@26.1.1)(supports-color@9.4.0): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(supports-color@9.4.0) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@25.5.0) + jest-cli: 29.7.0(@types/node@26.1.1)(supports-color@9.4.0) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -11154,26 +11171,16 @@ snapshots: jimp-compact@0.16.1: {} - jiti@2.6.1: {} - - joi@17.13.3: - dependencies: - '@hapi/hoek': 9.3.0 - '@hapi/topo': 5.1.0 - '@sideway/address': 4.1.5 - '@sideway/formula': 3.0.1 - '@sideway/pinpoint': 2.0.0 - - js-md4@0.3.2: {} + jiti@2.7.0: {} js-tokens@4.0.0: {} - js-yaml@3.14.2: + js-yaml@3.15.0: dependencies: argparse: 1.0.10 esprima: 4.0.1 - js-yaml@4.1.1: + js-yaml@4.3.0: dependencies: argparse: 2.0.1 @@ -11181,10 +11188,10 @@ snapshots: jsc-safe-url@0.2.4: {} - jsdom@20.0.3: + jsdom@20.0.3(supports-color@9.4.0): dependencies: abab: 2.0.6 - acorn: 8.16.0 + acorn: 8.17.0 acorn-globals: 7.0.1 cssom: 0.5.0 cssstyle: 2.3.0 @@ -11192,12 +11199,12 @@ snapshots: decimal.js: 10.6.0 domexception: 4.0.0 escodegen: 2.1.0 - form-data: 4.0.5 + form-data: 4.0.6 html-encoding-sniffer: 3.0.0 - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 + http-proxy-agent: 5.0.0(supports-color@9.4.0) + https-proxy-agent: 5.0.1(supports-color@9.4.0) is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.23 + nwsapi: 2.2.24 parse5: 7.3.0 saxes: 6.0.0 symbol-tree: 3.2.4 @@ -11207,7 +11214,7 @@ snapshots: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.20.0 + ws: 8.21.1 xml-name-validator: 4.0.0 transitivePeerDependencies: - bufferutil @@ -11224,24 +11231,11 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} - json5@2.2.3: {} - - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 - - jsonwebtoken@9.0.3: + json5@1.0.2: dependencies: - jws: 4.0.1 - lodash.includes: 4.3.0 - lodash.isboolean: 3.0.3 - lodash.isinteger: 4.0.4 - lodash.isnumber: 3.0.3 - lodash.isplainobject: 4.0.6 - lodash.isstring: 4.0.1 - lodash.once: 4.1.1 - ms: 2.1.3 - semver: 7.7.4 + minimist: 1.2.8 + + json5@2.2.3: {} jsx-ast-utils@3.3.5: dependencies: @@ -11250,29 +11244,17 @@ snapshots: object.assign: 4.1.7 object.values: 1.2.1 - jwa@2.0.1: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jws@4.0.1: - dependencies: - jwa: 2.0.1 - safe-buffer: 5.2.1 - keyv@4.5.4: dependencies: json-buffer: 3.0.1 kleur@3.0.3: {} - lan-network@0.2.0: {} + lan-network@0.2.1: {} - launch-editor@2.13.2: + launder@1.7.1: dependencies: - picocolors: 1.1.1 - shell-quote: 1.8.3 + dayjs: 1.11.21 leven@3.1.0: {} @@ -11281,9 +11263,9 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - lighthouse-logger@1.4.2: + lighthouse-logger@1.4.2(supports-color@9.4.0): dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@9.4.0) marky: 1.3.0 transitivePeerDependencies: - supports-color @@ -11384,43 +11366,24 @@ snapshots: dependencies: p-locate: 5.0.0 - locate-path@7.2.0: + locate-path@8.0.0: dependencies: p-locate: 6.0.0 - lodash-es@4.17.23: {} + lodash-es@4.18.1: {} lodash.debounce@4.0.8: {} - lodash.includes@4.3.0: {} - - lodash.isboolean@3.0.3: {} - - lodash.isinteger@4.0.4: {} - - lodash.isnumber@3.0.3: {} - - lodash.isplainobject@4.0.6: {} - - lodash.isstring@4.0.1: {} - lodash.merge@4.6.2: {} - lodash.once@4.1.1: {} - lodash.throttle@4.1.1: {} - lodash@4.17.23: {} + lodash@4.18.1: {} log-symbols@2.2.0: dependencies: chalk: 2.4.2 - log-symbols@4.1.0: - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - log-update@4.0.0: dependencies: ansi-escapes: 4.3.2 @@ -11428,41 +11391,20 @@ snapshots: slice-ansi: 4.0.0 wrap-ansi: 6.2.0 - logkitty@0.7.1: - dependencies: - ansi-fragments: 0.2.1 - dayjs: 1.11.20 - yargs: 15.4.1 - long@5.3.2: {} loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 - lottie-ios@3.2.3: {} - - lottie-ios@3.5.0: {} - - lottie-react-native@5.1.6(lottie-ios@3.2.3)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): - dependencies: - invariant: 2.2.4 - lottie-ios: 3.2.3 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-safe-modules: 1.0.3(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)) - - lottie-react-native@5.1.6(lottie-ios@3.5.0)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + lottie-react-native@7.3.8(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - invariant: 2.2.4 - lottie-ios: 3.5.0 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-safe-modules: 1.0.3(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) lru-cache@10.4.3: {} - lru-cache@11.2.7: {} + lru-cache@11.5.2: {} lru-cache@5.1.1: dependencies: @@ -11470,7 +11412,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.4 + semver: 7.8.5 make-plural@7.5.0: {} @@ -11482,358 +11424,191 @@ snapshots: math-intrinsics@1.1.0: {} - media-typer@1.1.0: {} - - memoize-one@5.2.1: {} - - merge-stream@2.0.0: {} - - merge2@1.4.1: {} - - metro-babel-transformer@0.83.3: - dependencies: - '@babel/core': 7.29.0 - flow-enums-runtime: 0.0.6 - hermes-parser: 0.32.0 - nullthrows: 1.1.1 - transitivePeerDependencies: - - supports-color - - metro-babel-transformer@0.83.5: - dependencies: - '@babel/core': 7.29.0 - flow-enums-runtime: 0.0.6 - hermes-parser: 0.33.3 - nullthrows: 1.1.1 - transitivePeerDependencies: - - supports-color - - metro-cache-key@0.83.3: - dependencies: - flow-enums-runtime: 0.0.6 - - metro-cache-key@0.83.5: - dependencies: - flow-enums-runtime: 0.0.6 + mdn-data@2.0.14: {} - metro-cache@0.83.3: - dependencies: - exponential-backoff: 3.1.3 - flow-enums-runtime: 0.0.6 - https-proxy-agent: 7.0.6 - metro-core: 0.83.3 - transitivePeerDependencies: - - supports-color + media-typer@0.3.0: {} - metro-cache@0.83.5: - dependencies: - exponential-backoff: 3.1.3 - flow-enums-runtime: 0.0.6 - https-proxy-agent: 7.0.6 - metro-core: 0.83.5 - transitivePeerDependencies: - - supports-color + media-typer@1.1.1: {} - metro-config@0.83.3: - dependencies: - connect: 3.7.0 - flow-enums-runtime: 0.0.6 - jest-validate: 29.7.0 - metro: 0.83.3 - metro-cache: 0.83.3 - metro-core: 0.83.3 - metro-runtime: 0.83.3 - yaml: 2.8.3 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate + memoize-one@5.2.1: {} - metro-config@0.83.5: - dependencies: - connect: 3.7.0 - flow-enums-runtime: 0.0.6 - jest-validate: 29.7.0 - metro: 0.83.5 - metro-cache: 0.83.5 - metro-core: 0.83.5 - metro-runtime: 0.83.5 - yaml: 2.8.3 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate + merge-descriptors@1.0.3: {} - metro-core@0.83.3: - dependencies: - flow-enums-runtime: 0.0.6 - lodash.throttle: 4.1.1 - metro-resolver: 0.83.3 + merge-descriptors@2.0.0: {} - metro-core@0.83.5: - dependencies: - flow-enums-runtime: 0.0.6 - lodash.throttle: 4.1.1 - metro-resolver: 0.83.5 + merge-stream@2.0.0: {} - metro-file-map@0.83.3: - dependencies: - debug: 4.4.3(supports-color@9.4.0) - fb-watchman: 2.0.2 - flow-enums-runtime: 0.0.6 - graceful-fs: 4.2.11 - invariant: 2.2.4 - jest-worker: 29.7.0 - micromatch: 4.0.8 - nullthrows: 1.1.1 - walker: 1.0.8 - transitivePeerDependencies: - - supports-color + methods@1.1.2: {} - metro-file-map@0.83.5: + metro-babel-transformer@0.84.4(supports-color@9.4.0): dependencies: - debug: 4.4.3(supports-color@9.4.0) - fb-watchman: 2.0.2 + '@babel/core': 7.29.7(supports-color@9.4.0) flow-enums-runtime: 0.0.6 - graceful-fs: 4.2.11 - invariant: 2.2.4 - jest-worker: 29.7.0 - micromatch: 4.0.8 + hermes-parser: 0.35.0 + metro-cache-key: 0.84.4 nullthrows: 1.1.1 - walker: 1.0.8 transitivePeerDependencies: - supports-color - metro-minify-terser@0.83.3: - dependencies: - flow-enums-runtime: 0.0.6 - terser: 5.46.1 - - metro-minify-terser@0.83.5: - dependencies: - flow-enums-runtime: 0.0.6 - terser: 5.46.1 - - metro-resolver@0.83.3: - dependencies: - flow-enums-runtime: 0.0.6 - - metro-resolver@0.83.5: + metro-cache-key@0.84.4: dependencies: flow-enums-runtime: 0.0.6 - metro-runtime@0.83.3: + metro-cache@0.84.4(supports-color@9.4.0): dependencies: - '@babel/runtime': 7.29.2 + exponential-backoff: 3.1.3 flow-enums-runtime: 0.0.6 + https-proxy-agent: 7.0.6(supports-color@9.4.0) + metro-core: 0.84.4 + transitivePeerDependencies: + - supports-color - metro-runtime@0.83.5: + metro-config@0.84.4(supports-color@9.4.0): dependencies: - '@babel/runtime': 7.29.2 + connect: 3.7.0(supports-color@9.4.0) flow-enums-runtime: 0.0.6 + jest-validate: 29.7.0 + metro: 0.84.4(supports-color@9.4.0) + metro-cache: 0.84.4(supports-color@9.4.0) + metro-core: 0.84.4 + metro-runtime: 0.84.4 + yaml: 2.9.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate - metro-source-map@0.83.3: + metro-core@0.84.4: dependencies: - '@babel/traverse': 7.29.0 - '@babel/traverse--for-generate-function-map': '@babel/traverse@7.29.0' - '@babel/types': 7.29.0 flow-enums-runtime: 0.0.6 - invariant: 2.2.4 - metro-symbolicate: 0.83.3 - nullthrows: 1.1.1 - ob1: 0.83.3 - source-map: 0.5.7 - vlq: 1.0.1 - transitivePeerDependencies: - - supports-color + lodash.throttle: 4.1.1 + metro-resolver: 0.84.4 - metro-source-map@0.83.5: + metro-file-map@0.84.4(supports-color@9.4.0): dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + debug: 4.4.3(supports-color@9.4.0) + fb-watchman: 2.0.2 flow-enums-runtime: 0.0.6 + graceful-fs: 4.2.11 invariant: 2.2.4 - metro-symbolicate: 0.83.5 + jest-worker: 29.7.0 + micromatch: 4.0.8 nullthrows: 1.1.1 - ob1: 0.83.5 - source-map: 0.5.7 - vlq: 1.0.1 + walker: 1.0.8 transitivePeerDependencies: - supports-color - metro-symbolicate@0.83.3: + metro-minify-terser@0.84.4: dependencies: flow-enums-runtime: 0.0.6 - invariant: 2.2.4 - metro-source-map: 0.83.3 - nullthrows: 1.1.1 - source-map: 0.5.7 - vlq: 1.0.1 - transitivePeerDependencies: - - supports-color + terser: 5.49.0 - metro-symbolicate@0.83.5: + metro-resolver@0.84.4: dependencies: flow-enums-runtime: 0.0.6 - invariant: 2.2.4 - metro-source-map: 0.83.5 - nullthrows: 1.1.1 - source-map: 0.5.7 - vlq: 1.0.1 - transitivePeerDependencies: - - supports-color - metro-transform-plugins@0.83.3: + metro-runtime@0.84.4: dependencies: - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/runtime': 7.29.7 flow-enums-runtime: 0.0.6 - nullthrows: 1.1.1 - transitivePeerDependencies: - - supports-color - metro-transform-plugins@0.83.5: + metro-source-map@0.84.4(supports-color@9.4.0): dependencies: - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.7(supports-color@9.4.0) + '@babel/types': 7.29.7 flow-enums-runtime: 0.0.6 + invariant: 2.2.4 + metro-symbolicate: 0.84.4(supports-color@9.4.0) nullthrows: 1.1.1 + ob1: 0.84.4 + source-map: 0.5.7 + vlq: 1.0.1 transitivePeerDependencies: - supports-color - metro-transform-worker@0.83.3: + metro-symbolicate@0.84.4(supports-color@9.4.0): dependencies: - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 flow-enums-runtime: 0.0.6 - metro: 0.83.3 - metro-babel-transformer: 0.83.3 - metro-cache: 0.83.3 - metro-cache-key: 0.83.3 - metro-minify-terser: 0.83.3 - metro-source-map: 0.83.3 - metro-transform-plugins: 0.83.3 + invariant: 2.2.4 + metro-source-map: 0.84.4(supports-color@9.4.0) nullthrows: 1.1.1 + source-map: 0.5.7 + vlq: 1.0.1 transitivePeerDependencies: - - bufferutil - supports-color - - utf-8-validate - metro-transform-worker@0.83.5: + metro-transform-plugins@0.84.4(supports-color@9.4.0): dependencies: - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/generator': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@9.4.0) flow-enums-runtime: 0.0.6 - metro: 0.83.5 - metro-babel-transformer: 0.83.5 - metro-cache: 0.83.5 - metro-cache-key: 0.83.5 - metro-minify-terser: 0.83.5 - metro-source-map: 0.83.5 - metro-transform-plugins: 0.83.5 nullthrows: 1.1.1 transitivePeerDependencies: - - bufferutil - supports-color - - utf-8-validate - metro@0.83.3: + metro-transform-worker@0.84.4(supports-color@9.4.0): dependencies: - '@babel/code-frame': 7.29.0 - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/parser': 7.29.2 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - accepts: 1.3.8 - chalk: 4.1.2 - ci-info: 2.0.0 - connect: 3.7.0 - debug: 4.4.3(supports-color@9.4.0) - error-stack-parser: 2.1.4 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/generator': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 flow-enums-runtime: 0.0.6 - graceful-fs: 4.2.11 - hermes-parser: 0.32.0 - image-size: 1.2.1 - invariant: 2.2.4 - jest-worker: 29.7.0 - jsc-safe-url: 0.2.4 - lodash.throttle: 4.1.1 - metro-babel-transformer: 0.83.3 - metro-cache: 0.83.3 - metro-cache-key: 0.83.3 - metro-config: 0.83.3 - metro-core: 0.83.3 - metro-file-map: 0.83.3 - metro-resolver: 0.83.3 - metro-runtime: 0.83.3 - metro-source-map: 0.83.3 - metro-symbolicate: 0.83.3 - metro-transform-plugins: 0.83.3 - metro-transform-worker: 0.83.3 - mime-types: 2.1.35 + metro: 0.84.4(supports-color@9.4.0) + metro-babel-transformer: 0.84.4(supports-color@9.4.0) + metro-cache: 0.84.4(supports-color@9.4.0) + metro-cache-key: 0.84.4 + metro-minify-terser: 0.84.4 + metro-source-map: 0.84.4(supports-color@9.4.0) + metro-transform-plugins: 0.84.4(supports-color@9.4.0) nullthrows: 1.1.1 - serialize-error: 2.1.0 - source-map: 0.5.7 - throat: 5.0.0 - ws: 7.5.10 - yargs: 17.7.2 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - metro@0.83.5: + metro@0.84.4(supports-color@9.4.0): dependencies: - '@babel/code-frame': 7.29.0 - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/parser': 7.29.2 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/generator': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@9.4.0) + '@babel/types': 7.29.7 accepts: 2.0.0 - chalk: 4.1.2 ci-info: 2.0.0 - connect: 3.7.0 + connect: 3.7.0(supports-color@9.4.0) debug: 4.4.3(supports-color@9.4.0) error-stack-parser: 2.1.4 flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 - hermes-parser: 0.33.3 + hermes-parser: 0.35.0 image-size: 1.2.1 invariant: 2.2.4 jest-worker: 29.7.0 jsc-safe-url: 0.2.4 lodash.throttle: 4.1.1 - metro-babel-transformer: 0.83.5 - metro-cache: 0.83.5 - metro-cache-key: 0.83.5 - metro-config: 0.83.5 - metro-core: 0.83.5 - metro-file-map: 0.83.5 - metro-resolver: 0.83.5 - metro-runtime: 0.83.5 - metro-source-map: 0.83.5 - metro-symbolicate: 0.83.5 - metro-transform-plugins: 0.83.5 - metro-transform-worker: 0.83.5 + metro-babel-transformer: 0.84.4(supports-color@9.4.0) + metro-cache: 0.84.4(supports-color@9.4.0) + metro-cache-key: 0.84.4 + metro-config: 0.84.4(supports-color@9.4.0) + metro-core: 0.84.4 + metro-file-map: 0.84.4(supports-color@9.4.0) + metro-resolver: 0.84.4 + metro-runtime: 0.84.4 + metro-source-map: 0.84.4(supports-color@9.4.0) + metro-symbolicate: 0.84.4(supports-color@9.4.0) + metro-transform-plugins: 0.84.4(supports-color@9.4.0) + metro-transform-worker: 0.84.4(supports-color@9.4.0) mime-types: 3.0.2 nullthrows: 1.1.1 serialize-error: 2.1.0 source-map: 0.5.7 throat: 5.0.0 - ws: 7.5.10 - yargs: 17.7.2 + ws: 7.5.13 + yargs: 17.7.3 transitivePeerDependencies: - bufferutil - supports-color @@ -11858,8 +11633,6 @@ snapshots: mime@1.6.0: {} - mime@2.6.0: {} - mimic-fn@1.2.0: {} mimic-fn@2.1.0: {} @@ -11870,15 +11643,15 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.5 + brace-expansion: 5.0.7 minimatch@3.1.5: dependencies: - brace-expansion: 1.1.13 + brace-expansion: 1.1.16 minimatch@8.0.7: dependencies: - brace-expansion: 2.0.3 + brace-expansion: 2.1.2 minimist@1.2.8: {} @@ -11890,29 +11663,27 @@ snapshots: mkdirp@1.0.4: {} - ms@2.0.0: {} - - ms@2.1.3: {} - - mssql@11.0.1(@azure/core-client@1.10.1): + morgan@1.11.0(supports-color@9.4.0): dependencies: - '@tediousjs/connection-string': 0.5.0 - commander: 11.1.0 - debug: 4.4.3(supports-color@9.4.0) - rfdc: 1.4.1 - tarn: 3.0.2 - tedious: 18.6.2(@azure/core-client@1.10.1) + basic-auth: 2.0.1 + debug: 2.6.9(supports-color@9.4.0) + depd: 2.0.0 + on-finished: 2.4.1 + on-headers: 1.1.0 transitivePeerDependencies: - - '@azure/core-client' - supports-color - multitars@0.2.4: {} + ms@2.0.0: {} + + ms@2.1.3: {} + + multitars@1.0.0: {} - nanoid@3.3.11: {} + nanoid@3.3.16: {} napi-build-utils@2.0.0: {} - native-duplexpair@1.0.0: {} + napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} @@ -11922,13 +11693,22 @@ snapshots: negotiator@1.0.0: {} - nocache@3.0.4: {} + nitrogen@0.36.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): + dependencies: + chalk: 5.6.2 + react-native-nitro-modules: 0.36.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + ts-morph: 28.0.0 + yargs: 18.0.0 + zod: 4.4.3 + transitivePeerDependencies: + - react + - react-native - node-abi@3.89.0: + node-abi@3.94.0: dependencies: - semver: 7.7.4 + semver: 7.8.5 - node-exports-info@1.6.0: + node-exports-info@1.6.2: dependencies: array.prototype.flatmap: 1.3.3 es-errors: 1.3.0 @@ -11939,9 +11719,7 @@ snapshots: node-int64@0.4.0: {} - node-releases@2.0.36: {} - - node-stream-zip@1.15.0: {} + node-releases@2.0.51: {} normalize-path@3.0.0: {} @@ -11949,7 +11727,7 @@ snapshots: dependencies: hosted-git-info: 7.0.2 proc-log: 4.2.0 - semver: 7.7.4 + semver: 7.8.5 validate-npm-package-name: 5.0.1 npm-run-path@4.0.1: @@ -11962,13 +11740,9 @@ snapshots: nullthrows@1.1.1: {} - nwsapi@2.2.23: {} - - ob1@0.83.3: - dependencies: - flow-enums-runtime: 0.0.6 + nwsapi@2.2.24: {} - ob1@0.83.5: + ob1@0.84.4: dependencies: flow-enums-runtime: 0.0.6 @@ -11980,33 +11754,39 @@ snapshots: object.assign@4.1.7: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 has-symbols: 1.1.0 object-keys: 1.1.1 object.entries@1.1.9: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 object.fromentries@2.0.8: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 - es-object-atoms: 1.1.1 + es-abstract: 1.24.2 object.values@1.2.1: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 on-finished@2.3.0: dependencies: @@ -12030,17 +11810,6 @@ snapshots: dependencies: mimic-fn: 2.1.0 - open@10.2.0: - dependencies: - default-browser: 5.5.0 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - wsl-utils: 0.1.0 - - open@6.4.0: - dependencies: - is-wsl: 1.1.0 - open@7.4.2: dependencies: is-docker: 2.2.1 @@ -12070,18 +11839,6 @@ snapshots: strip-ansi: 5.2.0 wcwidth: 1.0.1 - ora@5.4.1: - dependencies: - bl: 4.1.0 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-spinners: 2.9.2 - is-interactive: 1.0.0 - is-unicode-supported: 0.1.0 - log-symbols: 4.1.0 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - own-keys@1.0.1: dependencies: get-intrinsic: 1.3.0 @@ -12128,7 +11885,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.29.0 + '@babel/code-frame': 7.29.7 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -12150,14 +11907,12 @@ snapshots: parseurl@1.3.3: {} + path-browserify@1.0.1: {} + path-exists@3.0.0: {} path-exists@4.0.0: {} - path-exists@5.0.0: {} - - path-expression-matcher@1.2.0: {} - path-extra@1.0.3: {} path-is-absolute@1.0.1: {} @@ -12173,14 +11928,18 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.2.7 + lru-cache: 11.5.2 minipass: 7.1.3 + path-to-regexp@0.1.13: {} + + path-to-regexp@8.4.2: {} + picocolors@1.1.1: {} picomatch@2.3.2: {} - picomatch@4.0.4: {} + picomatch@4.0.5: {} pidtree@0.5.0: {} @@ -12194,9 +11953,9 @@ snapshots: dependencies: find-up: 3.0.0 - plist@3.1.0: + plist@3.1.1: dependencies: - '@xmldom/xmldom': 0.8.12 + '@xmldom/xmldom': 0.9.10 base64-js: 1.5.1 xmlbuilder: 15.1.1 @@ -12204,15 +11963,9 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss@8.4.49: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postcss@8.5.8: + postcss@8.5.19: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.16 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -12224,11 +11977,11 @@ snapshots: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 - node-abi: 3.89.0 + node-abi: 3.94.0 pump: 3.0.4 rc: 1.2.8 simple-get: 4.0.1 - tar-fs: 2.1.4 + tar-fs: 2.1.5 tunnel-agent: 0.6.0 prelude-ls@1.2.1: {} @@ -12241,16 +11994,15 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 - pretty-format@30.3.0: + pretty-format@30.4.1: dependencies: - '@jest/schemas': 30.0.5 + '@jest/schemas': 30.4.1 ansi-styles: 5.2.0 - react-is: 18.3.1 + react-is-18: react-is@18.3.1 + react-is-19: react-is@19.2.7 proc-log@4.2.0: {} - process@0.11.10: {} - progress@2.0.3: {} promise@8.3.0: @@ -12268,21 +12020,15 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 - protobufjs@8.0.0: - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.4 - '@protobufjs/eventemitter': 1.1.0 - '@protobufjs/fetch': 1.1.0 - '@protobufjs/float': 1.0.2 - '@protobufjs/inquire': 1.1.0 - '@protobufjs/path': 1.1.2 - '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 - '@types/node': 25.5.0 + protobufjs@8.7.1: + dependencies: long: 5.3.2 + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + psl@1.15.0: dependencies: punycode: 2.3.1 @@ -12296,9 +12042,10 @@ snapshots: pure-rand@6.1.0: {} - qs@6.15.0: + qs@6.15.3: dependencies: - side-channel: 1.1.0 + es-define-property: 1.0.1 + side-channel: 1.1.1 query-string@7.1.3: dependencies: @@ -12309,19 +12056,24 @@ snapshots: querystringify@2.2.0: {} - queue-microtask@1.2.3: {} - queue@6.0.2: dependencies: inherits: 2.0.4 range-parser@1.2.1: {} + raw-body@2.5.3: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + raw-body@3.0.2: dependencies: bytes: 3.1.2 http-errors: 2.0.1 - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 unpipe: 1.0.0 rc@1.2.8: @@ -12333,252 +12085,212 @@ snapshots: react-devtools-core@6.1.5: dependencies: - shell-quote: 1.8.3 - ws: 7.5.10 + shell-quote: 1.10.0 + ws: 7.5.13 transitivePeerDependencies: - bufferutil - utf-8-validate - react-freeze@1.0.4(react@19.2.4): + react-freeze@1.0.4(react@19.2.3): dependencies: - react: 19.2.4 + react: 19.2.3 react-is@16.13.1: {} react-is@18.3.1: {} - react-is@19.2.4: {} - - react-native-background-actions@4.0.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)): - dependencies: - eventemitter3: 4.0.7 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - - react-native-config@1.6.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): - dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react-is@19.2.7: {} - react-native-device-info@15.0.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)): + react-native-device-info@15.0.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)): dependencies: - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - react-native-draggable-flatlist@4.0.3(@babel/core@7.29.0)(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-reanimated@4.3.0(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)): + react-native-draggable-flatlist@4.0.3(caa27418ce3e0b0439d72a52bab7509e): dependencies: - '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-gesture-handler: 2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-reanimated: 4.3.0(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-native-gesture-handler: 2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + react-native-reanimated: 4.5.2(react-native-worklets@0.10.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) transitivePeerDependencies: - '@babel/core' - supports-color - react-native-drawer-layout@4.2.2(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-reanimated@4.3.0(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-drawer-layout@4.2.8(303ab35fdba57cc5c055f2029a2df72d): dependencies: color: 4.2.3 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-gesture-handler: 2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-reanimated: 4.3.0(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - use-latest-callback: 0.2.6(react@19.2.4) - - react-native-edge-to-edge@1.8.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): - dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - - react-native-error-boundary@3.1.0(react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): - dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-safe-area-context: 5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-native-gesture-handler: 2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + react-native-reanimated: 4.5.2(react-native-worklets@0.10.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + use-latest-callback: 0.2.6(react@19.2.3) - react-native-file-access@3.2.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-edge-to-edge@1.8.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - react-native-file-access@4.0.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-error-boundary@3.1.0(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-native-safe-area-context: 5.8.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) - react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-gesture-handler@2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: '@egjs/hammerjs': 2.0.17 + '@types/react-test-renderer': 19.1.0 hoist-non-react-statics: 3.3.2 invariant: 2.2.4 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - - react-native-is-edge-to-edge@1.3.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): - dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - react-native-linear-gradient@2.8.3(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-is-edge-to-edge@1.3.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - react-native-lottie-splash-screen@1.1.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-linear-gradient@2.8.3(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - lottie-ios: 3.2.3 - lottie-react-native: 5.1.6(lottie-ios@3.2.3)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - transitivePeerDependencies: - - react - - react-native-windows + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - react-native-mmkv@4.3.0(react-native-nitro-modules@0.35.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-mmkv@4.3.2(react-native-nitro-modules@0.36.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-nitro-modules: 0.35.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-native-nitro-modules: 0.36.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) - react-native-nitro-modules@0.35.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-nitro-modules@0.36.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - react-native-pager-view@8.0.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-pager-view@8.0.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - react-native-paper@5.15.0(react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-paper@5.15.3(react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - '@callstack/react-theme-provider': 3.0.9(react@19.2.4) + '@callstack/react-theme-provider': 3.0.9(react@19.2.3) color: 3.2.1 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-safe-area-context: 5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - use-latest-callback: 0.2.6(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-native-safe-area-context: 5.8.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + use-latest-callback: 0.2.6(react@19.2.3) - react-native-reanimated@4.3.0(react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-reanimated@4.5.2(react-native-worklets@0.10.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-is-edge-to-edge: 1.3.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-worklets: 0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - semver: 7.7.4 + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-native-is-edge-to-edge: 1.3.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + react-native-worklets: 0.10.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0) + semver: 7.8.5 - react-native-saf-x@2.2.3(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-safe-area-context@5.8.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) - react-native-safe-area-context@5.7.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-screens@4.26.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react: 19.2.3 + react-freeze: 1.0.4(react@19.2.3) + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + warn-once: 0.1.1 - react-native-safe-modules@1.0.3(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)): + react-native-shimmer-placeholder@2.0.9(prop-types@15.8.1)(react-native-linear-gradient@2.8.3(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)): dependencies: - dedent: 0.6.0 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + prop-types: 15.8.1 + react-native-linear-gradient: 2.8.3(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) - react-native-screens@4.24.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - react: 19.2.4 - react-freeze: 1.0.4(react@19.2.4) - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + css-select: 5.2.2 + css-tree: 1.1.3 + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) warn-once: 0.1.1 - react-native-shimmer-placeholder@2.0.9(prop-types@15.8.1)(react-native-linear-gradient@2.8.3(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)): + react-native-tab-view@4.3.2(react-native-pager-view@8.0.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: - prop-types: 15.8.1 - react-native-linear-gradient: 2.8.3(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - - react-native-tab-view@4.3.0(react-native-pager-view@8.0.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): - dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - react-native-pager-view: 8.0.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - use-latest-callback: 0.2.6(react@19.2.4) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + react-native-pager-view: 8.0.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) + use-latest-callback: 0.2.6(react@19.2.3) - react-native-url-polyfill@3.0.0(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4)): + react-native-url-polyfill@3.0.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0)): dependencies: - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) whatwg-url-without-unicode: 8.0.0-3 - react-native-webview@13.16.1(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): + react-native-webview@13.17.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3): dependencies: + '@typescript/native-preview': 7.0.0-dev.20260707.2 escape-string-regexp: 4.0.0 invariant: 2.2.4 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - - react-native-worklets@0.8.1(@babel/core@7.29.0)(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) - '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@react-native/metro-config': 0.83.4(@babel/core@7.29.0) + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + + react-native-worklets@0.10.2(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0): + dependencies: + '@babel/core': 7.29.7(supports-color@9.4.0) + '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0)) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) + '@babel/types': 7.29.7 + '@react-native/metro-config': 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0) convert-source-map: 2.0.0 - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - semver: 7.7.4 + react: 19.2.3 + react-native: 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0) + semver: 7.8.5 transitivePeerDependencies: - supports-color - react-native-zip-archive@6.1.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): - dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - - react-native-zip-archive@7.0.2(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): - dependencies: - react: 19.2.4 - react-native: 0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4) - - react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4): + react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0): dependencies: - '@jest/create-cache-key-function': 29.7.0 - '@react-native/assets-registry': 0.83.4 - '@react-native/codegen': 0.83.4(@babel/core@7.29.0) - '@react-native/community-cli-plugin': 0.83.4(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0)) - '@react-native/gradle-plugin': 0.83.4 - '@react-native/js-polyfills': 0.83.4 - '@react-native/normalize-colors': 0.83.4 - '@react-native/virtualized-lists': 0.83.4(@types/react@19.2.14)(react-native@0.83.4(@babel/core@7.29.0)(@react-native-community/cli@20.1.3(typescript@5.9.3))(@react-native/metro-config@0.83.4(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@react-native/assets-registry': 0.86.0 + '@react-native/codegen': 0.86.0(@babel/core@7.29.7(supports-color@9.4.0)) + '@react-native/community-cli-plugin': 0.86.0(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(supports-color@9.4.0) + '@react-native/gradle-plugin': 0.86.0 + '@react-native/js-polyfills': 0.86.0 + '@react-native/normalize-colors': 0.86.0 + '@react-native/virtualized-lists': 0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(@react-native/jest-preset@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(supports-color@9.4.0))(@types/react@19.2.17)(react@19.2.3)(supports-color@9.4.0))(react@19.2.3) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 - babel-jest: 29.7.0(@babel/core@7.29.0) - babel-plugin-syntax-hermes-parser: 0.32.0 + babel-plugin-syntax-hermes-parser: 0.36.0 base64-js: 1.5.1 commander: 12.1.0 flow-enums-runtime: 0.0.6 - glob: 7.2.3 - hermes-compiler: 0.14.1 + hermes-compiler: 250829098.0.14 invariant: 2.2.4 - jest-environment-node: 29.7.0 memoize-one: 5.2.1 - metro-runtime: 0.83.5 - metro-source-map: 0.83.5 + metro-runtime: 0.84.4 + metro-source-map: 0.84.4(supports-color@9.4.0) nullthrows: 1.1.1 pretty-format: 29.7.0 promise: 8.3.0 - react: 19.2.4 + react: 19.2.3 react-devtools-core: 6.1.5 react-refresh: 0.14.2 regenerator-runtime: 0.13.11 scheduler: 0.27.0 - semver: 7.7.4 + semver: 7.8.5 stacktrace-parser: 0.1.11 + tinyglobby: 0.2.17 whatwg-fetch: 3.6.20 - ws: 7.5.10 - yargs: 17.7.2 + ws: 7.5.13 + yargs: 17.7.3 optionalDependencies: - '@types/react': 19.2.14 + '@react-native/jest-preset': 0.86.0(@babel/core@7.29.7(supports-color@9.4.0))(react@19.2.3)(supports-color@9.4.0) + '@types/react': 19.2.17 transitivePeerDependencies: - '@babel/core' - '@react-native-community/cli' @@ -12589,19 +12301,13 @@ snapshots: react-refresh@0.14.2: {} - react-test-renderer@19.2.0(react@19.2.4): - dependencies: - react: 19.2.4 - react-is: 19.2.4 - scheduler: 0.27.0 - - react-test-renderer@19.2.4(react@19.2.4): + react-test-renderer@19.2.3(react@19.2.3): dependencies: - react: 19.2.4 - react-is: 19.2.4 + react: 19.2.3 + react-is: 19.2.7 scheduler: 0.27.0 - react@19.2.4: {} + react@19.2.3: {} readable-stream@3.6.2: dependencies: @@ -12609,14 +12315,6 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 - readable-stream@4.7.0: - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 - redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -12624,11 +12322,11 @@ snapshots: reflect.getprototypeof@1.0.10: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 get-proto: 1.0.1 which-builtin-type: 1.2.1 @@ -12643,7 +12341,7 @@ snapshots: regexp.prototype.flags@1.5.4: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-errors: 1.3.0 get-proto: 1.0.1 @@ -12655,20 +12353,18 @@ snapshots: regenerate: 1.4.2 regenerate-unicode-properties: 10.2.2 regjsgen: 0.8.0 - regjsparser: 0.13.0 + regjsparser: 0.13.2 unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.2.1 regjsgen@0.8.0: {} - regjsparser@0.13.0: + regjsparser@0.13.2: dependencies: jsesc: 3.1.0 require-directory@2.1.1: {} - require-main-filename@2.0.0: {} - require-resolve@0.0.2: dependencies: x-path: 0.0.2 @@ -12691,17 +12387,18 @@ snapshots: resolve.exports@2.0.3: {} - resolve@1.22.11: + resolve@1.22.12: dependencies: - is-core-module: 2.16.1 + es-errors: 1.3.0 + is-core-module: 2.16.2 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@2.0.0-next.6: + resolve@2.0.0-next.7: dependencies: es-errors: 1.3.0 - is-core-module: 2.16.1 - node-exports-info: 1.6.0 + is-core-module: 2.16.2 + node-exports-info: 1.6.2 object-keys: 1.1.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -12716,34 +12413,34 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 - reusify@1.1.0: {} - rfdc@1.4.1: {} - rimraf@3.0.2: + router@2.2.0(supports-color@9.4.0): dependencies: - glob: 7.2.3 + debug: 4.4.3(supports-color@9.4.0) + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color rtl-detect@1.1.2: {} - run-applescript@7.1.0: {} - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - rxjs@7.8.2: dependencies: tslib: 2.8.1 - safe-array-concat@1.1.3: + safe-array-concat@1.1.4: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 get-intrinsic: 1.3.0 has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safe-push-apply@1.0.0: @@ -12759,14 +12456,15 @@ snapshots: safer-buffer@2.1.2: {} - sanitize-html@2.17.2: + sanitize-html@2.17.6: dependencies: deepmerge: 4.3.1 escape-string-regexp: 4.0.0 - htmlparser2: 10.1.0 + htmlparser2: 12.0.0 is-plain-object: 5.0.0 + launder: 1.7.1 parse-srcset: 1.0.2 - postcss: 8.5.8 + postcss: 8.5.19 sax@1.6.0: {} @@ -12778,11 +12476,11 @@ snapshots: semver@6.3.1: {} - semver@7.7.4: {} + semver@7.8.5: {} - send@0.19.2: + send@0.19.2(supports-color@9.4.0): dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@9.4.0) depd: 2.0.0 destroy: 1.2.0 encodeurl: 2.0.0 @@ -12798,20 +12496,43 @@ snapshots: transitivePeerDependencies: - supports-color + send@1.2.1(supports-color@9.4.0): + dependencies: + debug: 4.4.3(supports-color@9.4.0) + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + serialize-error@2.1.0: {} - serve-static@1.16.3: + serve-static@1.16.3(supports-color@9.4.0): dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.19.2 + send: 0.19.2(supports-color@9.4.0) transitivePeerDependencies: - supports-color - server-only@0.0.1: {} + serve-static@2.2.1(supports-color@9.4.0): + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1(supports-color@9.4.0) + transitivePeerDependencies: + - supports-color - set-blocking@2.0.0: {} + server-only@0.0.1: {} set-function-length@1.2.2: dependencies: @@ -12833,7 +12554,7 @@ snapshots: dependencies: dunder-proto: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 setprototypeof@1.2.0: {} @@ -12845,9 +12566,9 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.3: {} + shell-quote@1.10.0: {} - side-channel-list@1.0.0: + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 @@ -12867,11 +12588,11 @@ snapshots: object-inspect: 1.13.4 side-channel-map: 1.0.1 - side-channel@1.1.0: + side-channel@1.1.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 - side-channel-list: 1.0.0 + side-channel-list: 1.0.1 side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 @@ -12889,7 +12610,7 @@ snapshots: dependencies: bplist-creator: 0.1.0 bplist-parser: 0.3.1 - plist: 3.1.0 + plist: 3.1.1 simple-swizzle@0.2.4: dependencies: @@ -12901,12 +12622,6 @@ snapshots: slash@5.1.0: {} - slice-ansi@2.1.0: - dependencies: - ansi-styles: 3.2.1 - astral-regex: 1.0.0 - is-fullwidth-code-point: 2.0.0 - slice-ansi@3.0.0: dependencies: ansi-styles: 4.3.0 @@ -12924,7 +12639,7 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 4.0.0 - slugify@1.6.8: {} + slugify@1.6.9: {} source-map-js@1.2.1: {} @@ -12948,7 +12663,7 @@ snapshots: sprintf-js@1.0.3: {} - sprintf-js@1.1.3: {} + stable-hash@0.0.5: {} stack-generator@2.0.10: dependencies: @@ -12975,6 +12690,10 @@ snapshots: dependencies: type-fest: 0.7.1 + standard-navigation@0.0.8(react@19.2.3): + dependencies: + react: 19.2.3 + statuses@1.5.0: {} statuses@2.0.2: {} @@ -12986,9 +12705,13 @@ snapshots: stream-buffers@2.2.0: {} - strict-uri-encode@2.0.0: {} + stream-chain@2.2.5: {} + + stream-json@1.9.1: + dependencies: + stream-chain: 2.2.5 - strict-url-sanitise@0.0.1: {} + strict-uri-encode@2.0.0: {} string-argv@0.3.2: {} @@ -13016,49 +12739,56 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.2.0 + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + string.prototype.matchall@4.0.12: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 gopd: 1.2.0 has-symbols: 1.1.0 internal-slot: 1.1.0 regexp.prototype.flags: 1.5.4 set-function-name: 2.0.2 - side-channel: 1.1.0 + side-channel: 1.1.1 string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 - string.prototype.trim@1.2.10: + string.prototype.trim@1.2.11: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.1 - es-object-atoms: 1.1.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 - string.prototype.trimend@1.0.9: + string.prototype.trimend@1.0.10: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 string.prototype.trimstart@1.0.8: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 string_decoder@1.3.0: dependencies: @@ -13076,6 +12806,8 @@ snapshots: dependencies: ansi-regex: 6.2.2 + strip-bom@3.0.0: {} + strip-bom@4.0.0: {} strip-final-newline@2.0.0: {} @@ -13088,8 +12820,6 @@ snapshots: strip-json-comments@3.1.1: {} - strnum@2.2.2: {} - structured-headers@0.4.1: {} supports-color@5.5.0: @@ -13115,7 +12845,7 @@ snapshots: symbol-tree@3.2.4: {} - tar-fs@2.1.4: + tar-fs@2.1.5: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 @@ -13130,68 +12860,32 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - tarn@3.0.2: {} - - tedious@18.6.2(@azure/core-client@1.10.1): - dependencies: - '@azure/core-auth': 1.10.1 - '@azure/identity': 4.13.1 - '@azure/keyvault-keys': 4.10.0(@azure/core-client@1.10.1) - '@js-joda/core': 5.7.0 - '@types/node': 25.5.0 - bl: 6.1.6 - iconv-lite: 0.6.3 - js-md4: 0.3.2 - native-duplexpair: 1.0.0 - sprintf-js: 1.1.3 - transitivePeerDependencies: - - '@azure/core-client' - - supports-color - - tedious@19.2.1(@azure/core-client@1.10.1): - dependencies: - '@azure/core-auth': 1.10.1 - '@azure/identity': 4.13.1 - '@azure/keyvault-keys': 4.10.0(@azure/core-client@1.10.1) - '@js-joda/core': 5.7.0 - '@types/node': 25.5.0 - bl: 6.1.6 - iconv-lite: 0.7.2 - js-md4: 0.3.2 - native-duplexpair: 1.0.0 - sprintf-js: 1.1.3 - transitivePeerDependencies: - - '@azure/core-client' - - supports-color - terminal-link@2.1.1: dependencies: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 - terser@5.46.1: + terser@5.49.0: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.16.0 + acorn: 8.17.0 commander: 2.20.3 source-map-support: 0.5.21 test-exclude@6.0.0: dependencies: - '@istanbuljs/schema': 0.1.3 + '@istanbuljs/schema': 0.1.6 glob: 7.2.3 minimatch: 3.1.5 - text-table@0.2.0: {} - throat@5.0.0: {} through@2.3.8: {} - tinyglobby@0.2.15: + tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 tmpl@1.0.5: {} @@ -13214,9 +12908,21 @@ snapshots: dependencies: punycode: 2.3.1 - ts-api-utils@2.5.0(typescript@5.9.3): + ts-api-utils@2.5.0(typescript@6.0.3): + dependencies: + typescript: 6.0.3 + + ts-morph@28.0.0: + dependencies: + '@ts-morph/common': 0.29.0 + code-block-writer: 13.0.3 + + tsconfig-paths@3.15.0: dependencies: - typescript: 5.9.3 + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 tslib@2.8.1: {} @@ -13230,16 +12936,19 @@ snapshots: type-detect@4.0.8: {} - type-fest@0.20.2: {} - type-fest@0.21.3: {} type-fest@0.7.1: {} - type-is@2.0.1: + type-is@1.6.18: dependencies: - content-type: 1.0.5 - media-typer: 1.1.0 + media-typer: 0.3.0 + mime-types: 2.1.35 + + type-is@2.1.0: + dependencies: + content-type: 2.0.0 + media-typer: 1.1.1 mime-types: 3.0.2 typed-array-buffer@1.0.3: @@ -13250,7 +12959,7 @@ snapshots: typed-array-byte-length@1.0.3: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 @@ -13259,23 +12968,23 @@ snapshots: typed-array-byte-offset@1.0.4: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 reflect.getprototypeof: 1.0.10 - typed-array-length@1.0.7: + typed-array-length@1.0.8: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 is-typed-array: 1.1.15 possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript@5.9.3: {} + typescript@6.0.3: {} unbox-primitive@1.1.0: dependencies: @@ -13284,7 +12993,9 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - undici-types@7.18.2: {} + undici-types@8.3.0: {} + + undici@6.28.0: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -13297,17 +13008,42 @@ snapshots: unicode-property-aliases-ecmascript@2.2.0: {} - unicorn-magic@0.1.0: {} - - universalify@0.1.2: {} + unicorn-magic@0.3.0: {} universalify@0.2.0: {} unpipe@1.0.0: {} - update-browserslist-db@1.2.3(browserslist@4.28.2): + unrs-resolver@1.12.2: dependencies: - browserslist: 4.28.2 + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.12.2 + '@unrs/resolver-binding-android-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-x64': 1.12.2 + '@unrs/resolver-binding-freebsd-x64': 1.12.2 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-arm64-musl': 1.12.2 + '@unrs/resolver-binding-linux-loong64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-loong64-musl': 1.12.2 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-musl': 1.12.2 + '@unrs/resolver-binding-linux-s390x-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-musl': 1.12.2 + '@unrs/resolver-binding-openharmony-arm64': 1.12.2 + '@unrs/resolver-binding-wasm32-wasi': 1.12.2 + '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2 + '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 + '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 + + update-browserslist-db@1.2.3(browserslist@4.28.6): + dependencies: + browserslist: 4.28.6 escalade: 3.2.0 picocolors: 1.1.1 @@ -13324,13 +13060,13 @@ snapshots: dependencies: iconv-lite: 0.6.3 - use-latest-callback@0.2.6(react@19.2.4): + use-latest-callback@0.2.6(react@19.2.3): dependencies: - react: 19.2.4 + react: 19.2.3 - use-sync-external-store@1.6.0(react@19.2.4): + use-sync-external-store@1.6.0(react@19.2.3): dependencies: - react: 19.2.4 + react: 19.2.3 util-deprecate@1.0.2: {} @@ -13338,8 +13074,6 @@ snapshots: uuid@7.0.3: {} - uuid@8.3.2: {} - v8-to-istanbul@9.3.0: dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -13378,7 +13112,7 @@ snapshots: whatwg-mimetype@3.0.0: {} - whatwg-url-minimum@0.1.1: {} + whatwg-url-minimum@0.1.2: {} whatwg-url-without-unicode@8.0.0-3: dependencies: @@ -13402,7 +13136,7 @@ snapshots: which-builtin-type@1.2.1: dependencies: call-bound: 1.0.4 - function.prototype.name: 1.1.8 + function.prototype.name: 1.2.0 has-tostringtag: 1.0.2 is-async-function: 2.1.1 is-date-object: 1.1.0 @@ -13413,7 +13147,7 @@ snapshots: isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.20 + which-typed-array: 1.1.22 which-collection@1.0.2: dependencies: @@ -13422,12 +13156,10 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.4 - which-module@2.0.1: {} - - which-typed-array@1.1.20: + which-typed-array@1.1.22: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 for-each: 0.3.5 get-proto: 1.0.1 @@ -13452,6 +13184,12 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + wrappy@1.0.2: {} write-file-atomic@4.0.2: @@ -13459,17 +13197,9 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 - ws@6.2.3: - dependencies: - async-limiter: 1.0.1 - - ws@7.5.10: {} + ws@7.5.13: {} - ws@8.20.0: {} - - wsl-utils@0.1.0: - dependencies: - is-wsl: 3.1.1 + ws@8.21.1: {} x-path@0.0.2: dependencies: @@ -13493,38 +13223,19 @@ snapshots: xmlchars@2.2.0: {} - y18n@4.0.3: {} - y18n@5.0.8: {} yallist@3.1.1: {} yaml@1.10.3: {} - yaml@2.8.3: {} - - yargs-parser@18.1.3: - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 + yaml@2.9.0: {} yargs-parser@21.1.1: {} - yargs@15.4.1: - dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.1 - y18n: 4.0.3 - yargs-parser: 18.1.3 + yargs-parser@22.0.0: {} - yargs@17.7.2: + yargs@17.7.3: dependencies: cliui: 8.0.1 escalade: 3.2.0 @@ -13534,14 +13245,29 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yargs@18.0.0: + dependencies: + cliui: 9.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + string-width: 7.2.0 + y18n: 5.0.8 + yargs-parser: 22.0.0 + yocto-queue@0.1.0: {} yocto-queue@1.2.2: {} - zod-validation-error@4.0.2(zod@4.3.6): + zod-validation-error@4.0.2(zod@4.4.3): dependencies: - zod: 4.3.6 + zod: 4.4.3 zod@3.25.76: {} - zod@4.3.6: {} + zod@4.4.3: {} + + zustand@5.0.14(@types/react@19.2.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)): + optionalDependencies: + '@types/react': 19.2.17 + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0900ce43a..5272b6ed1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,34 @@ +allowBuilds: + better-sqlite3: true + esbuild: true + msgpackr-extract: true + protobufjs: true + unrs-resolver: true + +minimumReleaseAgeExclude: + - '@expo/cli@56.1.14' + - '@expo/fingerprint@0.19.4' + - '@expo/inline-modules@0.0.11' + - '@expo/prebuild-config@56.0.15' + - '@expo/router-server@56.0.13' + - expo-asset@56.0.16 + - expo-constants@56.0.17 + - expo-modules-autolinking@56.0.15 + - expo-modules-core@56.0.15 + - expo-modules-jsi@56.0.8 + - expo-server@56.0.5 + - expo@56.0.9 + - expo-clipboard@56.0.4 + +nodeLinker: hoisted + +packages: + - '.' + onlyBuiltDependencies: - better-sqlite3 - esbuild - protobufjs + +patchedDependencies: + '@legendapp/list@3.3.3': patches/@legendapp__list@3.3.3.patch diff --git a/scripts/generate-env-file.cjs b/scripts/generate-env-file.cjs index ff7ee15b2..3ac78e918 100644 --- a/scripts/generate-env-file.cjs +++ b/scripts/generate-env-file.cjs @@ -1,60 +1,257 @@ const fs = require('fs'); -const os = require('os'); const path = require('path'); const { execSync } = require('child_process'); -const formattedDate = new Date().getTime(); -const commitHash = execSync('git rev-parse --short HEAD').toString().trim(); -const buildType = process.argv[2] || 'Beta'; +function parseArgs(argv) { + const out = {}; -const newEnvVars = [ - `BUILD_TYPE=${buildType}`, - `GIT_HASH=${commitHash}`, - `RELEASE_DATE=${formattedDate}`, - `NODE_ENV=${buildType === 'Release' ? 'production' : 'development'}`, -].join(os.EOL); + for (let i = 2; i < argv.length; i += 1) { + const token = argv[i]; -const envFilePath = path.join(__dirname, '..', '.env'); -let existingEnvData = ''; + if (!token.startsWith('--')) { + continue; + } -try { - if (fs.existsSync(envFilePath)) { - const existingContent = fs.readFileSync(envFilePath, 'utf8'); - - existingEnvData = existingContent - .split(os.EOL) - .filter(line => { - const trimmedLine = line.trim(); - return ( - trimmedLine && - !trimmedLine.startsWith('BUILD_TYPE=') && - !trimmedLine.startsWith('GIT_HASH=') && - !trimmedLine.startsWith('RELEASE_DATE=') && - !trimmedLine.startsWith('NODE_ENV=') - ); - }) - .join(os.EOL); + const eqIndex = token.indexOf('='); + + if (eqIndex !== -1) { + out[token.slice(2, eqIndex)] = token.slice(eqIndex + 1); + continue; + } + + const key = token.slice(2); + const next = argv[i + 1]; + + if (next && !next.startsWith('--')) { + out[key] = next; + i += 1; + } else { + out[key] = true; + } } -} catch (err) { - console.warn('Warning: Could not read existing .env file:', err.message); + + return out; +} + +function formatUtcDate(date) { + const pad = n => String(n).padStart(2, '0'); + + const day = pad(date.getUTCDate()); + const month = pad(date.getUTCMonth() + 1); + const year = String(date.getUTCFullYear()).slice(-2); + + let hours = date.getUTCHours(); + const minutes = pad(date.getUTCMinutes()); + const ampm = hours >= 12 ? 'PM' : 'AM'; + + hours %= 12; + if (hours === 0) { + hours = 12; + } + + return `${day}/${month}/${year} ${pad(hours)}:${minutes} ${ampm} UTC`; +} + +function getGitHash() { + return execSync('git rev-parse --short HEAD').toString().trim(); } -const finalContent = existingEnvData - ? `${newEnvVars}${os.EOL}${existingEnvData}${os.EOL}` - : `${newEnvVars}${os.EOL}`; +function readEnvValue(filePath, key) { + if (!fs.existsSync(filePath)) { + return undefined; + } + + const line = fs + .readFileSync(filePath, 'utf8') + .split(/\r?\n/) + .find(entry => entry.startsWith(`${key}=`)); + + if (!line) { + return undefined; + } + + const value = line.slice(key.length + 1).trim(); + + if (!value || value === 'undefined') { + return undefined; + } + + try { + const parsedValue = JSON.parse(value); + return typeof parsedValue === 'string' ? parsedValue : value; + } catch { + return value; + } +} + +function resolveOptionalValue({ argument, existingValue, environmentValue }) { + const value = argument ?? existingValue ?? environmentValue; + + if (value === undefined || value === null || value === '') { + return undefined; + } + + if (value === true) { + throw new Error('Expected a value after the client ID argument'); + } + + return String(value); +} + +function formatEnvEntry(key, value) { + return value === undefined ? undefined : `${key}=${JSON.stringify(value)}`; +} + +function formatTypeScriptValue(value) { + return JSON.stringify(value) ?? 'undefined'; +} + +function writeFileAtomically(filePath, content) { + const temporaryPath = `${filePath}.${process.pid}.tmp`; + + try { + fs.writeFileSync(temporaryPath, content, 'utf8'); + fs.renameSync(temporaryPath, filePath); + } finally { + if (fs.existsSync(temporaryPath)) { + fs.rmSync(temporaryPath); + } + } +} + +function getHiddenEnvFiles(rootDir) { + return fs + .readdirSync(rootDir, { withFileTypes: true }) + .filter(entry => { + return ( + entry.isFile() && + /^\..*\.env$/.test(entry.name) && + entry.name !== '.env' + ); + }) + .map(entry => path.join(rootDir, entry.name)) + .sort(); +} + +function readHiddenEnvFiles(rootDir) { + const files = getHiddenEnvFiles(rootDir); + + const content = files + .map(filePath => { + const fileName = path.basename(filePath); + const fileContent = fs.readFileSync(filePath, 'utf8').trimEnd(); + + return `# Imported from ${fileName}\n${fileContent}`; + }) + .join('\n\n'); + + return { files, content }; +} + +const args = parseArgs(process.argv); +const projectRoot = path.join(__dirname, '..'); +const envFilePath = path.join(projectRoot, '.env'); + +const buildType = args['build-type'] || 'Beta'; +const myanimelistClientId = resolveOptionalValue({ + argument: args['myanimelist-client-id'], + existingValue: readEnvValue(envFilePath, 'MYANIMELIST_CLIENT_ID'), + environmentValue: process.env.MYANIMELIST_CLIENT_ID, +}); +const anilistClientId = resolveOptionalValue({ + argument: args['anilist-client-id'], + existingValue: readEnvValue(envFilePath, 'ANILIST_CLIENT_ID'), + environmentValue: process.env.ANILIST_CLIENT_ID, +}); + +const gitHash = args['git-hash'] || getGitHash(); +const releaseDate = args['release-date'] || formatUtcDate(new Date()); +const nodeEnv = + args['node-env'] || + (buildType.toLowerCase().includes('release') ? 'production' : 'development'); +const rozeniteEnabled = nodeEnv !== 'production'; + +const generatedEnvContent = [ + formatEnvEntry('BUILD_TYPE', buildType), + formatEnvEntry('GIT_HASH', gitHash), + formatEnvEntry('RELEASE_DATE', releaseDate), + formatEnvEntry('NODE_ENV', nodeEnv), + formatEnvEntry('WITH_ROZENITE', rozeniteEnabled), + formatEnvEntry('MYANIMELIST_CLIENT_ID', myanimelistClientId), + formatEnvEntry('ANILIST_CLIENT_ID', anilistClientId), + '', +] + .filter(value => value !== undefined) + .join('\n'); + +const { files: hiddenEnvFiles, content: hiddenEnvContent } = + readHiddenEnvFiles(projectRoot); + +const envContent = hiddenEnvContent + ? `${generatedEnvContent}\n# Imported hidden env files\n${hiddenEnvContent}\n` + : generatedEnvContent; + +const buildInfoPath = path.join( + projectRoot, + 'src', + 'generated', + 'build-info.ts', +); + +const buildInfoContent = `// This file is generated. Do not edit manually. +export const BUILD_TYPE = ${JSON.stringify(buildType)}; +export const GIT_HASH = ${JSON.stringify(gitHash)}; +export const RELEASE_DATE = ${JSON.stringify(releaseDate)}; +export const NODE_ENV = ${JSON.stringify(nodeEnv)}; +export const MYANIMELIST_CLIENT_ID: string | undefined = ${formatTypeScriptValue( + myanimelistClientId, +)}; +export const ANILIST_CLIENT_ID: string | undefined = ${formatTypeScriptValue( + anilistClientId, +)}; + +export default { + BUILD_TYPE, + GIT_HASH, + RELEASE_DATE, + NODE_ENV, + MYANIMELIST_CLIENT_ID, + ANILIST_CLIENT_ID, +}; +`; try { - fs.writeFileSync(envFilePath, finalContent, 'utf8'); + fs.mkdirSync(path.dirname(buildInfoPath), { recursive: true }); + writeFileAtomically(buildInfoPath, buildInfoContent); + writeFileAtomically(envFilePath, envContent); + + console.log(`Generated build environment for ${buildType} build`); + console.log( + `Wrote ${path.relative(projectRoot, envFilePath)} and ${path.relative( + projectRoot, + buildInfoPath, + )}`, + ); + + if (hiddenEnvFiles.length > 0) { + console.log( + `Imported ${hiddenEnvFiles.length} hidden environment file(s):`, + hiddenEnvFiles.map(filePath => path.basename(filePath)), + ); + } - console.log(`Generated .env file for ${buildType} build\n`); console.table({ BUILD_TYPE: buildType, - GIT_HASH: commitHash, - RELEASE_DATE: formattedDate, - NODE_ENV: buildType === 'Release' ? 'production' : 'development', + GIT_HASH: gitHash, + RELEASE_DATE: releaseDate, + NODE_ENV: nodeEnv, + WITH_ROZENITE: rozeniteEnabled ? 'enabled' : 'disabled', + MYANIMELIST_CLIENT_ID: myanimelistClientId + ? 'configured' + : 'not configured', + ANILIST_CLIENT_ID: anilistClientId ? 'configured' : 'not configured', }); - console.log('\n'); } catch (err) { - console.error('Error: Could not write .env file:', err.message); + console.error('Error: Could not generate build environment:', err.message); process.exit(1); } diff --git a/scripts/generate-string-types.cjs b/scripts/generate-string-types.cjs index ea0a18901..2bafa15e4 100644 --- a/scripts/generate-string-types.cjs +++ b/scripts/generate-string-types.cjs @@ -18,7 +18,7 @@ const flatten = (obj, target, prefix) => { }; const strings = fs.readFileSync( - path.resolve(process.cwd(), 'strings/languages/en/strings.json'), + path.resolve(process.cwd(), 'src/i18n/languages/en/strings.json'), 'utf8', ); @@ -34,7 +34,7 @@ const formatContent = prettier.format(stringTypes, { }); fs.writeFile( - path.resolve(process.cwd(), 'strings/types/index.ts'), + path.resolve(process.cwd(), 'src/i18n/types/index.ts'), formatContent, err => { if (err) { diff --git a/scripts/package-release-artifacts.cjs b/scripts/package-release-artifacts.cjs new file mode 100644 index 000000000..638eb8c6e --- /dev/null +++ b/scripts/package-release-artifacts.cjs @@ -0,0 +1,103 @@ +const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); + +const VERSION_PATTERN = /^\d+\.\d+\.\d+$/; +const EXPECTED_OUTPUTS = new Set([ + 'arm64-v8a', + 'armeabi-v7a', + 'x86', + 'x86_64', + 'universal', +]); + +const projectRoot = path.join(__dirname, '..'); +const apkOutputDir = path.join( + projectRoot, + 'android', + 'app', + 'build', + 'outputs', + 'apk', + 'release', +); +const artifactDir = path.join(projectRoot, 'release-artifacts'); +const metadataPath = path.join(apkOutputDir, 'output-metadata.json'); +const version = process.argv[2]; + +if (!VERSION_PATTERN.test(version || '')) { + console.error('Usage: node scripts/package-release-artifacts.cjs '); + process.exit(1); +} + +const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8')); +const packagedOutputs = new Set(); +const checksums = []; + +const mismatchedVersion = metadata.elements?.find( + element => element.versionName !== version, +); + +if (mismatchedVersion) { + console.error( + `APK version ${mismatchedVersion.versionName} does not match requested release ${version}`, + ); + process.exit(1); +} + +fs.rmSync(artifactDir, { recursive: true, force: true }); +fs.mkdirSync(artifactDir, { recursive: true }); + +for (const element of metadata.elements || []) { + const abiFilter = element.filters?.find( + filter => filter.filterType === 'ABI', + ); + const outputName = abiFilter?.value || 'universal'; + + if (!EXPECTED_OUTPUTS.has(outputName)) { + continue; + } + + const sourcePath = path.join(apkOutputDir, element.outputFile); + const artifactName = `LNReader-v${version}-${outputName}.apk`; + const artifactPath = path.join(artifactDir, artifactName); + + fs.copyFileSync(sourcePath, artifactPath); + + const hash = crypto + .createHash('sha256') + .update(fs.readFileSync(artifactPath)) + .digest('hex'); + + checksums.push(`${hash} ${artifactName}`); + packagedOutputs.add(outputName); +} + +const missingOutputs = [...EXPECTED_OUTPUTS].filter( + outputName => !packagedOutputs.has(outputName), +); + +if (missingOutputs.length > 0) { + console.error(`Missing release APK outputs: ${missingOutputs.join(', ')}`); + process.exit(1); +} + +checksums.sort(); +fs.writeFileSync( + path.join(artifactDir, 'SHA256SUMS.txt'), + `${checksums.join('\n')}\n`, + 'utf8', +); + +console.table( + [...packagedOutputs].sort().map(outputName => { + const artifactName = `LNReader-v${version}-${outputName}.apk`; + const stats = fs.statSync(path.join(artifactDir, artifactName)); + + return { + output: outputName, + artifact: artifactName, + sizeMiB: (stats.size / 1024 / 1024).toFixed(2), + }; + }), +); diff --git a/scripts/prepare-release.cjs b/scripts/prepare-release.cjs new file mode 100644 index 000000000..83ea83aea --- /dev/null +++ b/scripts/prepare-release.cjs @@ -0,0 +1,146 @@ +const fs = require('fs'); +const path = require('path'); + +const VERSION_PATTERN = /^(\d+)\.(\d+)\.(\d+)$/; +const MAX_MAJOR = 1023; +const MAX_MINOR = 1023; +const MAX_PATCH = 2047; +const MAX_ANDROID_VERSION_CODE = 2100000000; + +const projectRoot = path.join(__dirname, '..'); +const packageJsonPath = path.join(projectRoot, 'package.json'); +const appJsonPath = path.join(projectRoot, 'app.json'); +const issueTemplatePaths = [ + path.join(projectRoot, '.github', 'ISSUE_TEMPLATE', 'report_issue.yml'), + path.join(projectRoot, '.github', 'ISSUE_TEMPLATE', 'request_feature.yml'), +]; + +const nextVersion = process.argv[2]; +const match = nextVersion?.match(VERSION_PATTERN); + +if (!match) { + console.error('Usage: pnpm release:prepare '); + process.exit(1); +} + +const [, majorText, minorText, patchText] = match; +const versionParts = [majorText, minorText, patchText].map(Number); +const [major, minor, patch] = versionParts; + +if (major > MAX_MAJOR || minor > MAX_MINOR || patch > MAX_PATCH) { + console.error( + `Version components must fit within ${MAX_MAJOR}.${MAX_MINOR}.${MAX_PATCH}`, + ); + process.exit(1); +} + +const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8'); +const appJsonContent = fs.readFileSync(appJsonPath, 'utf8'); +const packageJson = JSON.parse(packageJsonContent); +const appJson = JSON.parse(appJsonContent); +const currentVersion = packageJson.version; +const currentMatch = currentVersion?.match(VERSION_PATTERN); + +if (!currentMatch) { + console.error( + `Current package version is not stable semver: ${currentVersion}`, + ); + process.exit(1); +} + +const currentParts = currentMatch.slice(1).map(Number); +const versionComparison = versionParts.findIndex( + (part, index) => part !== currentParts[index], +); + +if ( + versionComparison !== -1 && + versionParts[versionComparison] < currentParts[versionComparison] +) { + console.error( + `Release version ${nextVersion} must not be older than ${currentVersion}`, + ); + process.exit(1); +} + +if (appJson.expo.version !== currentVersion) { + console.error( + `Version mismatch: package.json=${currentVersion}, app.json=${appJson.expo.version}`, + ); + process.exit(1); +} + +const versionCode = major * 1024 * 2048 + minor * 2048 + patch; + +if (versionCode > MAX_ANDROID_VERSION_CODE) { + console.error( + `Android version code ${versionCode} exceeds ${MAX_ANDROID_VERSION_CODE}`, + ); + process.exit(1); +} + +const replaceSingle = (content, search, replacement, filePath) => { + const firstMatch = content.indexOf(search); + const secondMatch = content.indexOf(search, firstMatch + search.length); + + if (firstMatch === -1 || secondMatch !== -1) { + console.error(`Expected exactly one "${search}" entry in ${filePath}`); + process.exit(1); + } + + return ( + content.slice(0, firstMatch) + + replacement + + content.slice(firstMatch + search.length) + ); +}; + +const nextPackageJsonContent = replaceSingle( + packageJsonContent, + `"version": "${currentVersion}"`, + `"version": "${nextVersion}"`, + packageJsonPath, +); +let nextAppJsonContent = replaceSingle( + appJsonContent, + `"version": "${currentVersion}"`, + `"version": "${nextVersion}"`, + appJsonPath, +); +nextAppJsonContent = replaceSingle( + nextAppJsonContent, + `"versionCode": ${appJson.expo.android.versionCode}`, + `"versionCode": ${versionCode}`, + appJsonPath, +); + +const issueTemplateUpdates = issueTemplatePaths.map(issueTemplatePath => { + const content = fs.readFileSync(issueTemplatePath, 'utf8'); + + if (currentVersion !== nextVersion && !content.includes(currentVersion)) { + console.error( + `${path.relative( + projectRoot, + issueTemplatePath, + )} does not contain ${currentVersion}`, + ); + process.exit(1); + } + + return { + issueTemplatePath, + content: content.replaceAll(currentVersion, nextVersion), + }; +}); + +fs.writeFileSync(packageJsonPath, nextPackageJsonContent, 'utf8'); +fs.writeFileSync(appJsonPath, nextAppJsonContent, 'utf8'); + +for (const { issueTemplatePath, content } of issueTemplateUpdates) { + fs.writeFileSync(issueTemplatePath, content, 'utf8'); +} + +console.table({ + version: nextVersion, + androidVersionCode: versionCode, +}); diff --git a/scripts/resolve-release-version.cjs b/scripts/resolve-release-version.cjs new file mode 100644 index 000000000..93360244a --- /dev/null +++ b/scripts/resolve-release-version.cjs @@ -0,0 +1,40 @@ +const fs = require('fs'); +const path = require('path'); + +const VERSION_PATTERN = /^(\d+)\.(\d+)\.(\d+)$/; +const BUMP_TYPES = new Set(['major', 'minor', 'patch']); + +const bumpType = process.argv[2]?.toLowerCase(); + +if (!BUMP_TYPES.has(bumpType)) { + console.error('Usage: node scripts/resolve-release-version.cjs '); + console.error('Bump type must be one of: major, minor, patch'); + process.exit(1); +} + +const packageJsonPath = path.join(__dirname, '..', 'package.json'); +const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); +const currentVersion = packageJson.version; +const match = currentVersion?.match(VERSION_PATTERN); + +if (!match) { + console.error( + `Current package version is not stable semver: ${currentVersion}`, + ); + process.exit(1); +} + +let [, major, minor, patch] = match.map(Number); + +if (bumpType === 'major') { + major += 1; + minor = 0; + patch = 0; +} else if (bumpType === 'minor') { + minor += 1; + patch = 0; +} else { + patch += 1; +} + +process.stdout.write(`${major}.${minor}.${patch}`); diff --git a/shared/Epub.hpp b/shared/Epub.hpp deleted file mode 100644 index aad18b219..000000000 --- a/shared/Epub.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include - -#ifndef Epub_H -#define Epub_H - -struct Chapter -{ - std::string name; - std::string path; -}; - -struct EpubMetadata -{ - std::string name; - std::string path; - std::string cover; - std::string summary; - std::string author; - std::string artist; - std::vector chapters; - std::vector cssPaths; - std::vector imagePaths; -}; - -EpubMetadata parseEpub(const std::string epub_path); - -#endif diff --git a/shared/NativeEpub.cpp b/shared/NativeEpub.cpp deleted file mode 100644 index 4fcbe0f99..000000000 --- a/shared/NativeEpub.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include - -namespace facebook::react -{ - - NativeEpub::NativeEpub(std::shared_ptr jsInvoker) - : NativeEpubCxxSpec(std::move(jsInvoker)) {} - - jsi::Object NativeEpub::parseNovelAndChapters(jsi::Runtime &rt, jsi::String epubDirPath) - { - jsi::Object novel(rt); - EpubMetadata metadata = parseEpub(epubDirPath.utf8(rt).c_str()); - novel.setProperty(rt, "name", metadata.name); - novel.setProperty(rt, "author", metadata.author); - novel.setProperty(rt, "artist", metadata.artist); - novel.setProperty(rt, "summary", metadata.summary); - novel.setProperty(rt, "cover", metadata.cover); - - jsi::Array chapters(rt, metadata.chapters.size()); - for (int i = 0; i < metadata.chapters.size(); i++) - { - jsi::Object chapter(rt); - chapter.setProperty(rt, "name", metadata.chapters[i].name); - chapter.setProperty(rt, "path", metadata.chapters[i].path); - chapters.setValueAtIndex(rt, i, chapter); - } - novel.setProperty(rt, "chapters", chapters); - jsi::Array cssPaths(rt, metadata.cssPaths.size()); - for (int i = 0; i < metadata.cssPaths.size(); i++) - { - cssPaths.setValueAtIndex(rt, i, metadata.cssPaths[i]); - } - novel.setProperty(rt, "cssPaths", cssPaths); - - jsi::Array imagePaths(rt, metadata.imagePaths.size()); - for (int i = 0; i < metadata.imagePaths.size(); i++) - { - imagePaths.setValueAtIndex(rt, i, metadata.imagePaths[i]); - } - novel.setProperty(rt, "imagePaths", imagePaths); - - return novel; - } - -} // namespace facebook::react \ No newline at end of file diff --git a/shared/NativeEpub.hpp b/shared/NativeEpub.hpp deleted file mode 100644 index 0004f5669..000000000 --- a/shared/NativeEpub.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace facebook::react -{ - - class NativeEpub : public NativeEpubCxxSpec - { - public: - NativeEpub(std::shared_ptr jsInvoker); - - jsi::Object parseNovelAndChapters(jsi::Runtime &rt, jsi::String epubDirPath); - }; - -} // namespace facebook::react diff --git a/skills-lock.json b/skills-lock.json new file mode 100644 index 000000000..305d3ded2 --- /dev/null +++ b/skills-lock.json @@ -0,0 +1,11 @@ +{ + "version": 1, + "skills": { + "build-nitro-modules": { + "source": "margelo/react-native-skills", + "sourceType": "github", + "skillPath": "skills/build-nitro-modules/SKILL.md", + "computedHash": "9fce6b96add81fc0baf4594edd0c70c811c25ab2109d25b16f148c46c6d754b4" + } + } +} diff --git a/specs/NativeEpub.ts b/specs/NativeEpub.ts deleted file mode 100644 index fcab3d4c7..000000000 --- a/specs/NativeEpub.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { TurboModule, TurboModuleRegistry } from 'react-native'; - -interface EpubChapter { - name: string; - path: string; -} -interface EpubNovel { - name: string; - cover: string | null; - summary: string | null; - author: string | null; - artist: string | null; - chapters: EpubChapter[]; - cssPaths: string[]; - imagePaths: string[]; -} - -export interface Spec extends TurboModule { - parseNovelAndChapters: (epubDirPath: string) => EpubNovel; -} - -export default TurboModuleRegistry.getEnforcing('NativeEpub'); diff --git a/specs/NativeFile.ts b/specs/NativeFile.ts deleted file mode 100644 index 66fc6fbfd..000000000 --- a/specs/NativeFile.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { TurboModule, TurboModuleRegistry } from 'react-native'; - -interface ReadDirResult { - name: string; - path: string; - isDirectory: boolean; // int -} - -export interface Spec extends TurboModule { - writeFile: (path: string, content: string) => void; - readFile: (path: string) => string; - copyFile: (sourcePath: string, destPath: string) => void; - moveFile: (sourcePath: string, destPath: string) => void; - exists: (filePath: string) => boolean; - /** - * @description create parents, and do nothing if exists; - */ - mkdir: (filePath: string) => void; - /** - * @description remove recursively - */ - unlink: (filePath: string) => void; - readDir: (dirPath: string) => ReadDirResult[]; - downloadFile: ( - url: string, - destPath: string, - method: string, - headers: { [key: string]: string } | Headers, - body?: string, - ) => Promise; - getConstants: () => { - ExternalDirectoryPath: string; - ExternalCachesDirectoryPath: string; - }; -} - -export default TurboModuleRegistry.getEnforcing('NativeFile'); diff --git a/specs/NativeTTSMediaControl.ts b/specs/NativeTTSMediaControl.ts deleted file mode 100644 index 8a6622b60..000000000 --- a/specs/NativeTTSMediaControl.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { TurboModule, TurboModuleRegistry } from 'react-native'; - -export interface Spec extends TurboModule { - showMediaNotification( - title: string, - subtitle: string, - coverUri: string, - isPlaying: boolean, - ): void; - updatePlaybackState(isPlaying: boolean): void; - updateProgress(current: number, total: number): void; - dismiss(): void; - addListener: (eventName: string) => void; - removeListeners: (count: number) => void; -} - -export default TurboModuleRegistry.getEnforcing('NativeTTSMediaControl'); diff --git a/specs/NativeVolumeButtonListener.ts b/specs/NativeVolumeButtonListener.ts deleted file mode 100644 index d958c3fd9..000000000 --- a/specs/NativeVolumeButtonListener.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { TurboModule, TurboModuleRegistry } from 'react-native'; - -export interface Spec extends TurboModule { - addListener: (eventName: string) => void; - removeListeners: (count: number) => void; -} - -export default TurboModuleRegistry.getEnforcing( - 'NativeVolumeButtonListener', -); diff --git a/specs/NativeZipArchive.ts b/specs/NativeZipArchive.ts deleted file mode 100644 index a6d0f54aa..000000000 --- a/specs/NativeZipArchive.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TurboModule, TurboModuleRegistry } from 'react-native'; - -export interface Spec extends TurboModule { - zip: (sourceDirPath: string, destFilePath: string) => Promise; - unzip: (sourceFilePath: string, distDirPath: string) => Promise; - remoteUnzip: ( - distDirPath: string, - url: string, - headers: { [key: string]: string }, - ) => Promise; - remoteZip: ( - sourceDirPath: string, - url: string, - headers: { [key: string]: string }, - ) => Promise; // return response as text -} - -export default TurboModuleRegistry.getEnforcing('NativeZipArchive'); diff --git a/src/api/drive/request.ts b/src/api/drive/request.ts index 5e0b60db6..cd211339d 100644 --- a/src/api/drive/request.ts +++ b/src/api/drive/request.ts @@ -6,7 +6,7 @@ import { DriveRequestParams, } from './types'; import { PATH_SEPARATOR } from '@api/constants'; -import NativeZipArchive from '@specs/NativeZipArchive'; +import NativeZipArchive from '@modules/native-zip-archive' const BASE_URL = 'https://www.googleapis.com/drive/v3/files'; const MEDIA_UPLOAD_URL = 'https://www.googleapis.com/upload/drive/v3/files'; diff --git a/src/api/remote/index.ts b/src/api/remote/index.ts index 79bee066e..860601162 100644 --- a/src/api/remote/index.ts +++ b/src/api/remote/index.ts @@ -1,5 +1,5 @@ import { PATH_SEPARATOR } from '@api/constants'; -import NativeZipArchive from '@specs/NativeZipArchive'; +import NativeZipArchive from '@modules/native-zip-archive' import { fetchTimeout } from '@utils/fetch/fetch'; const commonHeaders = { diff --git a/src/components/AppErrorBoundary/AppErrorBoundary.tsx b/src/components/AppErrorBoundary/AppErrorBoundary.tsx index 95e3f4072..9085ef56b 100644 --- a/src/components/AppErrorBoundary/AppErrorBoundary.tsx +++ b/src/components/AppErrorBoundary/AppErrorBoundary.tsx @@ -1,7 +1,9 @@ import React from 'react'; import { StyleSheet, View, Text, StatusBar } from 'react-native'; import ErrorBoundary from 'react-native-error-boundary'; - +import * as Clipboard from 'expo-clipboard'; +import { getString } from '@i18n/translations'; +import { showToast } from '@utils/showToast'; import { Button, List } from '@components'; import { useTheme } from '@hooks/persisted'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -17,6 +19,33 @@ export const ErrorFallback: React.FC = ({ }) => { const theme = useTheme(); + const fallbackGetString = ( + key: Parameters[0], + fallback: string, + options?: Parameters[1], + ) => { + try { + return getString(key, options); + } catch { + return fallback; + } + }; + + const handleCopyStackTrace = async () => { + try { + await Clipboard.setStringAsync(`${error.message}\n\n${error.stack}`); + showToast( + fallbackGetString( + 'common.copiedToClipboard', + 'Copied to clipboard: Stack trace', + { name: 'Stack trace' }, + ), + ); + } catch { + // clipboard failure is non-critical + } + }; + return ( = ({ - An Unexpected Error Ocurred + {fallbackGetString( + 'errorBoundary.title', + 'An Unexpected Error Occurred', + )} - The application ran into an unexpected error. We suggest you - screenshot this message and then share it in our support channel on - Discord. + {fallbackGetString( + 'errorBoundary.description', + 'The application ran into an unexpected error. Please copy the stack trace below and share it on our Discord support channel.', + )} = ({ + - - - - - ); -}; +}) => ( + +); export default ClearHistoryDialog; - -const styles = StyleSheet.create({ - button: { - marginLeft: 4, - }, - container: { - borderRadius: 28, - margin: 20, - }, - title: { - fontSize: 16, - letterSpacing: 0, - }, -}); diff --git a/src/screens/history/components/HistoryCard/HistoryCard.tsx b/src/screens/history/components/HistoryCard/HistoryCard.tsx index e46de3134..6419465c4 100644 --- a/src/screens/history/components/HistoryCard/HistoryCard.tsx +++ b/src/screens/history/components/HistoryCard/HistoryCard.tsx @@ -1,66 +1,66 @@ import React from 'react'; -import { Image, Pressable, StyleSheet, Text, View } from 'react-native'; +import { Pressable, StyleSheet, Text, View } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import dayjs from 'dayjs'; -import { IconButtonV2 } from '@components'; - -import { defaultCover } from '@plugins/helpers/constants'; -import { getString } from '@strings/translations'; +import { IconButtonV2, NovelCoverImage } from '@components'; +import { getString } from '@i18n/translations'; import { useTheme } from '@hooks/persisted'; -import { History, NovelInfo } from '@database/types'; +import { History } from '@database/types'; import { HistoryScreenProps } from '@navigators/types'; -import { coverPlaceholderColor } from '@theme/colors'; - interface HistoryCardProps { history: History; - handleRemoveFromHistory: (chapterId: number) => void; + onRemove: (history: History) => void; } -const HistoryCard: React.FC = ({ - history, - handleRemoveFromHistory, -}) => { +const HistoryCard: React.FC = ({ history, onRemove }) => { const theme = useTheme(); const { navigate } = useNavigation(); return ( - - navigate('ReaderStack', { - screen: 'Chapter', - params: { - novel: { - path: history.novelPath, - name: history.novelName, - pluginId: history.pluginId, - } as NovelInfo, - chapter: history, - }, - }) - } - > - + + + navigate('ReaderStack', { + screen: 'Chapter', + params: { + novel: { + id: history.novelId, + path: history.novelPath, + name: history.novelName, + pluginId: history.pluginId, + cover: history.novelCover, + inLibrary: history.inLibrary, + }, + chapter: history, + }, + }) + } + > + onPress={event => { + event.stopPropagation(); navigate('ReaderStack', { screen: 'Novel', params: { - name: history.name, + name: history.novelName, path: history.novelPath, cover: history.novelCover, pluginId: history.pluginId, + inLibrary: history.inLibrary, }, - }) - } + }); + }} > - @@ -72,23 +72,27 @@ const HistoryCard: React.FC = ({ {history.novelName} - {`${getString('historyScreen.chapter')} ${history.chapterNumber - } • ${dayjs(history.readTime).format('LT').toUpperCase()}` + - `${history.progress && history.progress > 0 - ? ' • ' + history.progress + '%' - : '' + {`${getString('historyScreen.chapter')} ${ + history.chapterNumber + } • ${dayjs(history.readTime).format('LT').toUpperCase()}` + + `${ + history.progress && history.progress > 0 + ? ' • ' + history.progress + '%' + : '' }`} - + + handleRemoveFromHistory(history.id)} + onPress={() => onRemove(history)} /> - + ); }; @@ -96,11 +100,16 @@ export default HistoryCard; const styles = StyleSheet.create({ buttonContainer: { - alignItems: 'center', - flexDirection: 'row', - justifyContent: 'space-around', + bottom: 8, + justifyContent: 'center', + position: 'absolute', + right: 16, + top: 8, }, - container: { + buttonSpacer: { + width: 40, + }, + row: { alignItems: 'center', flexDirection: 'row', justifyContent: 'space-between', @@ -108,7 +117,6 @@ const styles = StyleSheet.create({ paddingVertical: 8, }, cover: { - backgroundColor: coverPlaceholderColor, borderRadius: 4, height: 80, width: 56, @@ -117,11 +125,7 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: 'center', marginStart: 16, - }, - imageAndNameContainer: { - alignItems: 'center', - flex: 1, - flexDirection: 'row', + minHeight: 80, }, novelName: { marginBottom: 4, diff --git a/src/screens/history/components/HistorySkeletonLoading.tsx b/src/screens/history/components/HistorySkeletonLoading.tsx index 6ed8779bf..cb5005e72 100644 --- a/src/screens/history/components/HistorySkeletonLoading.tsx +++ b/src/screens/history/components/HistorySkeletonLoading.tsx @@ -1,33 +1,43 @@ import React, { memo } from 'react'; -import { StyleSheet, View } from 'react-native'; -import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder'; -import { LinearGradient } from 'expo-linear-gradient'; +import { StyleSheet, useWindowDimensions, View } from 'react-native'; import { ThemeColors } from '@theme/types'; import useLoadingColors from '@utils/useLoadingColors'; -import { useAppSettings } from '@hooks/persisted/index'; +import ShimmerPlaceholder from '@components/Skeleton/ShimmerPlaceholder'; + +const SKELETON_ITEMS = [ + { dateWidth: 72 }, + { dateWidth: null }, + { dateWidth: null }, + { dateWidth: 88 }, + { dateWidth: null }, +] as const; interface Props { theme: ThemeColors; } const HistorySkeletonLoading: React.FC = ({ theme }) => { - const { disableLoadingAnimations } = useAppSettings(); - const ShimmerPlaceHolder = createShimmerPlaceholder(LinearGradient); - const [highlightColor, backgroundColor] = useLoadingColors(theme); + const { width } = useWindowDimensions(); + const textWidth = Math.max(80, width - 144); + const [highlightColor, backgroundColor, disableLoadingAnimations] = + useLoadingColors(theme); - const renderLoadingChapter = (index: number) => ( + const renderLoadingChapter = ( + { dateWidth }: (typeof SKELETON_ITEMS)[number], + index: number, + ) => ( - {index === 0 || Math.random() > 0.6 ? ( - ) : null} - = ({ theme }) => { stopAutoRun={disableLoadingAnimations} /> - - - {Array.from({ length: 2 }).map((_, buttonIndex) => ( - - ))} + ); - const items = Array.from({ length: Math.floor(Math.random() * 3 + 3) }); - - return {items.map((_, index) => renderLoadingChapter(index))}; + return {SKELETON_ITEMS.map(renderLoadingChapter)}; }; const styles = StyleSheet.create({ @@ -106,8 +111,10 @@ const styles = StyleSheet.create({ }, textCtn: { borderRadius: 6, + flex: 1, marginBottom: 2, marginTop: 5, + overflow: 'hidden', }, }); diff --git a/src/screens/history/components/RemoveHistoryDialog.tsx b/src/screens/history/components/RemoveHistoryDialog.tsx new file mode 100644 index 000000000..aae35ab9d --- /dev/null +++ b/src/screens/history/components/RemoveHistoryDialog.tsx @@ -0,0 +1,60 @@ +import React, { useState } from 'react'; + +import { Checkbox, Dialog } from '@components'; +import { useTheme } from '@hooks/persisted'; +import { getString } from '@i18n/translations'; + +interface RemoveHistoryDialogProps { + visible: boolean; + onSubmit: (resetAllChapters: boolean) => void | Promise; + onDismiss: () => void; +} + +const RemoveHistoryDialog: React.FC = ({ + visible, + onSubmit, + onDismiss, +}) => { + const theme = useTheme(); + const [resetAllChapters, setResetAllChapters] = useState(false); + + const handleDismiss = () => { + setResetAllChapters(false); + onDismiss(); + }; + + const handleSubmit = () => { + void onSubmit(resetAllChapters); + handleDismiss(); + }; + + return ( + + + {getString('common.remove')} + + {getString('historyScreen.removeHistoryWarning')} + + + + setResetAllChapters(value => !value)} + theme={theme} + viewStyle={{ paddingHorizontal: 0 }} + /> + + + + {getString('common.cancel')} + + + {getString('common.remove')} + + + + ); +}; + +export default RemoveHistoryDialog; diff --git a/src/screens/library/LibraryScreen.tsx b/src/screens/library/LibraryScreen.tsx index f1204dabd..89e457f94 100644 --- a/src/screens/library/LibraryScreen.tsx +++ b/src/screens/library/LibraryScreen.tsx @@ -13,16 +13,21 @@ import { useWindowDimensions, View, } from 'react-native'; -import { BottomSheetModal } from '@gorhom/bottom-sheet'; +import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types'; import { NavigationState, SceneRendererProps, - TabBar, TabView, } from 'react-native-tab-view'; -import Color from 'color'; -import { SearchbarV2, Button, SafeAreaView } from '@components/index'; +import { + SearchbarV2, + Button, + EmptyView, + ErrorScreenV2, + SafeAreaView, + TopTabBar, +} from '@components/index'; import { LibraryView } from './components/LibraryListView'; import LibraryBottomSheet from './components/LibraryBottomSheet/LibraryBottomSheet'; import { Banner } from './components/Banner'; @@ -30,7 +35,7 @@ import { Actionbar } from '@components/Actionbar/Actionbar'; import { useAppSettings, useHistory, useTheme } from '@hooks/persisted'; import { useSearch, useBackHandler, useBoolean } from '@hooks'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { FAB, Portal } from 'react-native-paper'; import { markAllChaptersRead, @@ -44,12 +49,13 @@ import { Row } from '@components/Common'; import { LibraryScreenProps } from '@navigators/types'; import { NovelInfo } from '@database/types'; import * as DocumentPicker from 'expo-document-picker'; -import ServiceManager from '@services/ServiceManager'; +import { backgroundTasks } from '@services/backgroundTasks'; import useImport from '@hooks/persisted/useImport'; import { ThemeColors } from '@theme/types'; import { useLibraryContext } from '@components/Context/LibraryContext'; import { xor } from 'lodash-es'; import { SelectionContext } from './SelectionContext'; +import { getLibraryCategoryIndex } from './constants/constants'; type State = NavigationState<{ key: string; @@ -82,7 +88,14 @@ const LibraryScreen = ({ navigation }: LibraryScreenProps) => { categories, refetchLibrary, isLoading, - settings: { showNumberOfNovels, downloadedOnlyMode, incognitoMode }, + error: libraryError, + settings: { + showNumberOfNovels, + downloadedOnlyMode, + incognitoMode, + lastUsedCategoryId, + setLibrarySettings, + }, } = useLibraryContext(); const { importNovel } = useImport(); @@ -92,9 +105,21 @@ const LibraryScreen = ({ navigation }: LibraryScreenProps) => { const layout = useWindowDimensions(); - const bottomSheetRef = useRef(null); - - const [index, setIndex] = useState(0); + const bottomSheetRef = useRef(null); + + const [selectedCategoryId, setSelectedCategoryId] = + useState(lastUsedCategoryId); + const index = getLibraryCategoryIndex(categories, selectedCategoryId); + const setIndex = useCallback( + (nextIndex: number) => { + const categoryId = categories[nextIndex]?.id; + if (categoryId !== undefined) { + setSelectedCategoryId(categoryId); + setLibrarySettings({ lastUsedCategoryId: categoryId }); + } + }, + [categories, setLibrarySettings], + ); const { value: setCategoryModalVisible, @@ -178,25 +203,17 @@ const LibraryScreen = ({ navigation }: LibraryScreenProps) => { const searchLower = useMemo(() => searchText.toLowerCase(), [searchText]); - const tabBarBorderColor = useMemo( - () => - Color(theme.isDark ? '#FFFFFF' : '#000000') - .alpha(0.12) - .string(), - [theme.isDark], - ); - const renderTabBar = useCallback( (props: SceneRendererProps & { navigationState: State }) => { return categories.length ? ( - { styles.tabBar, styles.tabBarIndicator, styles.tabStyle, - tabBarBorderColor, + theme.outlineVariant, theme.primary, theme.rippleColor, theme.secondary, @@ -244,9 +261,7 @@ const LibraryScreen = ({ navigation }: LibraryScreenProps) => { ) : unfilteredNovels; - return isLoading ? ( - - ) : ( + return ( <> {searchText ? ( - - - - - ); -}; +}: RemoveDownloadsDialogProps) => ( + +); export default RemoveDownloadsDialog; - -const styles = StyleSheet.create({ - fontSize: { - letterSpacing: 0, - fontSize: 16, - }, - borderRadius: { borderRadius: 6 }, -}); diff --git a/src/screens/novel/NovelContext.tsx b/src/screens/novel/NovelContext.tsx index 2db08342a..8377ca3c1 100644 --- a/src/screens/novel/NovelContext.tsx +++ b/src/screens/novel/NovelContext.tsx @@ -1,73 +1,122 @@ -import React, { createContext, useContext, useMemo, useRef } from 'react'; -import { useNovel } from '@hooks/persisted'; +import React, { createContext, useContext, useEffect, useMemo } from 'react'; import { RouteProp } from '@react-navigation/native'; +import { useStore } from 'zustand'; import { ReaderStackParamList } from '@navigators/types'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useDeviceOrientation } from '@hooks/index'; +import { useLibraryContext } from '@components/Context/LibraryContext'; +import { useAppSettings } from '@hooks/persisted'; +import { + NovelStoreActions, + NovelStoreApi, + NovelStoreData, + NovelStoreState, +} from '@hooks/persisted/useNovel/store/novelStore.types'; import { NovelInfo } from '@database/types'; +import { createStore } from '@hooks/persisted/useNovel/store/createStore'; -type NovelContextType = ReturnType & { +type Props = { + children: React.ReactNode; + route: + | RouteProp + | RouteProp; +}; + +type NovelLayout = { navigationBarHeight: number; statusBarHeight: number; - chapterTextCache: Map>; }; -const defaultValue = {} as NovelContextType; +const NovelStoreContext = createContext(null); +const NovelLayoutContext = createContext(null); -const NovelContext = createContext(defaultValue); +export function NovelContextProvider({ children, route }: Props) { + const initialNovel = + 'id' in route.params ? (route.params as NovelInfo) : undefined; -export function NovelContextProvider({ - children, - - route, -}: { - children: React.JSX.Element; - - route: - | RouteProp - | RouteProp; -}) { const { path, pluginId } = 'novel' in route.params ? route.params.novel : route.params; - const novelHookContent = useNovel( - 'id' in route.params ? (route.params as NovelInfo) : path, - pluginId, + const { switchNovelToLibrary } = useLibraryContext(); + const { defaultChapterSort } = useAppSettings(); + + const novelStore = useMemo( + () => + createStore({ + path, + pluginId, + novel: initialNovel, + defaultChapterSort, + switchNovelToLibrary, + }), + [defaultChapterSort, initialNovel, path, pluginId, switchNovelToLibrary], ); + useEffect(() => { + const actions = novelStore.getState().actions; + if (!actions.bootstrapNovelSync()) { + void actions.bootstrapNovel(); + } + }, [novelStore]); + const { bottom, top } = useSafeAreaInsets(); - const orientation = useDeviceOrientation(); - const NavigationBarHeight = useRef(bottom); - const StatusBarHeight = useRef(top); - const chapterTextCache = useRef>>( - new Map(), - ); - if (bottom < NavigationBarHeight.current && orientation === 'landscape') { - NavigationBarHeight.current = bottom; - } else if (bottom > NavigationBarHeight.current) { - NavigationBarHeight.current = bottom; - } - if (top > StatusBarHeight.current) { - StatusBarHeight.current = top; - } - const contextValue = useMemo( + const layoutValue = useMemo( () => ({ - ...novelHookContent, - navigationBarHeight: NavigationBarHeight.current, - statusBarHeight: StatusBarHeight.current, - chapterTextCache: chapterTextCache.current, + navigationBarHeight: bottom, + statusBarHeight: top, }), - [novelHookContent], + [bottom, top], ); return ( - - {children} - + + + {children} + + ); } -export const useNovelContext = () => { - const context = useContext(NovelContext); +function useNovelStoreApi() { + const store = useContext(NovelStoreContext); + + if (!store) { + throw new Error('useNovelStore must be used inside NovelContextProvider'); + } + + return store; +} + +export function useNovelStore(selector: (state: NovelStoreState) => T): T { + const store = useNovelStoreApi(); + return useStore(store, selector); +} + +export function useNovelState(selector: (state: NovelStoreData) => T): T { + return useNovelStore(state => selector(state)); +} + +export function useNovelValue( + key: K, +): NovelStoreData[K] { + return useNovelStore(state => state[key]); +} + +export function useNovelActions(): NovelStoreActions { + return useNovelStore(state => state.actions); +} + +export function useNovelAction( + key: K, +): NovelStoreActions[K] { + return useNovelStore(state => state.actions[key]); +} + +export function useNovelLayout() { + const context = useContext(NovelLayoutContext); + + if (!context) { + throw new Error('useNovelLayout must be used inside NovelContextProvider'); + } + return context; -}; +} diff --git a/src/screens/novel/NovelScreen.tsx b/src/screens/novel/NovelScreen.tsx index c04f037f6..9b0ee6579 100644 --- a/src/screens/novel/NovelScreen.tsx +++ b/src/screens/novel/NovelScreen.tsx @@ -1,5 +1,5 @@ -import React, { Suspense, useCallback, useMemo, useRef, useState } from 'react'; -import { StyleSheet, View, StatusBar, Text, Share } from 'react-native'; +import { Suspense, useCallback, useMemo, useRef, useState } from 'react'; +import { StyleSheet, View, StatusBar, Text } from 'react-native'; import Animated, { SlideInUp, SlideOutUp, @@ -7,110 +7,56 @@ import Animated, { } from 'react-native-reanimated'; import { Portal, Appbar, Snackbar } from 'react-native-paper'; -import { useDownload, useTheme } from '@hooks/persisted'; +import { useAppSettings, useTheme } from '@hooks/persisted'; import JumpToChapterModal from './components/JumpToChapterModal'; import { Actionbar } from '../../components/Actionbar/Actionbar'; import EditInfoModal from './components/EditInfoModal'; -import { pickCustomNovelCover } from '../../database/queries/NovelQueries'; import DownloadCustomChapterModal from './components/DownloadCustomChapterModal'; import { useBoolean } from '@hooks'; import NovelScreenLoading from './components/LoadingAnimation/NovelScreenLoading'; import { NovelScreenProps } from '@navigators/types'; -import { ChapterInfo } from '@database/types'; -import { getString } from '@strings/translations'; -import { isNumber, noop } from 'lodash-es'; +import { getString } from '@i18n/translations'; import NovelAppbar from './components/NovelAppbar'; -import { resolveUrl } from '@services/plugin/fetch'; -import { - getAllUndownloadedAndUnreadChapters, - getAllUndownloadedChapters, - updateChapterProgressByIds, -} from '@database/queries/ChapterQueries'; -import { MaterialDesignIconName } from '@type/icon'; import NovelScreenList from './components/NovelScreenList'; import { ThemeColors } from '@theme/types'; import { SafeAreaView } from '@components'; -import { useNovelContext } from './NovelContext'; -import { LegendListRef } from '@legendapp/list'; +import { useNovelActions, useNovelValue } from './NovelContext'; +import { LegendListRef } from '@legendapp/list/react-native'; +import { useCustomNovelCover } from './hooks/useCustomNovelCover'; +import { useChapterSelection } from './hooks/useChapterSelection'; +import { useNovelScreenActions } from './hooks/useNovelScreenActions'; +import { useNovelRefresh } from './hooks/useNovelRefresh'; +import SetCategoryModal from './components/SetCategoriesModal'; +import { backgroundTasks } from '@services/backgroundTasks'; const Novel = ({ route, navigation }: NovelScreenProps) => { - const { - novel, - chapters, - fetching, - batchInformation, - getNextChapterBatch, - loadUpToBatch, - setNovel, - bookmarkChapters, - markChaptersRead, - markChaptersUnread, - markPreviouschaptersRead, - markPreviousChaptersUnread, - refreshChapters, - deleteChapters, - } = useNovelContext(); + const novel = useNovelValue('novel'); + const chapters = useNovelValue('chapters'); + const { setNovel, deleteChapters, refreshNovel } = useNovelActions(); const theme = useTheme(); - const { downloadChapters } = useDownload(); + const { downloadNewChapters, refreshNovelMetadata } = useAppSettings(); - const [selected, setSelected] = useState([]); + const { + selectedIds: selected, + selectedChapters, + setSelectedIds: setSelected, + clearSelection, + selectAll, + } = useChapterSelection(chapters); const [editInfoModal, showEditInfoModal] = useState(false); const chapterListRef = useRef(null); const deleteDownloadsSnackbar = useBoolean(); + const { + value: setCategoriesModalVisible, + setTrue: showSetCategoriesModal, + setFalse: closeSetCategoriesModal, + } = useBoolean(); const headerOpacity = useSharedValue(0); - const downloadChs = useCallback( - async (amount: number | 'all' | 'unread') => { - if (!novel) { - return; - } - - let chaptersToUse = chapters; - - if (amount === 'all') { - const allChapters = await getAllUndownloadedChapters(novel.id); - chaptersToUse = allChapters; - } - - if (amount === 'unread') { - const allUnreadChapters = await getAllUndownloadedAndUnreadChapters( - novel.id, - ); - chaptersToUse = allUnreadChapters; - } - - let filtered = chaptersToUse; - - if (isNumber(amount)) { - filtered = filtered - .filter(chapter => !chapter.isDownloaded) - .slice(0, amount); - } - - if (filtered.length > 0) { - downloadChapters(novel, filtered); - } - }, - [chapters, downloadChapters, novel], - ); - - const deleteChs = useCallback(() => { - deleteChapters(chapters.filter(c => c.isDownloaded)); - }, [chapters, deleteChapters]); - - const shareNovel = useCallback(() => { - if (!novel) { - return; - } - Share.share({ - message: resolveUrl(novel.pluginId, novel.path, true), - }); - }, [novel]); - const [jumpToChapterModal, showJumpToChapterModal] = useState(false); const { value: dlChapterModalVisible, @@ -118,135 +64,34 @@ const Novel = ({ route, navigation }: NovelScreenProps) => { setFalse: closeDlChapterModal, } = useBoolean(); - const actions = useMemo(() => { - const list: { icon: MaterialDesignIconName; onPress: () => void }[] = []; - - if (!novel?.isLocal && selected.some(obj => !obj.isDownloaded)) { - list.push({ - icon: 'download-outline', - onPress: () => { - if (novel) { - downloadChapters( - novel, - selected.filter(chapter => !chapter.isDownloaded), - ); - } - setSelected([]); - }, - }); - } - if (!novel?.isLocal && selected.some(obj => obj.isDownloaded)) { - list.push({ - icon: 'trash-can-outline', - onPress: () => { - deleteChapters(selected.filter(chapter => chapter.isDownloaded)); - setSelected([]); - }, - }); - } - - list.push({ - icon: 'bookmark-outline', - onPress: () => { - bookmarkChapters(selected); - setSelected([]); - }, - }); - - if (selected.some(obj => obj.unread)) { - list.push({ - icon: 'check', - onPress: () => { - markChaptersRead(selected); - setSelected([]); - }, - }); - } - - if (selected.some(obj => !obj.unread)) { - const chapterIds = selected.map(chapter => chapter.id); - - list.push({ - icon: 'check-outline', - onPress: () => { - markChaptersUnread(selected); - updateChapterProgressByIds(chapterIds, 0); - setSelected([]); - refreshChapters(); - }, - }); - } - - if (selected.length === 1) { - if (selected[0].unread) { - list.push({ - icon: 'playlist-check', - onPress: () => { - markPreviouschaptersRead(selected[0].id); - setSelected([]); - }, - }); - } else { - list.push({ - icon: 'playlist-remove', - onPress: () => { - markPreviousChaptersUnread(selected[0].id); - setSelected([]); - }, - }); - } - } - - return list; - }, [ - bookmarkChapters, - deleteChapters, + const { + deleteDownloadedChapters, + downloadAvailableChapters, downloadChapters, - markChaptersRead, - markChaptersUnread, - markPreviousChaptersUnread, - markPreviouschaptersRead, + selectionActions, + shareNovel, + } = useNovelScreenActions({ + chapters, + clearSelection, novel, - refreshChapters, - selected, - ]); - - const setCustomNovelCover = useCallback(async () => { - if (!novel) { - return; - } - const newCover = await pickCustomNovelCover(novel); - if (newCover) { - setNovel({ - ...novel, - cover: newCover, - }); - } - }, [novel, setNovel]); + selectedChapters, + }); - const stableGetNextBatch = useMemo( - () => - batchInformation.batch < batchInformation.total && !fetching - ? getNextChapterBatch - : noop, - [batchInformation.batch, batchInformation.total, fetching, getNextChapterBatch], - ); + const setCustomNovelCover = useCustomNovelCover(novel, setNovel); + const { updating, refresh: onRefresh } = useNovelRefresh({ + novel, + downloadNewChapters, + refreshNovelMetadata, + reloadNovel: refreshNovel, + enqueue: backgroundTasks.enqueue, + }); const hideJumpToChapterModal = useCallback( () => showJumpToChapterModal(false), [], ); - const hideEditInfoModal = useCallback( - () => showEditInfoModal(false), - [], - ); - const clearSelection = useCallback(() => setSelected([]), []); - const selectAll = useCallback(() => setSelected(chapters), [chapters]); - - const snackbarTheme = useMemo( - () => ({ colors: { primary: theme.primary } }), - [theme.primary], - ); + const hideEditInfoModal = useCallback(() => showEditInfoModal(false), []); + const snackbarTheme = useMemo(() => ({ colors: theme }), [theme]); const snackbarTextStyle = useMemo( () => ({ color: theme.onSurface }), [theme.onSurface], @@ -278,13 +123,15 @@ const Novel = ({ route, navigation }: NovelScreenProps) => { {selected.length === 0 ? ( { routeBaseNovel={route.params} selected={selected} setSelected={setSelected} - getNextChapterBatch={stableGetNextBatch} + deleteDownloadSnackbar={deleteDownloadsSnackbar} + onRefresh={onRefresh} + updating={updating} /> + {novel && setCategoriesModalVisible ? ( + + ) : null} + - 0} actions={actions} /> + 0} actions={selectionActions} /> { novel={novel} chapterListRef={chapterListRef} navigation={navigation} - loadUpToBatch={loadUpToBatch} - totalChapters={batchInformation.totalChapters} - chapters={chapters} /> ({ + useAppSettings: () => ({ + downloadNewChapters: false, + refreshNovelMetadata: false, + }), + useTheme: () => ({ + primary: '#111', + onSurface: '#222', + background: '#333', + onBackground: '#444', + surface: '#555', + surface2: '#666', + }), + useDownload: () => ({ + downloadChapters: mockDownloadChapters, + }), +})); + +jest.mock('../hooks/useNovelRefresh', () => ({ + useNovelRefresh: () => ({ + refresh: jest.fn(), + updating: false, + }), +})); + +jest.mock('@services/backgroundTasks', () => ({ + backgroundTasks: { + enqueue: jest.fn(), + }, +})); + +jest.mock('@hooks', () => ({ + useBoolean: () => ({ + value: false, + setTrue: jest.fn(), + setFalse: jest.fn(), + }), +})); + +jest.mock('../NovelContext', () => ({ + useNovelValue: (key: string) => mockUseNovelValue(key), + useNovelActions: () => mockUseNovelActions(), +})); + +jest.mock('@services/plugin/fetch', () => ({ + resolveUrl: jest.fn(() => 'https://example.com'), +})); + +jest.mock('@i18n/translations', () => ({ + getString: (key: string) => key, +})); + +jest.mock('@database/queries/ChapterQueries', () => ({ + getAllUndownloadedAndUnreadChapters: jest.fn().mockResolvedValue([]), + getAllUndownloadedChapters: jest.fn().mockResolvedValue([]), + updateChapterProgressByIds: (...args: unknown[]) => + mockUpdateChapterProgressByIds(...args), +})); + +jest.mock('../../../database/queries/NovelQueries', () => ({ + pickCustomNovelCover: jest.fn().mockResolvedValue(undefined), +})); + +jest.mock('../components/NovelAppbar', () => { + const React = require('react'); + const { Text } = require('react-native'); + return () => React.createElement(Text, { testID: 'novel-appbar' }, 'appbar'); +}); + +jest.mock('../components/NovelScreenList', () => { + const React = require('react'); + const { Pressable, Text, View } = require('react-native'); + + return { + __esModule: true, + default: ({ setSelected }: any) => { + return React.createElement( + View, + null, + React.createElement( + Pressable, + { + testID: 'select-unread', + onPress: () => setSelected([10]), + }, + React.createElement(Text, null, 'select-unread'), + ), + React.createElement( + Pressable, + { + testID: 'select-read', + onPress: () => setSelected([11]), + }, + React.createElement(Text, null, 'select-read'), + ), + React.createElement( + Pressable, + { + testID: 'select-undownloaded', + onPress: () => setSelected([12]), + }, + React.createElement(Text, null, 'select-undownloaded'), + ), + ); + }, + }; +}); + +jest.mock('../../../components/Actionbar/Actionbar', () => { + const React = require('react'); + const { Pressable, Text, View } = require('react-native'); + + return { + Actionbar: ({ active, actions }: any) => { + if (!active) return null; + + return React.createElement( + View, + { testID: 'actionbar' }, + ...actions.map((action: any) => + React.createElement( + Pressable, + { + key: action.icon, + testID: `action-${action.icon}`, + onPress: action.onPress, + }, + React.createElement(Text, null, action.icon), + ), + ), + ); + }, + }; +}); + +jest.mock('@components', () => { + const React = require('react'); + return { + SafeAreaView: ({ children }: { children: React.ReactNode }) => + React.createElement(React.Fragment, null, children), + }; +}); + +jest.mock('react-native-paper', () => { + const React = require('react'); + const { Pressable, Text } = require('react-native'); + + const Portal: any = ({ children }: { children: React.ReactNode }) => + React.createElement(React.Fragment, null, children); + Portal.Host = ({ children }: { children: React.ReactNode }) => + React.createElement(React.Fragment, null, children); + + return { + Portal, + Appbar: { + Action: ({ icon, onPress }: any) => + React.createElement( + Pressable, + { testID: `appbar-action-${icon}`, onPress }, + React.createElement(Text, null, icon), + ), + Content: ({ title }: any) => React.createElement(Text, null, title), + }, + Snackbar: ({ visible, children }: any) => + visible ? React.createElement(React.Fragment, null, children) : null, + }; +}); + +jest.mock('../components/JumpToChapterModal', () => { + const React = require('react'); + const { Text } = require('react-native'); + return () => + React.createElement(Text, { testID: 'jump-to-chapter-modal' }, 'jump'); +}); + +jest.mock('../components/EditInfoModal', () => { + const React = require('react'); + const { Text } = require('react-native'); + return () => React.createElement(Text, { testID: 'edit-info-modal' }, 'edit'); +}); + +jest.mock('../components/DownloadCustomChapterModal', () => { + const React = require('react'); + const { Text } = require('react-native'); + return () => + React.createElement( + Text, + { testID: 'download-custom-modal' }, + 'download-custom', + ); +}); + +jest.mock('../components/LoadingAnimation/NovelScreenLoading', () => { + const React = require('react'); + const { Text } = require('react-native'); + return () => + React.createElement(Text, { testID: 'novel-screen-loading' }, 'loading'); +}); + +const baseNovel = { + id: 7, + path: '/novels/test', + pluginId: 'plugin.test', + name: 'Test Novel', + inLibrary: false, + totalPages: 1, + isLocal: false, +}; + +const createStore = (overrides: Record = {}) => { + const state = { + novel: baseNovel, + chapters: [ + { + id: 10, + unread: true, + isDownloaded: false, + name: 'Chapter 10', + }, + { + id: 11, + unread: false, + isDownloaded: false, + name: 'Chapter 11', + }, + { + id: 12, + unread: true, + isDownloaded: false, + name: 'Chapter 12', + }, + ], + fetching: false, + batchInformation: { batch: 0, total: 0, totalChapters: 0 }, + getNextChapterBatch: jest.fn(), + loadUpToBatch: jest.fn(), + setNovel: jest.fn(), + bookmarkChapters: jest.fn(), + markChaptersRead: jest.fn(), + markChaptersUnreadAndResetProgress: jest.fn().mockResolvedValue(true), + markPreviouschaptersRead: jest.fn(), + markPreviousChaptersUnread: jest.fn(), + refreshChapters: jest.fn(), + deleteChapters: jest.fn(), + ...overrides, + }; + + return { + getState: () => state, + subscribe: jest.fn(() => () => {}), + state, + }; +}; + +const wireStoreSelectors = (store: ReturnType) => { + mockUseNovelValue.mockImplementation( + (key: keyof typeof store.state) => store.state[key], + ); + mockUseNovelActions.mockReturnValue({ + setNovel: store.state.setNovel, + bookmarkChapters: store.state.bookmarkChapters, + markChaptersRead: store.state.markChaptersRead, + markChaptersUnreadAndResetProgress: + store.state.markChaptersUnreadAndResetProgress, + markPreviouschaptersRead: store.state.markPreviouschaptersRead, + markPreviousChaptersUnread: store.state.markPreviousChaptersUnread, + deleteChapters: store.state.deleteChapters, + }); +}; + +const route = { + params: { + name: 'Route Novel', + path: '/novels/test', + pluginId: 'plugin.test', + isLocal: false, + }, +}; + +const navigation = { + goBack: jest.fn(), + navigate: jest.fn(), +}; + +describe('NovelScreen (task 12 context boundary cutover)', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('uses novelStore action selectors for selected unread workflow', () => { + const store = createStore(); + wireStoreSelectors(store); + + render( + // @ts-expect-error narrowed test props + , + ); + + fireEvent.press(screen.getByTestId('select-unread')); + fireEvent.press(screen.getByTestId('action-check')); + + expect(store.state.markChaptersRead).toHaveBeenCalledTimes(1); + }); + + it('uses the atomic unread and progress-reset workflow', () => { + const store = createStore(); + wireStoreSelectors(store); + + render( + // @ts-expect-error narrowed test props + , + ); + + fireEvent.press(screen.getByTestId('select-read')); + fireEvent.press(screen.getByTestId('action-check-outline')); + + expect( + store.state.markChaptersUnreadAndResetProgress, + ).toHaveBeenCalledTimes(1); + expect(mockUpdateChapterProgressByIds).not.toHaveBeenCalled(); + }); + + it('keeps undefined-novel safety path for download action and guarded modals', () => { + const store = createStore({ novel: undefined }); + wireStoreSelectors(store); + + render( + // @ts-expect-error narrowed test props + , + ); + + expect(screen.queryByTestId('jump-to-chapter-modal')).toBeNull(); + expect(screen.queryByTestId('edit-info-modal')).toBeNull(); + expect(screen.queryByTestId('download-custom-modal')).toBeNull(); + + fireEvent.press(screen.getByTestId('select-undownloaded')); + fireEvent.press(screen.getByTestId('action-download-outline')); + + expect(mockDownloadChapters).not.toHaveBeenCalled(); + }); +}); diff --git a/src/screens/novel/components/Chapter/ChapterDownloadButtons.tsx b/src/screens/novel/components/Chapter/ChapterDownloadButtons.tsx index 7b2afbe06..d6454b61d 100644 --- a/src/screens/novel/components/Chapter/ChapterDownloadButtons.tsx +++ b/src/screens/novel/components/Chapter/ChapterDownloadButtons.tsx @@ -3,7 +3,7 @@ import { ThemeColors } from '@theme/types'; import { ActivityIndicator, Pressable, StyleSheet, View } from 'react-native'; import { overlay } from 'react-native-paper'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { useBoolean } from '@hooks/index'; import { IconButtonV2, Menu } from '@components'; import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; diff --git a/src/screens/novel/components/ChapterItem.tsx b/src/screens/novel/components/ChapterItem.tsx index 9ab5fb225..8fc42f3a4 100644 --- a/src/screens/novel/components/ChapterItem.tsx +++ b/src/screens/novel/components/ChapterItem.tsx @@ -7,7 +7,8 @@ import { import { ThemeColors } from '@theme/types'; import { ChapterInfo } from '@database/types'; import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; +import { DateFormat, formatDate } from '@utils/dateFormat'; interface ChapterItemProps { chapter: ChapterInfo; @@ -15,7 +16,7 @@ interface ChapterItemProps { isBookmarked?: boolean; isSelected?: boolean; isLocal: boolean; - isUpdateCard?: boolean; + variant?: 'default' | 'grouped'; theme: ThemeColors; showChapterTitles: boolean; novelName: string; @@ -24,6 +25,8 @@ interface ChapterItemProps { onDownloadChapter: (chapter: ChapterInfo) => void; onSelectPress: (chapter: ChapterInfo) => void; onSelectLongPress?: (chapter: ChapterInfo) => void; + dateFormat?: DateFormat; + relativeTimestamps?: boolean; } const ChapterItem: React.FC = ({ @@ -32,7 +35,7 @@ const ChapterItem: React.FC = ({ isBookmarked, isSelected, isLocal, - isUpdateCard, + variant = 'default', theme, showChapterTitles, novelName, @@ -41,9 +44,12 @@ const ChapterItem: React.FC = ({ onDownloadChapter, onSelectPress, onSelectLongPress, + dateFormat = 'default', + relativeTimestamps = true, }) => { const { id, name, unread, releaseTime, bookmark, chapterNumber, progress } = chapter; + const isGrouped = variant === 'grouped'; isBookmarked ??= bookmark ?? false; @@ -95,8 +101,13 @@ const ChapterItem: React.FC = ({ const releaseTimeStyle = { color: theme.outline, - marginStart: chapter.releaseTime ? 5 : 0, + marginStart: chapter.releaseTime || chapter.scanlator ? 5 : 0, } as const; + function parseTime(time?: string | Date | null) { + if (!time) return undefined; + return formatDate(time, dateFormat, relativeTimestamps); + } + const parsedTime = parseTime(releaseTime); return ( @@ -110,10 +121,10 @@ const ChapterItem: React.FC = ({ {left} {isBookmarked ? : null} - {isUpdateCard ? ( + {isGrouped ? ( = ({ = ({ - {releaseTime && !isUpdateCard ? ( + {parsedTime && !isGrouped ? ( - {releaseTime} + {parsedTime} ) : null} - {!isUpdateCard && progress && progress > 0 && chapter.unread ? ( + {chapter.scanlator && !isGrouped ? ( + + {parsedTime ? '• ' : null} + {chapter.scanlator} + + ) : null} + {!isGrouped && progress && progress > 0 && chapter.unread ? ( - {chapter.releaseTime ? '• ' : null} + {chapter.releaseTime || chapter.scanlator ? '• ' : null} {getString('novelScreen.progress', { progress })} ) : null} @@ -221,7 +246,7 @@ const styles = StyleSheet.create({ unreadIcon: { marginEnd: 4, }, - updateCardName: { + groupedChapterNovelName: { fontSize: 14, }, mt4: { diff --git a/src/screens/novel/components/DownloadCustomChapterModal.tsx b/src/screens/novel/components/DownloadCustomChapterModal.tsx index 1848952f5..0f20c78f1 100644 --- a/src/screens/novel/components/DownloadCustomChapterModal.tsx +++ b/src/screens/novel/components/DownloadCustomChapterModal.tsx @@ -1,11 +1,11 @@ -import React, { useState } from 'react'; -import { StyleSheet, Text, View, TextInput } from 'react-native'; +import { useState } from 'react'; +import { StyleSheet, View, TextInput } from 'react-native'; -import { Button, IconButton, Portal } from 'react-native-paper'; +import { IconButton } from 'react-native-paper'; import { ThemeColors } from '@theme/types'; import { ChapterInfo, NovelInfo } from '@database/types'; -import { getString } from '@strings/translations'; -import { Modal } from '@components'; +import { getString } from '@i18n/translations'; +import { Dialog } from '@components'; interface DownloadCustomChapterModalProps { theme: ThemeColors; @@ -48,25 +48,33 @@ const DownloadCustomChapterModal = ({ }; return ( - - - - {getString('novelScreen.download.customAmount')} - + + + {getString('novelScreen.download.customAmount')} + + text > 9 && setText(prevState => prevState - 10)} + onPress={() => { + if (text > 9) { + setText(prevState => prevState - 10); + } + }} /> text > 0 && setText(prevState => prevState - 1)} + onPress={() => { + if (text > 0) { + setText(prevState => prevState - 1); + } + }} /> setText(prevState => prevState + 10)} /> - - - + /> + + ); }; export default DownloadCustomChapterModal; const styles = StyleSheet.create({ - errorText: { - color: '#FF0033', - paddingTop: 8, - }, - modalTitle: { - fontSize: 16, - marginBottom: 16, - }, row: { flexDirection: 'row', justifyContent: 'center' }, marginHorizontal: { marginHorizontal: 4 }, }); diff --git a/src/screens/novel/components/EditInfoModal.tsx b/src/screens/novel/components/EditInfoModal.tsx index 3980b7666..b424cb45c 100644 --- a/src/screens/novel/components/EditInfoModal.tsx +++ b/src/screens/novel/components/EditInfoModal.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { FlatList, Pressable, @@ -9,15 +9,17 @@ import { } from 'react-native'; import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; -import { Portal, TextInput } from 'react-native-paper'; +import { TextInput } from 'react-native-paper'; import { updateNovelInfo } from '@database/queries/NovelQueries'; -import { getString } from '@strings/translations'; -import { Button, Modal } from '@components'; +import { getString } from '@i18n/translations'; +import { Dialog } from '@components'; import { ThemeColors } from '@theme/types'; import { NovelInfo } from '@database/types'; import { NovelStatus } from '@plugins/types'; import { translateNovelStatus } from '@utils/translateEnum'; +import { showToast } from '@utils/showToast'; +import { parseGenres } from '../utils/genres'; interface EditInfoModalProps { theme: ThemeColors; @@ -28,7 +30,6 @@ interface EditInfoModalProps { } // --- Dynamic style helpers --- -const getModalTitleColor = (theme: ThemeColors) => ({ color: theme.onSurface }); const getStatusLabelColor = (theme: ThemeColors) => ({ color: theme.onSurfaceVariant, }); @@ -41,188 +42,223 @@ const getStatusChipText = (selected: boolean, theme: ThemeColors) => ({ color: selected ? theme.primary : theme.onSurfaceVariant, }); const getGenreListStyle = () => styles.genreList; -const getButtonRowStyle = () => styles.buttonRow; -const getFlex1 = () => styles.flex1; // --- Main Component --- -const EditInfoModal = ({ +type EditInfoModalContentProps = Omit; + +const EditInfoModalContent = ({ theme, hideModal, - modalVisible, novel, setNovel, -}: EditInfoModalProps) => { - const initialNovelInfo = { ...novel }; +}: EditInfoModalContentProps) => { const [novelInfo, setNovelInfo] = useState(novel); + const [saving, setSaving] = useState(false); const [newGenre, setNewGenre] = useState(''); + const genres = useMemo( + () => parseGenres(novelInfo.genres), + [novelInfo.genres], + ); const removeTag = (t: string) => { - setNovelInfo({ - ...novel, - genres: novelInfo.genres - ?.split(',') + setNovelInfo(current => ({ + ...current, + genres: parseGenres(current.genres) .filter(item => item !== t) - ?.join(','), - }); + .join(','), + })); }; const status = Object.values(NovelStatus); + const persistNovelInfo = async (nextNovel: NovelInfo, dismiss: boolean) => { + setSaving(true); + try { + await updateNovelInfo(nextNovel); + setNovel(nextNovel); + if (dismiss) { + hideModal(); + } + } catch (error) { + showToast(error instanceof Error ? error.message : String(error)); + } finally { + setSaving(false); + } + }; return ( - - - - {getString('novelScreen.edit.info')} - - - - {getString('novelScreen.edit.status')} - - - {status.map((item, index) => ( - - setNovelInfo({ ...novel, status: item })} + !saving && hideModal()}> + {getString('novelScreen.edit.info')} + + + + + {getString('novelScreen.edit.status')} + + + {status.map((item, index) => ( + - + setNovelInfo(current => ({ ...current, status: item })) + } > - {translateNovelStatus(item)} - - - - ))} - - - setNovelInfo({ ...novel, name: text })} - dense - style={styles.inputWrapper} - /> - setNovelInfo({ ...novel, author: text })} - dense - style={styles.inputWrapper} - /> - setNovelInfo({ ...novel, artist: text })} - dense - style={styles.inputWrapper} - /> - setNovelInfo({ ...novel, summary: text })} - theme={{ colors: { ...theme } }} - dense - style={styles.inputWrapper} - /> + + {translateNovelStatus(item)} + + + + ))} + + + + setNovelInfo(current => ({ ...current, name })) + } + dense + style={styles.inputWrapper} + /> + + setNovelInfo(current => ({ ...current, author })) + } + dense + style={styles.inputWrapper} + /> + + setNovelInfo(current => ({ ...current, artist })) + } + dense + style={styles.inputWrapper} + /> + + setNovelInfo(current => ({ ...current, summary })) + } + theme={{ colors: { ...theme } }} + dense + style={styles.inputWrapper} + /> - setNewGenre(text)} - onSubmitEditing={() => { - const newGenreTrimmed = newGenre.trim(); + setNewGenre(text)} + onSubmitEditing={() => { + const newGenreTrimmed = newGenre.trim(); - if (newGenreTrimmed === '') { - return; - } + if (newGenreTrimmed === '') { + return; + } - setNovelInfo(prevVal => ({ - ...prevVal, - genres: novelInfo.genres - ? `${novelInfo.genres},` + newGenreTrimmed - : newGenreTrimmed, - })); - setNewGenre(''); + setNovelInfo(prevVal => ({ + ...prevVal, + genres: [...parseGenres(prevVal.genres), newGenreTrimmed].join( + ',', + ), + })); + setNewGenre(''); + }} + theme={{ colors: { ...theme } }} + dense + style={styles.inputWrapper} + /> + + {genres.length > 0 ? ( + 'novelTag' + index} + renderItem={({ item }) => ( + removeTag(item)}> + {item} + + )} + showsHorizontalScrollIndicator={false} + /> + ) : null} + + + + { + setNovelInfo(novel); + void persistNovelInfo(novel, false); }} - theme={{ colors: { ...theme } }} - dense - style={styles.inputWrapper} /> - - {novelInfo.genres !== undefined && novelInfo.genres !== '' ? ( - 'novelTag' + index} - renderItem={({ item }) => ( - removeTag(item)}> - {item} - - )} - showsHorizontalScrollIndicator={false} - /> - ) : null} - - - - - - - - + + { + void persistNovelInfo(novelInfo, true); + }} + /> + + ); }; +const EditInfoModal = ({ modalVisible, ...props }: EditInfoModalProps) => + modalVisible ? : null; + export default EditInfoModal; // --- GenreChip with split styles --- @@ -268,9 +304,8 @@ const styles = StyleSheet.create({ fontSize: 14, marginBottom: 12, }, - modalTitle: { - fontSize: 24, - marginBottom: 16, + formContent: { + paddingHorizontal: 24, }, statusRow: { marginVertical: 8, @@ -291,12 +326,6 @@ const styles = StyleSheet.create({ genreList: { marginVertical: 8, }, - buttonRow: { - flexDirection: 'row', - }, - flex1: { - flex: 1, - }, genreChipContainer: { flex: 1, flexDirection: 'row', diff --git a/src/screens/novel/components/ExportEpubModal.tsx b/src/screens/novel/components/ExportEpubModal.tsx index e41d97327..ddcc1cd51 100644 --- a/src/screens/novel/components/ExportEpubModal.tsx +++ b/src/screens/novel/components/ExportEpubModal.tsx @@ -1,23 +1,38 @@ import React, { useState } from 'react'; -import { StyleSheet, View } from 'react-native'; -import { TextInput, Text } from 'react-native-paper'; -import { openDocumentTree } from 'react-native-saf-x'; +import { Pressable, ScrollView, StyleSheet, View } from 'react-native'; +import { HelperText, TextInput } from 'react-native-paper'; -import { Button, List, Modal, SwitchItem } from '@components'; +import { Dialog, SwitchItem } from '@components'; +import NativeFile from '@modules/native-file'; import { useBoolean } from '@hooks'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { useChapterReaderSettings, useTheme } from '@hooks/persisted'; import { showToast } from '@utils/showToast'; interface ExportEpubModalProps { isVisible: boolean; - onSubmit?: (uri: string, startChapter?: number, endChapter?: number) => void; + defaultFileName: string; + onSubmit: ( + uri: string, + fileName: string, + options: EpubExportOptions, + startChapter?: number, + endChapter?: number, + ) => Promise; hideModal: () => void; } +export interface EpubExportOptions { + useAppTheme: boolean; + useCustomCSS: boolean; + useCustomJS: boolean; + includeChapterNumber: boolean; +} + const ExportEpubModal: React.FC = ({ isVisible, + defaultFileName, onSubmit: onSubmitProp, hideModal, }) => { @@ -27,186 +42,302 @@ const ExportEpubModal: React.FC = ({ epubUseAppTheme = false, epubUseCustomCSS = false, epubUseCustomJS = false, + epubIncludeChapterNumber = false, setChapterReaderSettings, } = useChapterReaderSettings(); const [uri, setUri] = useState(epubLocation); + const [fileName, setFileName] = useState(defaultFileName); const useAppTheme = useBoolean(epubUseAppTheme); const useCustomCSS = useBoolean(epubUseCustomCSS); const useCustomJS = useBoolean(epubUseCustomJS); + const includeChapterNumber = useBoolean(epubIncludeChapterNumber); const exportAll = useBoolean(true); const [startChapter, setStartChapter] = useState(''); const [endChapter, setEndChapter] = useState(''); + const [fileNameError, setFileNameError] = useState(false); + const [rangeError, setRangeError] = useState(''); + const [submitting, setSubmitting] = useState(false); const onDismiss = () => { + if (submitting) { + return; + } + hideModal(); setUri(epubLocation); + setFileName(defaultFileName); + setFileNameError(false); + setRangeError(''); exportAll.setTrue(); setStartChapter(''); setEndChapter(''); }; - const onSubmit = () => { - if (!exportAll.value) { - const start = parseInt(startChapter, 10); - const end = parseInt(endChapter, 10); + const onSubmit = async () => { + const trimmedFileName = fileName.trim(); + if (!trimmedFileName) { + setFileNameError(true); + return; + } - if (isNaN(start) || isNaN(end)) { - showToast(getString('novelScreen.exportEpubModal.invalidRange')); - return; - } + let start: number | undefined; + let end: number | undefined; + + if (!exportAll.value) { + start = Number(startChapter); + end = Number(endChapter); - if (start < 1 || end < 1) { - showToast(getString('novelScreen.exportEpubModal.invalidRange')); + if ( + !Number.isInteger(start) || + !Number.isInteger(end) || + start < 1 || + end < 1 + ) { + setRangeError(getString('novelScreen.exportEpubModal.invalidRange')); return; } if (start > end) { - showToast(getString('novelScreen.exportEpubModal.startGreaterThanEnd')); + setRangeError( + getString('novelScreen.exportEpubModal.startGreaterThanEnd'), + ); return; } } + setFileNameError(false); + setRangeError(''); setChapterReaderSettings({ epubLocation: uri, epubUseAppTheme: useAppTheme.value, epubUseCustomCSS: useCustomCSS.value, epubUseCustomJS: useCustomJS.value, + epubIncludeChapterNumber: includeChapterNumber.value, }); - const start = exportAll.value ? undefined : parseInt(startChapter, 10); - const end = exportAll.value ? undefined : parseInt(endChapter, 10); - - onSubmitProp?.(uri, start, end); - hideModal(); + setSubmitting(true); + try { + await onSubmitProp( + uri, + trimmedFileName, + { + useAppTheme: useAppTheme.value, + useCustomCSS: useCustomCSS.value, + useCustomJS: useCustomJS.value, + includeChapterNumber: includeChapterNumber.value, + }, + start, + end, + ); + hideModal(); + } finally { + setSubmitting(false); + } }; const openFolderPicker = async () => { try { - const resultUri = await openDocumentTree(true); - if (resultUri) { - setUri(resultUri.uri); - } - } catch (error: any) { - showToast(error.message); + const result = await NativeFile.pickDirectory(); + setUri(result.uri); + } catch (error) { + showToast(error instanceof Error ? error.message : String(error)); } }; return ( - - - + + + {getString('novelScreen.exportEpubModal.title')} - - - } - /> - - - - {!exportAll.value && ( - - - + + + {getString('novelScreen.exportEpubModal.description')} + + + + + + void openFolderPicker()} + > + void openFolderPicker()} + /> + } + theme={{ colors: { ...theme } }} + value={uri} + /> + + + { + setFileName(value); + if (value.trim()) { + setFileNameError(false); + } + }} + onSubmitEditing={() => void onSubmit()} + returnKeyType="done" + right={} + theme={{ colors: { ...theme } }} + value={fileName} + /> + {fileNameError ? ( + + {getString('novelScreen.exportEpubModal.fileNameRequired')} + + ) : null} + - )} - { + exportAll.toggle(); + setRangeError(''); + }} + theme={theme} + /> + + {!exportAll.value ? ( + <> + + { + setStartChapter(value); + setRangeError(''); + }} + keyboardType="number-pad" + mode="outlined" + returnKeyType="next" + theme={{ colors: { ...theme } }} + style={styles.rangeInput} + /> + { + setEndChapter(value); + setRangeError(''); + }} + keyboardType="number-pad" + mode="outlined" + onSubmitEditing={() => void onSubmit()} + returnKeyType="done" + theme={{ colors: { ...theme } }} + style={styles.rangeInput} + /> + + + {rangeError} + + + ) : null} + + + + + + + - void onSubmit()} + title={getString('novelScreen.exportEpubModal.export')} /> - - - - - - - - + + Chapters + + + + + + {getString('common.cancel')} + + + {getString('common.save')} + + + ); }; -export default SetTrackChaptersDialog; +const SetTrackChaptersDialog: React.FC = ({ + visible, + ...props +}) => (visible ? : null); -const styles = StyleSheet.create({ - buttonContainer: { - flexDirection: 'row', - justifyContent: 'flex-end', - gap: 8, - marginTop: 16, - }, -}); +export default SetTrackChaptersDialog; diff --git a/src/screens/novel/components/Tracker/SetTrackScoreDialog.tsx b/src/screens/novel/components/Tracker/SetTrackScoreDialog.tsx index 92bb2b167..064ca9e01 100644 --- a/src/screens/novel/components/Tracker/SetTrackScoreDialog.tsx +++ b/src/screens/novel/components/Tracker/SetTrackScoreDialog.tsx @@ -1,8 +1,7 @@ -import React, { useEffect, useMemo, useState } from 'react'; -import { StyleSheet, View } from 'react-native'; +import React, { useMemo, useState } from 'react'; -import { Button, DialogTitle, Modal } from '@components'; -import { getString } from '@strings/translations'; +import { Dialog } from '@components'; +import { getString } from '@i18n/translations'; import { AniListScoreSelector, KitsuScoreSelector, @@ -11,21 +10,16 @@ import { } from './ScoreSelectors'; import { TrackScoreDialogProps } from './types'; -const SetTrackScoreDialog: React.FC = ({ +type SetTrackScoreDialogContentProps = Omit; + +const SetTrackScoreDialogContent: React.FC = ({ tracker, trackItem, - visible, onDismiss, onUpdateScore, }) => { const [selectedScore, setSelectedScore] = useState(trackItem.score); - useEffect(() => { - if (visible) { - setSelectedScore(trackItem.score); - } - }, [visible, trackItem.score]); - const handleSave = () => { onUpdateScore(selectedScore); onDismiss(); @@ -67,24 +61,28 @@ const SetTrackScoreDialog: React.FC = ({ }, [tracker, trackItem, selectedScore]); return ( - - - {ScoreSelector} - - - - - + + Score + {tracker.name === 'Kitsu' || tracker.name === 'AniList' ? ( + {ScoreSelector} + ) : ( + {ScoreSelector} + )} + + + {getString('common.cancel')} + + + {getString('common.save')} + + + ); }; -export default SetTrackScoreDialog; +const SetTrackScoreDialog: React.FC = ({ + visible, + ...props +}) => (visible ? : null); -const styles = StyleSheet.create({ - buttonContainer: { - flexDirection: 'row', - justifyContent: 'flex-end', - gap: 8, - marginTop: 16, - }, -}); +export default SetTrackScoreDialog; diff --git a/src/screens/novel/components/Tracker/SetTrackStatusDialog.tsx b/src/screens/novel/components/Tracker/SetTrackStatusDialog.tsx index 25d308b73..0c97f2cb3 100644 --- a/src/screens/novel/components/Tracker/SetTrackStatusDialog.tsx +++ b/src/screens/novel/components/Tracker/SetTrackStatusDialog.tsx @@ -1,29 +1,21 @@ -import React, { useEffect, useState } from 'react'; -import { StyleSheet, View } from 'react-native'; +import React, { useState } from 'react'; -import { Button, DialogTitle, Modal } from '@components'; +import { Dialog } from '@components'; import { RadioButton, RadioButtonGroup } from '@components/RadioButton'; import { useTheme } from '@hooks/persisted'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { UserListStatus } from '@services/Trackers'; import { STATUS_LABELS } from './constants'; import { TrackStatusDialogProps } from './types'; -const SetTrackStatusDialog: React.FC = ({ - trackItem, - visible, - onDismiss, - onUpdateStatus, -}) => { +type SetTrackStatusDialogContentProps = Omit; + +const SetTrackStatusDialogContent: React.FC< + SetTrackStatusDialogContentProps +> = ({ trackItem, onDismiss, onUpdateStatus }) => { const theme = useTheme(); const [selectedStatus, setSelectedStatus] = useState(trackItem.status); - useEffect(() => { - if (visible) { - setSelectedStatus(trackItem.status); - } - }, [visible, trackItem.status]); - const handleSave = () => { onUpdateStatus(selectedStatus); onDismiss(); @@ -34,31 +26,33 @@ const SetTrackStatusDialog: React.FC = ({ }; return ( - - - - {Object.entries(STATUS_LABELS).map(([key, label]) => ( - - ))} - - - - - - + + Status + + + {Object.entries(STATUS_LABELS).map(([key, label]) => ( + + ))} + + + + + {getString('common.cancel')} + + + {getString('common.save')} + + + ); }; -export default SetTrackStatusDialog; +const SetTrackStatusDialog: React.FC = ({ + visible, + ...props +}) => (visible ? : null); -const styles = StyleSheet.create({ - buttonContainer: { - flexDirection: 'row', - justifyContent: 'flex-end', - gap: 8, - marginTop: 16, - }, -}); +export default SetTrackStatusDialog; diff --git a/src/screens/novel/components/Tracker/TrackSearchDialog.tsx b/src/screens/novel/components/Tracker/TrackSearchDialog.tsx index fcec09b4f..179a3b950 100644 --- a/src/screens/novel/components/Tracker/TrackSearchDialog.tsx +++ b/src/screens/novel/components/Tracker/TrackSearchDialog.tsx @@ -1,12 +1,17 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { ActivityIndicator, Image, StyleSheet, Text, View } from 'react-native'; -import { ScrollView } from 'react-native-gesture-handler'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { + ActivityIndicator, + StyleSheet, + Text, + TextInputSubmitEditingEvent, +} from 'react-native'; import { TextInput, TouchableRipple } from 'react-native-paper'; import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; +import { FlashList } from '@shopify/flash-list'; -import { Button, Modal } from '@components'; +import { Dialog, NovelCoverImage } from '@components'; import { getTracker, useTheme } from '@hooks/persisted'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { SearchResult } from '@services/Trackers'; import { TrackSearchDialogProps } from './types'; import { showToast } from '@utils/showToast'; @@ -20,41 +25,128 @@ const TrackSearchDialog: React.FC = ({ novelName, }) => { const theme = useTheme(); - const [loading, setLoading] = useState(true); + const [loading, setLoading] = useState(false); const [searchResults, setSearchResults] = useState([]); - const [searchText, setSearchText] = useState(novelName); + const [searchTextOverride, setSearchTextOverride] = useState(); const [selectedNovel, setSelectedNovel] = useState(); + const latestRequestId = useRef(0); + const searchTimer = useRef | null>(null); + const searchText = searchTextOverride ?? novelName; - const getSearchResults = useCallback(async () => { - setLoading(true); - try { - const trackerObj = getTracker(tracker.name); - const results = await trackerObj.handleSearch(searchText, tracker.auth); - setSearchResults(results); - } catch (error) { - showToast( - `Failed to fetch search results from ${tracker.name}: ${getErrorMessage( - error, - )}`, - ); - setSearchResults([]); - } finally { - setLoading(false); + const getSearchResults = useCallback( + async (query: string) => { + const normalizedQuery = query.trim(); + const requestId = ++latestRequestId.current; + + if (!normalizedQuery) { + setLoading(false); + setSearchResults([]); + return; + } + + setLoading(true); + try { + const trackerObj = getTracker(tracker.name); + const results = await trackerObj.handleSearch( + normalizedQuery, + tracker.auth, + ); + + if (requestId === latestRequestId.current) { + setSearchResults(results); + } + } catch (error) { + if (requestId === latestRequestId.current) { + showToast( + `Failed to fetch search results from ${ + tracker.name + }: ${getErrorMessage(error)}`, + ); + setSearchResults([]); + } + } finally { + if (requestId === latestRequestId.current) { + setLoading(false); + } + } + }, + [tracker.auth, tracker.name], + ); + + const cancelScheduledSearch = useCallback(() => { + if (searchTimer.current) { + clearTimeout(searchTimer.current); + searchTimer.current = null; } - }, [searchText, tracker.auth, tracker.name]); + }, []); + + const scheduleSearch = useCallback( + (query: string) => { + cancelScheduledSearch(); + latestRequestId.current += 1; + searchTimer.current = setTimeout(() => { + void getSearchResults(query); + }, 350); + }, + [cancelScheduledSearch, getSearchResults], + ); + + useEffect( + () => () => { + cancelScheduledSearch(); + latestRequestId.current += 1; + }, + [cancelScheduledSearch], + ); useEffect(() => { - if (visible) { - getSearchResults(); + if (!visible) { + cancelScheduledSearch(); + latestRequestId.current += 1; + return; } - }, [getSearchResults, visible]); - const handleSelectNovel = useCallback((item: SearchResult) => { - setSelectedNovel(item); - }, []); + cancelScheduledSearch(); + searchTimer.current = setTimeout(() => { + void getSearchResults(novelName); + }, 0); + }, [cancelScheduledSearch, getSearchResults, novelName, visible]); + + const handleSearchTextChange = useCallback( + (value: string) => { + setSearchTextOverride(value); + setSelectedNovel(undefined); + scheduleSearch(value); + }, + [scheduleSearch], + ); + + const handleSubmitSearch = useCallback( + (event: TextInputSubmitEditingEvent) => { + cancelScheduledSearch(); + void getSearchResults(event.nativeEvent.text); + }, + [cancelScheduledSearch, getSearchResults], + ); const handleClearSearch = useCallback(() => { - setSearchText(''); + cancelScheduledSearch(); + latestRequestId.current += 1; + setSearchTextOverride(''); + setSearchResults([]); + setLoading(false); + }, [cancelScheduledSearch]); + + const handleDismiss = useCallback(() => { + cancelScheduledSearch(); + latestRequestId.current += 1; + setSearchTextOverride(undefined); + setSelectedNovel(undefined); + onDismiss(); + }, [cancelScheduledSearch, onDismiss]); + + const handleSelectNovel = useCallback((item: SearchResult) => { + setSelectedNovel(item); }, []); const handleRemoveSelection = useCallback(() => { @@ -65,8 +157,8 @@ const TrackSearchDialog: React.FC = ({ if (selectedNovel) { onTrackNovel(tracker, selectedNovel); } - onDismiss(); - }, [selectedNovel, onTrackNovel, tracker, onDismiss]); + handleDismiss(); + }, [selectedNovel, onTrackNovel, tracker, handleDismiss]); const renderSearchResultCard = useCallback( (item: SearchResult) => { @@ -94,8 +186,10 @@ const TrackSearchDialog: React.FC = ({ style={styles.checkIcon} /> )} - = ({ ); }, - [selectedNovel, handleSelectNovel, theme.rippleColor, theme.primary, theme.onSurface], + [selectedNovel, handleSelectNovel, theme], ); return ( - - - } - /> - - {loading ? ( - - ) : ( - searchResults.map(renderSearchResultCard) - )} - - - - - - - - - + + {tracker.name} + + + } + /> + + + item.id.toString()} + ListEmptyComponent={ + loading ? ( + + ) : null + } + renderItem={({ item }) => renderSearchResultCard(item)} + style={styles.resultsList} + /> + + + + + + + ); }; export default TrackSearchDialog; const styles = StyleSheet.create({ - actionButtons: { - flexDirection: 'row', - }, - buttonContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - marginTop: 30, - }, checkIcon: { position: 'absolute', right: 8, @@ -191,7 +289,7 @@ const styles = StyleSheet.create({ padding: 8, paddingLeft: 0, }, - scrollView: { + resultsList: { flexGrow: 1, marginVertical: 8, maxHeight: 500, diff --git a/src/screens/novel/components/Tracker/TrackSheet.tsx b/src/screens/novel/components/Tracker/TrackSheet.tsx index 0a0b6e43f..b86916587 100644 --- a/src/screens/novel/components/Tracker/TrackSheet.tsx +++ b/src/screens/novel/components/Tracker/TrackSheet.tsx @@ -1,9 +1,9 @@ import React, { useCallback, useMemo, useState } from 'react'; import { ScrollView, StyleSheet, ToastAndroid, View } from 'react-native'; -import { Portal, overlay } from 'react-native-paper'; +import { Portal } from 'react-native-paper'; import BottomSheet from '@components/BottomSheet/BottomSheet'; -import { useTheme, useTracker, useTrackedNovel } from '@hooks/persisted'; +import { useTracker, useTrackedNovel } from '@hooks/persisted'; import { TrackerName, UserListStatus } from '@services/Trackers'; import { NovelInfo } from '@database/types'; import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types'; @@ -21,7 +21,6 @@ interface TrackSheetProps { } const TrackSheet: React.FC = ({ bottomSheetRef, novel }) => { - const theme = useTheme(); const { getAuthenticatedTrackers } = useTracker(); const { getTrackedNovel, @@ -156,6 +155,9 @@ const TrackSheet: React.FC = ({ bottomSheetRef, novel }) => { return [Math.min(totalHeight, 600)]; // Cap at 600px }, [authenticatedTrackers, isTrackedOn]); + const activeTrackedNovel = activeTracker + ? getTrackedNovel(activeTracker.name) + : undefined; if (authenticatedTrackers.length === 0) { return null; @@ -164,12 +166,7 @@ const TrackSheet: React.FC = ({ bottomSheetRef, novel }) => { return ( <> - + {authenticatedTrackers.map(tracker => { const trackerIcon = getTrackerIcon(tracker.name); const trackedNovel = getTrackedNovel(tracker.name); @@ -201,43 +198,49 @@ const TrackSheet: React.FC = ({ bottomSheetRef, novel }) => { - {activeTracker && ( + {activeTracker ? ( <> - {getTrackedNovel(activeTracker.name) ? ( + {activeTrackedNovel ? ( <> - - - + {trackStatusDialog ? ( + + ) : null} + {trackChaptersDialog ? ( + + ) : null} + {trackScoreDialog ? ( + + ) : null} - ) : ( + ) : trackSearchDialog ? ( - )} + ) : null} - )} + ) : null} ); diff --git a/src/screens/novel/components/Tracker/__tests__/TrackSearchDialog.test.tsx b/src/screens/novel/components/Tracker/__tests__/TrackSearchDialog.test.tsx new file mode 100644 index 000000000..ea28783b0 --- /dev/null +++ b/src/screens/novel/components/Tracker/__tests__/TrackSearchDialog.test.tsx @@ -0,0 +1,196 @@ +import React from 'react'; +import { act, fireEvent, render, screen } from '@testing-library/react-native'; + +import TrackSearchDialog from '../TrackSearchDialog'; +import type { SearchResult } from '@services/Trackers'; +import type { TrackerMetadata } from '@hooks/persisted/useTracker'; + +const mockHandleSearch = jest.fn(); + +jest.mock('@hooks/persisted', () => ({ + getTracker: () => ({ handleSearch: mockHandleSearch }), + useTheme: () => ({ + onSurface: '#111111', + onSurfaceVariant: '#444444', + outline: '#777777', + primary: '#006666', + rippleColor: '#dddddd', + surface: '#ffffff', + surfaceVariant: '#eeeeee', + }), +})); + +jest.mock('@components', () => { + const ReactModule = jest.requireActual('react'); + const { Text, View } = + jest.requireActual('react-native'); + const Section = ({ children }: { children: React.ReactNode }) => + ReactModule.createElement(View, null, children); + + return { + Dialog: { + Action: ({ onPress, title }: { onPress: () => void; title: string }) => + ReactModule.createElement(Text, { onPress }, title), + Actions: Section, + Content: Section, + Root: Section, + ScrollArea: Section, + Title: Section, + }, + NovelCoverImage: () => null, + }; +}); + +jest.mock('react-native-paper', () => { + const ReactModule = jest.requireActual('react'); + const { Pressable, TextInput: NativeTextInput } = + jest.requireActual('react-native'); + const TextInput = (props: Record) => + ReactModule.createElement(NativeTextInput, { + ...props, + testID: 'tracker-search-input', + }); + TextInput.Icon = () => null; + + return { + TextInput, + TouchableRipple: ({ + children, + onPress, + }: { + children: React.ReactNode; + onPress: () => void; + }) => ReactModule.createElement(Pressable, { onPress }, children), + }; +}); + +jest.mock('@shopify/flash-list', () => { + const ReactModule = jest.requireActual('react'); + const { View } = + jest.requireActual('react-native'); + + return { + FlashList: ({ + data, + ListEmptyComponent, + renderItem, + }: { + data: SearchResult[]; + ListEmptyComponent?: React.ReactNode; + renderItem: ({ item }: { item: SearchResult }) => React.ReactElement; + }) => + ReactModule.createElement( + View, + null, + data.length + ? data.map(item => + ReactModule.createElement( + ReactModule.Fragment, + { key: item.id }, + renderItem({ item }), + ), + ) + : ListEmptyComponent, + ), + }; +}); + +jest.mock('@react-native-vector-icons/material-design-icons', () => 'Icon'); + +const tracker = { + auth: { accessToken: 'token' }, + name: 'AniList', +} as TrackerMetadata; + +const deferred = () => { + let resolve!: (value: T) => void; + const promise = new Promise(promiseResolve => { + resolve = promiseResolve; + }); + return { promise, resolve }; +}; + +describe('TrackSearchDialog', () => { + beforeEach(() => { + jest.useFakeTimers(); + mockHandleSearch.mockReset(); + mockHandleSearch.mockResolvedValue([]); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('debounces search text changes and searches only the latest value', async () => { + render( + , + ); + + fireEvent.changeText( + screen.getByTestId('tracker-search-input'), + 'First value', + ); + act(() => jest.advanceTimersByTime(200)); + fireEvent.changeText( + screen.getByTestId('tracker-search-input'), + 'Latest value', + ); + act(() => jest.advanceTimersByTime(349)); + + expect(mockHandleSearch).not.toHaveBeenCalled(); + + await act(async () => { + jest.advanceTimersByTime(1); + await Promise.resolve(); + }); + + expect(mockHandleSearch).toHaveBeenCalledTimes(1); + expect(mockHandleSearch).toHaveBeenCalledWith('Latest value', tracker.auth); + }); + + it('ignores a stale response that finishes after a newer search', async () => { + const firstSearch = deferred(); + const secondSearch = deferred(); + mockHandleSearch + .mockReturnValueOnce(firstSearch.promise) + .mockReturnValueOnce(secondSearch.promise); + + render( + , + ); + + act(() => jest.advanceTimersByTime(350)); + fireEvent.changeText( + screen.getByTestId('tracker-search-input'), + 'New title', + ); + act(() => jest.advanceTimersByTime(350)); + + await act(async () => { + secondSearch.resolve([{ coverImage: '', id: 2, title: 'Newer result' }]); + await Promise.resolve(); + }); + + expect(screen.getByText('Newer result')).toBeTruthy(); + + await act(async () => { + firstSearch.resolve([{ coverImage: '', id: 1, title: 'Stale result' }]); + await Promise.resolve(); + }); + + expect(screen.queryByText('Stale result')).toBeNull(); + expect(screen.getByText('Newer result')).toBeTruthy(); + }); +}); diff --git a/src/screens/novel/components/__tests__/NovelScreenList.test.tsx b/src/screens/novel/components/__tests__/NovelScreenList.test.tsx new file mode 100644 index 000000000..17966f0c5 --- /dev/null +++ b/src/screens/novel/components/__tests__/NovelScreenList.test.tsx @@ -0,0 +1,433 @@ +import { fireEvent, render, screen } from '@testing-library/react-native'; +import NovelScreenList from '../NovelScreenList'; + +const mockUseNovelValue = jest.fn(); +const mockUseNovelActions = jest.fn(); +const mockDownloadChapter = jest.fn(); +let mockDownloadingChapterIds = new Set(); +let mockDownloadingNovelIds = new Set(); +let mockDownloadQueue: any[] = []; + +jest.mock('../../NovelContext', () => ({ + useNovelValue: (key: string) => mockUseNovelValue(key), + useNovelActions: () => mockUseNovelActions(), +})); + +jest.mock('@hooks/persisted', () => ({ + useAppSettings: () => ({ + useFabForContinueReading: true, + disableHapticFeedback: true, + downloadNewChapters: false, + refreshNovelMetadata: false, + }), + useDownload: () => ({ + downloadQueue: mockDownloadQueue, + downloadingChapterIds: mockDownloadingChapterIds, + downloadingNovelIds: mockDownloadingNovelIds, + downloadChapter: mockDownloadChapter, + }), + useTheme: () => ({ + primary: '#111', + onPrimary: '#fff', + surface2: '#222', + }), +})); + +jest.mock('@hooks/index', () => ({ + useBoolean: () => ({ + value: false, + setTrue: jest.fn(), + setFalse: jest.fn(), + }), +})); + +jest.mock('react-native-safe-area-context', () => ({ + useSafeAreaInsets: () => ({ top: 0, bottom: 0 }), +})); + +jest.mock('@legendapp/list/reanimated', () => { + const React = require('react'); + const { View } = require('react-native'); + + return { + AnimatedLegendList: ({ data, renderItem, ListHeaderComponent }: any) => + React.createElement( + View, + null, + ListHeaderComponent, + ...(data || []).map((item: any, index: number) => + React.createElement( + React.Fragment, + { key: `row-${item.id ?? index}` }, + renderItem({ item, index }), + ), + ), + ), + }; +}); + +jest.mock('../ChapterItem', () => { + const React = require('react'); + const { Pressable, Text, View } = require('react-native'); + + return ({ chapter, onDeleteChapter }: any) => + React.createElement( + View, + { testID: `chapter-item-${chapter.id}` }, + React.createElement( + Pressable, + { + testID: `delete-chapter-${chapter.id}`, + onPress: () => onDeleteChapter(chapter), + }, + React.createElement(Text, null, 'delete'), + ), + ); +}); + +jest.mock('../Info/NovelInfoHeader', () => { + const React = require('react'); + const { Text } = require('react-native'); + return () => + React.createElement(Text, { testID: 'novel-info-header' }, 'hdr'); +}); + +jest.mock('../PagePaginationControl', () => { + const React = require('react'); + const { Pressable, Text, View } = require('react-native'); + + return ({ onPageChange }: any) => + React.createElement( + View, + null, + React.createElement( + Pressable, + { testID: 'pagination-change-page', onPress: () => onPageChange(1) }, + React.createElement(Text, null, 'change-page'), + ), + ); +}); + +jest.mock('../NovelBottomSheet', () => { + const React = require('react'); + const { Text } = require('react-native'); + return () => + React.createElement(Text, { testID: 'novel-bottom-sheet' }, 'nbs'); +}); + +jest.mock('../Tracker/TrackSheet', () => { + const React = require('react'); + const { Text } = require('react-native'); + return () => React.createElement(Text, { testID: 'track-sheet' }, 'track'); +}); + +jest.mock('../PageNavigationBottomSheet', () => { + const React = require('react'); + const { Text } = require('react-native'); + return () => + React.createElement(Text, { testID: 'page-navigation-sheet' }, 'navsheet'); +}); + +jest.mock('react-native-paper', () => { + const React = require('react'); + const { Pressable, Text } = require('react-native'); + + return { + AnimatedFAB: ({ onPress, label }: any) => + React.createElement( + Pressable, + { + testID: label ? 'continue-reading-fab' : 'scroll-to-top-fab', + onPress, + }, + React.createElement(Text, null, label || 'fab'), + ), + }; +}); + +jest.mock('@components/Skeleton/Skeleton', () => ({ + ChapterListSkeleton: () => null, +})); + +jest.mock('@database/queries/NovelQueries', () => ({ + pickCustomNovelCover: jest.fn(), +})); + +jest.mock('@services/updates/LibraryUpdateQueries', () => ({ + updateNovel: jest.fn(), + updateNovelPage: jest.fn(), +})); + +jest.mock('@i18n/translations', () => ({ + getString: (key: string) => key, +})); + +jest.mock('@utils/showToast', () => ({ + showToast: jest.fn(), +})); + +jest.mock('@modules/native-file', () => ({ + __esModule: true, + default: { + ExternalCachesDirectoryPath: '/tmp', + copyFile: jest.fn(), + copyFileToDirectory: jest.fn(), + pickDirectory: jest.fn(), + unlink: jest.fn(), + }, +})); + +jest.mock('@plugins/helpers/fetch', () => ({ + downloadFile: jest.fn(), +})); + +jest.mock('expo-file-system/legacy', () => ({ + StorageAccessFramework: { + requestDirectoryPermissionsAsync: jest.fn(), + createFileAsync: jest.fn(), + }, +})); + +jest.mock('expo-haptics', () => ({ + impactAsync: jest.fn(), + ImpactFeedbackStyle: { Medium: 'medium' }, +})); + +const baseChapter = { + id: 1, + novelId: 7, + path: '/chapter/1', + name: 'Chapter 1', + releaseTime: '2026-01-01', + updatedTime: '2026-01-01', + readTime: '2026-01-01', + chapterNumber: 1, + bookmark: false, + progress: 0, + page: '1', + unread: true, + isDownloaded: false, +}; + +const baseNovel = { + id: 7, + name: 'Test Novel', + path: '/novel/test', + pluginId: 'plugin.test', + cover: null, + inLibrary: false, + isLocal: false, + totalPages: 2, +}; + +const createStore = (overrides: Record = {}) => { + const state = { + chapters: [baseChapter], + deleteChapter: jest.fn(), + fetching: false, + firstUnreadChapter: { ...baseChapter, id: 2 }, + loading: false, + novelSettings: { filter: [], showChapterTitles: false }, + pages: ['1', '2'], + setNovel: jest.fn(), + novel: baseNovel, + batchInformation: { batch: 0, total: 1, totalChapters: 2 }, + pageIndex: 0, + getChapters: jest.fn(), + openPage: jest.fn(), + updateChapter: jest.fn(), + refreshNovel: jest.fn(), + lastRead: undefined, + ...overrides, + }; + + return { + getState: () => state, + subscribe: jest.fn(() => () => {}), + state, + }; +}; + +const wireStoreSelectors = (store: ReturnType) => { + mockUseNovelValue.mockImplementation( + (key: keyof typeof store.state) => store.state[key], + ); + mockUseNovelActions.mockReturnValue({ + deleteChapter: store.state.deleteChapter, + setNovel: store.state.setNovel, + getChapters: store.state.getChapters, + openPage: store.state.openPage, + updateChapter: store.state.updateChapter, + refreshNovel: store.state.refreshNovel, + }); +}; + +const navigation = { navigate: jest.fn() }; +const listRef = { current: { scrollToOffset: jest.fn() } }; +const headerOpacity = { set: jest.fn() }; +const onRefresh = jest.fn(); + +const renderList = () => + render( + , + ); + +describe('NovelScreenList (task 12 context boundary cutover)', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockDownloadingChapterIds = new Set(); + mockDownloadingNovelIds = new Set(); + mockDownloadQueue = []; + }); + + it('uses novelStore selector actions', () => { + const store = createStore(); + wireStoreSelectors(store); + + renderList(); + + fireEvent.press(screen.getByTestId('delete-chapter-1')); + + expect(store.state.deleteChapter).toHaveBeenCalledTimes(1); + }); + + it('reconciles once after the novel download queue settles', () => { + const store = createStore(); + + mockDownloadingChapterIds = new Set([1, 2]); + mockDownloadingNovelIds = new Set([baseNovel.id]); + wireStoreSelectors(store); + + const view = renderList(); + + mockDownloadingChapterIds = new Set([2]); + view.rerender( + , + ); + + expect(store.state.getChapters).not.toHaveBeenCalled(); + + mockDownloadingChapterIds = new Set(); + mockDownloadingNovelIds = new Set(); + view.rerender( + , + ); + + expect(store.state.getChapters).toHaveBeenCalledTimes(1); + expect(store.state.updateChapter).not.toHaveBeenCalled(); + }); + + it('reconciles chapter state as a novel download makes progress', () => { + const store = createStore(); + mockDownloadingNovelIds = new Set([baseNovel.id]); + mockDownloadQueue = [ + { + id: 'download-1', + task: { + name: 'DOWNLOAD_CHAPTER', + data: { novelId: baseNovel.id, chapters: [] }, + }, + state: 'running', + meta: { progress: 0, isRunning: true }, + }, + ]; + wireStoreSelectors(store); + + const view = renderList(); + expect(store.state.getChapters).not.toHaveBeenCalled(); + + mockDownloadQueue = [ + { + ...mockDownloadQueue[0], + meta: { progress: 0.5, isRunning: true }, + }, + ]; + view.rerender( + , + ); + + expect(store.state.getChapters).toHaveBeenCalledTimes(1); + }); + + it('uses selector-backed page navigation action from novelStore', () => { + const store = createStore(); + wireStoreSelectors(store); + + renderList(); + + fireEvent.press(screen.getByTestId('pagination-change-page')); + + expect(store.state.openPage).toHaveBeenCalledWith(1); + }); + + it('keeps continue-reading FAB navigation parity with lastRead fallback chain', () => { + const lastRead = { ...baseChapter, id: 42 }; + const store = createStore({ + firstUnreadChapter: { ...baseChapter, id: 99 }, + lastRead, + }); + + wireStoreSelectors(store); + + renderList(); + + fireEvent.press(screen.getByTestId('continue-reading-fab')); + + expect(navigation.navigate).toHaveBeenCalledWith('ReaderStack', { + screen: 'Chapter', + params: { novel: baseNovel, chapter: lastRead }, + }); + }); +}); diff --git a/src/screens/novel/hooks/__tests__/useNovelRefresh.test.ts b/src/screens/novel/hooks/__tests__/useNovelRefresh.test.ts new file mode 100644 index 000000000..5d1a4cf8b --- /dev/null +++ b/src/screens/novel/hooks/__tests__/useNovelRefresh.test.ts @@ -0,0 +1,92 @@ +import { act, renderHook } from '@testing-library/react-native'; + +import { useNovelRefresh } from '../useNovelRefresh'; +import { updateNovel } from '@services/updates/LibraryUpdateQueries'; +import { useLibraryContext } from '@components/Context/LibraryContext'; +import { NovelInfo } from '@database/types'; + +jest.mock('@services/updates/LibraryUpdateQueries', () => ({ + updateNovel: jest.fn(), +})); + +jest.mock('@components/Context/LibraryContext', () => ({ + useLibraryContext: jest.fn(), +})); + +jest.mock('@i18n/translations', () => ({ + getString: (key: string) => key, +})); + +jest.mock('@utils/showToast', () => ({ + showToast: jest.fn(), +})); + +const mockUpdateNovel = updateNovel as jest.MockedFunction; +const mockUseLibraryContext = useLibraryContext as jest.MockedFunction< + typeof useLibraryContext +>; + +const novel = { + id: 1, + name: 'Novel', + path: '/novel', + pluginId: 'plugin', + inLibrary: true, +} as NovelInfo; + +describe('useNovelRefresh', () => { + const reloadNovel = jest.fn().mockResolvedValue(undefined); + const refetchLibrary = jest.fn().mockResolvedValue(undefined); + const enqueue = jest.fn(); + + beforeEach(() => { + reloadNovel.mockClear(); + refetchLibrary.mockClear(); + enqueue.mockClear(); + mockUpdateNovel.mockClear(); + mockUpdateNovel.mockResolvedValue(undefined); + mockUseLibraryContext.mockReturnValue({ + refetchLibrary, + } as unknown as ReturnType); + }); + + it('refreshes the novel and library after updating a library novel', async () => { + const { result } = renderHook(() => + useNovelRefresh({ + novel, + downloadNewChapters: false, + refreshNovelMetadata: false, + enqueue, + reloadNovel, + }), + ); + + await act(result.current.refresh); + + expect(mockUpdateNovel).toHaveBeenCalledWith( + novel.pluginId, + novel.path, + novel.id, + expect.objectContaining({ enqueue }), + ); + expect(reloadNovel).toHaveBeenCalledTimes(1); + expect(refetchLibrary).toHaveBeenCalledTimes(1); + }); + + it('does not refresh the library for a novel outside the library', async () => { + const { result } = renderHook(() => + useNovelRefresh({ + novel: { ...novel, inLibrary: false }, + downloadNewChapters: false, + refreshNovelMetadata: false, + enqueue, + reloadNovel, + }), + ); + + await act(result.current.refresh); + + expect(reloadNovel).toHaveBeenCalledTimes(1); + expect(refetchLibrary).not.toHaveBeenCalled(); + }); +}); diff --git a/src/screens/novel/hooks/useChapterSelection.ts b/src/screens/novel/hooks/useChapterSelection.ts new file mode 100644 index 000000000..736a176bf --- /dev/null +++ b/src/screens/novel/hooks/useChapterSelection.ts @@ -0,0 +1,24 @@ +import { useCallback, useMemo, useState } from 'react'; +import { ChapterInfo } from '@database/types'; + +export const useChapterSelection = (chapters: ChapterInfo[]) => { + const [selectedIds, setSelectedIds] = useState([]); + const selectedIdSet = useMemo(() => new Set(selectedIds), [selectedIds]); + const selectedChapters = useMemo( + () => chapters.filter(chapter => selectedIdSet.has(chapter.id)), + [chapters, selectedIdSet], + ); + const clearSelection = useCallback(() => setSelectedIds([]), []); + const selectAll = useCallback( + () => setSelectedIds(chapters.map(chapter => chapter.id)), + [chapters], + ); + + return { + selectedIds, + selectedChapters, + setSelectedIds, + clearSelection, + selectAll, + }; +}; diff --git a/src/screens/novel/hooks/useCustomNovelCover.ts b/src/screens/novel/hooks/useCustomNovelCover.ts new file mode 100644 index 000000000..a1da9c8aa --- /dev/null +++ b/src/screens/novel/hooks/useCustomNovelCover.ts @@ -0,0 +1,18 @@ +import { useCallback } from 'react'; +import { NovelInfo } from '@database/types'; +import { pickCustomNovelCover } from '@database/queries/NovelQueries'; + +export const useCustomNovelCover = ( + novel: NovelInfo | undefined, + setNovel: (novel: NovelInfo | undefined) => void, +) => + useCallback(async () => { + if (!novel) { + return; + } + + const cover = await pickCustomNovelCover(novel); + if (cover) { + setNovel({ ...novel, cover }); + } + }, [novel, setNovel]); diff --git a/src/screens/novel/hooks/useDownloadReconciliation.ts b/src/screens/novel/hooks/useDownloadReconciliation.ts new file mode 100644 index 000000000..816e2aa39 --- /dev/null +++ b/src/screens/novel/hooks/useDownloadReconciliation.ts @@ -0,0 +1,30 @@ +import { useEffect, useRef } from 'react'; + +export const useDownloadReconciliation = ( + isNovelDownloading: boolean, + downloadProgressKey: string, + reloadChapters: () => Promise, +) => { + const hadNovelDownloadsRef = useRef(false); + const previousProgressKeyRef = useRef(''); + + useEffect(() => { + if (isNovelDownloading) { + if ( + hadNovelDownloadsRef.current && + previousProgressKeyRef.current !== downloadProgressKey + ) { + void reloadChapters(); + } + hadNovelDownloadsRef.current = true; + previousProgressKeyRef.current = downloadProgressKey; + return; + } + + if (hadNovelDownloadsRef.current) { + hadNovelDownloadsRef.current = false; + previousProgressKeyRef.current = ''; + void reloadChapters(); + } + }, [downloadProgressKey, isNovelDownloading, reloadChapters]); +}; diff --git a/src/screens/novel/hooks/useNovelRefresh.ts b/src/screens/novel/hooks/useNovelRefresh.ts new file mode 100644 index 000000000..6378f0f9f --- /dev/null +++ b/src/screens/novel/hooks/useNovelRefresh.ts @@ -0,0 +1,64 @@ +import { useCallback, useState } from 'react'; +import { NovelInfo } from '@database/types'; +import { BackgroundTaskEnqueuer } from '@services/backgroundTasks'; +import { updateNovel } from '@services/updates/LibraryUpdateQueries'; +import { getString } from '@i18n/translations'; +import { showToast } from '@utils/showToast'; +import { useLibraryContext } from '@components/Context/LibraryContext'; + +interface UseNovelRefreshOptions { + novel: NovelInfo | undefined; + downloadNewChapters: boolean; + refreshNovelMetadata: boolean; + enqueue: BackgroundTaskEnqueuer; + reloadNovel: () => Promise; +} + +export const useNovelRefresh = ({ + novel, + downloadNewChapters, + refreshNovelMetadata, + enqueue, + reloadNovel, +}: UseNovelRefreshOptions) => { + const [updating, setUpdating] = useState(false); + const { refetchLibrary } = useLibraryContext(); + + const refresh = useCallback(async () => { + if (!novel || updating) { + return; + } + + setUpdating(true); + try { + await updateNovel(novel.pluginId, novel.path, novel.id, { + downloadNewChapters, + refreshNovelMetadata, + enqueue, + }); + await Promise.all([ + reloadNovel(), + novel.inLibrary ? refetchLibrary() : Promise.resolve(), + ]); + showToast(getString('novelScreen.updatedToast', { name: novel.name })); + } catch (error) { + showToast( + `Failed updating: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + } finally { + setUpdating(false); + } + }, [ + downloadNewChapters, + enqueue, + novel, + refetchLibrary, + refreshNovelMetadata, + reloadNovel, + updating, + ]); + + return { updating, refresh }; +}; diff --git a/src/screens/novel/hooks/useNovelScreenActions.ts b/src/screens/novel/hooks/useNovelScreenActions.ts new file mode 100644 index 000000000..0492857b3 --- /dev/null +++ b/src/screens/novel/hooks/useNovelScreenActions.ts @@ -0,0 +1,174 @@ +import { useCallback, useMemo } from 'react'; +import { Share } from 'react-native'; +import { isNumber } from 'lodash-es'; + +import { + getAllUndownloadedAndUnreadChapters, + getAllUndownloadedChapters, +} from '@database/queries/ChapterQueries'; +import { ChapterInfo, NovelInfo } from '@database/types'; +import { useDownload } from '@hooks/persisted'; +import { resolveUrl } from '@services/plugin/fetch'; +import { MaterialDesignIconName } from '@type/icon'; + +import { useNovelActions } from '../NovelContext'; + +type SelectionAction = { + icon: MaterialDesignIconName; + onPress: () => void; +}; + +interface UseNovelScreenActionsOptions { + chapters: ChapterInfo[]; + clearSelection: () => void; + novel?: NovelInfo; + selectedChapters: ChapterInfo[]; +} + +export const useNovelScreenActions = ({ + chapters, + clearSelection, + novel, + selectedChapters, +}: UseNovelScreenActionsOptions) => { + const { + bookmarkChapters, + deleteChapters, + markChaptersRead, + markChaptersUnreadAndResetProgress, + markPreviouschaptersRead, + markPreviousChaptersUnread, + } = useNovelActions(); + const { downloadChapters } = useDownload(); + + const downloadAvailableChapters = useCallback( + async (amount: number | 'all' | 'unread') => { + if (!novel) { + return; + } + + let availableChapters = chapters; + if (amount === 'all') { + availableChapters = await getAllUndownloadedChapters(novel.id); + } else if (amount === 'unread') { + availableChapters = await getAllUndownloadedAndUnreadChapters(novel.id); + } else if (isNumber(amount)) { + availableChapters = availableChapters + .filter(chapter => !chapter.isDownloaded) + .slice(0, amount); + } + + if (availableChapters.length > 0) { + downloadChapters(novel, availableChapters); + } + }, + [chapters, downloadChapters, novel], + ); + + const deleteDownloadedChapters = useCallback(() => { + deleteChapters(chapters.filter(chapter => chapter.isDownloaded)); + }, [chapters, deleteChapters]); + + const shareNovel = useCallback(() => { + if (novel) { + void Share.share({ + message: resolveUrl(novel.pluginId, novel.path, true), + }); + } + }, [novel]); + + const selectionActions = useMemo(() => { + const actions: SelectionAction[] = []; + const finish = (action: () => void) => () => { + action(); + clearSelection(); + }; + + if ( + !novel?.isLocal && + selectedChapters.some(chapter => !chapter.isDownloaded) + ) { + actions.push({ + icon: 'download-outline', + onPress: finish(() => { + if (novel) { + downloadChapters( + novel, + selectedChapters.filter(chapter => !chapter.isDownloaded), + ); + } + }), + }); + } + + if ( + !novel?.isLocal && + selectedChapters.some(chapter => chapter.isDownloaded) + ) { + actions.push({ + icon: 'trash-can-outline', + onPress: finish(() => + deleteChapters( + selectedChapters.filter(chapter => chapter.isDownloaded), + ), + ), + }); + } + + actions.push({ + icon: 'bookmark-outline', + onPress: finish(() => bookmarkChapters(selectedChapters)), + }); + + if (selectedChapters.some(chapter => chapter.unread)) { + actions.push({ + icon: 'check', + onPress: finish(() => markChaptersRead(selectedChapters)), + }); + } + + if (selectedChapters.some(chapter => !chapter.unread)) { + actions.push({ + icon: 'check-outline', + onPress: finish(() => { + void markChaptersUnreadAndResetProgress(selectedChapters); + }), + }); + } + + if (selectedChapters.length === 1) { + const selectedChapter = selectedChapters[0]; + actions.push({ + icon: selectedChapter.unread ? 'playlist-check' : 'playlist-remove', + onPress: finish(() => { + if (selectedChapter.unread) { + markPreviouschaptersRead(selectedChapter.id); + } else { + markPreviousChaptersUnread(selectedChapter.id); + } + }), + }); + } + + return actions; + }, [ + bookmarkChapters, + clearSelection, + deleteChapters, + downloadChapters, + markChaptersRead, + markChaptersUnreadAndResetProgress, + markPreviousChaptersUnread, + markPreviouschaptersRead, + novel, + selectedChapters, + ]); + + return { + deleteDownloadedChapters, + downloadAvailableChapters, + downloadChapters, + selectionActions, + shareNovel, + }; +}; diff --git a/src/screens/novel/hooks/useSaveNovelCover.ts b/src/screens/novel/hooks/useSaveNovelCover.ts new file mode 100644 index 000000000..04e574f13 --- /dev/null +++ b/src/screens/novel/hooks/useSaveNovelCover.ts @@ -0,0 +1,59 @@ +import { useCallback } from 'react'; +import { StorageAccessFramework } from 'expo-file-system/legacy'; + +import { NovelInfo } from '@database/types'; +import { getString } from '@i18n/translations'; +import NativeFile from '@modules/native-file'; +import { downloadFile } from '@plugins/helpers/fetch'; +import { showToast } from '@utils/showToast'; + +export const useSaveNovelCover = (novel: NovelInfo | undefined) => + useCallback(async () => { + if (!novel?.cover) { + showToast( + getString( + novel ? 'novelScreen.noCoverFound' : 'novelScreen.coverNotSaved', + ), + ); + return; + } + + const permissions = + await StorageAccessFramework.requestDirectoryPermissionsAsync(); + if (!permissions.granted) { + showToast(getString('novelScreen.coverNotSaved')); + return; + } + + const cover = novel.cover; + let tempCoverUri: string | null = null; + try { + const rawExtension = cover.split('.').pop()?.split('?')[0] || 'png'; + const extension = ['jpg', 'jpeg', 'png', 'webp'].includes(rawExtension) + ? rawExtension + : 'png'; + const fileName = `${novel.name.replace(/[^a-zA-Z0-9]/g, '_')}_${ + novel.id + }.${extension}`; + const destination = await StorageAccessFramework.createFileAsync( + permissions.directoryUri, + fileName, + `image/${extension}`, + ); + + if (cover.startsWith('http')) { + tempCoverUri = `${NativeFile.ExternalCachesDirectoryPath}/${fileName}`; + await downloadFile(cover, tempCoverUri); + await NativeFile.copyFile(tempCoverUri, destination); + } else { + await NativeFile.copyFile(cover, destination); + } + showToast(getString('novelScreen.coverSaved')); + } catch (error) { + showToast(error instanceof Error ? error.message : String(error)); + } finally { + if (tempCoverUri) { + await NativeFile.unlink(tempCoverUri).catch(() => undefined); + } + } + }, [novel]); diff --git a/src/screens/novel/utils/__tests__/genres.test.ts b/src/screens/novel/utils/__tests__/genres.test.ts new file mode 100644 index 000000000..7d47b3694 --- /dev/null +++ b/src/screens/novel/utils/__tests__/genres.test.ts @@ -0,0 +1,18 @@ +import { parseGenres } from '../genres'; + +describe('parseGenres', () => { + it('normalizes comma-separated genres', () => { + expect(parseGenres('Fantasy, Adventure, , Romance ')).toEqual([ + 'Fantasy', + 'Adventure', + 'Romance', + ]); + }); + + it.each([undefined, null, ['Fantasy'], { genre: 'Fantasy' }])( + 'returns an empty list for malformed input: %p', + genres => { + expect(parseGenres(genres)).toEqual([]); + }, + ); +}); diff --git a/src/screens/novel/utils/genres.ts b/src/screens/novel/utils/genres.ts new file mode 100644 index 000000000..7f727a6b4 --- /dev/null +++ b/src/screens/novel/utils/genres.ts @@ -0,0 +1,7 @@ +export const parseGenres = (genres: unknown): string[] => + typeof genres === 'string' + ? genres + .split(',') + .map(genre => genre.trim()) + .filter(Boolean) + : []; diff --git a/src/screens/onboarding/OnboardingScreen.tsx b/src/screens/onboarding/OnboardingScreen.tsx index 3f3978029..28247c048 100644 --- a/src/screens/onboarding/OnboardingScreen.tsx +++ b/src/screens/onboarding/OnboardingScreen.tsx @@ -6,7 +6,7 @@ import { Button } from '@components'; import ThemeSelectionStep from './ThemeSelectionStep'; import { useState } from 'react'; import { MMKVStorage } from '@utils/mmkv/mmkv'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; enum OnboardingStep { PICK_THEME, diff --git a/src/screens/onboarding/ThemeSelectionStep.tsx b/src/screens/onboarding/ThemeSelectionStep.tsx index 36aae08ee..34ef00d17 100644 --- a/src/screens/onboarding/ThemeSelectionStep.tsx +++ b/src/screens/onboarding/ThemeSelectionStep.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import { View, Text, Pressable, StyleSheet, ScrollView } from 'react-native'; +import { View, Text, Pressable, StyleSheet } from 'react-native'; import { useMMKVBoolean, useMMKVNumber, @@ -11,7 +11,14 @@ import { ThemePicker } from '@components/ThemePicker/ThemePicker'; import { ThemeColors } from '@theme/types'; import { useTheme } from '@hooks/persisted'; import { darkThemes, lightThemes } from '@theme/md3'; -import { getString } from '@strings/translations'; +import { + getSystemDynamicTheme, + isDynamicThemeAvailable, + toDynamicThemeColors, +} from '@theme/dynamic'; +import { getString } from '@i18n/translations'; +import { LegendList } from '@legendapp/list/react-native'; +import Switch from '@components/Switch/Switch'; type ThemeMode = 'light' | 'dark' | 'system'; @@ -23,39 +30,23 @@ const AmoledToggle: React.FC = ({ theme }) => { const [isAmoledBlack = false, setAmoledBlack] = useMMKVBoolean('AMOLED_BLACK'); - if (!theme.isDark) { - return null; - } + const toggle = () => setAmoledBlack(!isAmoledBlack); + + if (!theme.isDark) return null; return ( - + {getString('appearanceScreen.pureBlackDarkMode')} - setAmoledBlack(!isAmoledBlack)} - style={[ - styles.toggle, - { - backgroundColor: isAmoledBlack - ? theme.primary - : theme.surfaceVariant, - }, - ]} - > - - - + + ); }; @@ -67,8 +58,16 @@ export default function ThemeSelectionStep() { const currentMode = themeMode as ThemeMode; const availableThemes = useMemo(() => { - return theme.isDark ? darkThemes : lightThemes; - }, [theme.isDark]); + const themes = theme.isDark ? darkThemes : lightThemes; + if (!isDynamicThemeAvailable) { + return themes; + } + + return [ + toDynamicThemeColors(getSystemDynamicTheme(), theme.isDark), + ...themes, + ]; + }, [theme]); const themeModeOptions: SegmentedControlOption[] = useMemo( () => [ @@ -90,20 +89,10 @@ export default function ThemeSelectionStep() { const handleModeChange = (mode: ThemeMode) => { setThemeMode(mode); - - if (mode !== 'system') { - const themes = mode === 'dark' ? darkThemes : lightThemes; - const currentThemeInMode = themes.find(t => t.id === theme.id); - - if (!currentThemeInMode) { - setThemeId(themes[0].id); - } - } }; const handleThemeSelect = (selectedTheme: ThemeColors) => { setThemeId(selectedTheme.id); - setThemeMode(selectedTheme.isDark ? 'dark' : 'light'); }; return ( @@ -117,24 +106,23 @@ export default function ThemeSelectionStep() { theme={theme} /> - {/* Theme List */} - - {availableThemes.map(item => ( - + data={availableThemes} + extraData={theme} + keyExtractor={item => 'theme-' + item.id} + renderItem={({ item }) => ( + handleThemeSelect(item)} /> - ))} - - + )} + /> {/* AMOLED Toggle */} @@ -149,13 +137,6 @@ const styles = StyleSheet.create({ segmentedControlContainer: { marginBottom: 24, }, - themeScrollContent: { - paddingVertical: 16, - paddingHorizontal: 24, - }, - themeItem: { - marginHorizontal: 8, - }, amoledContainer: { flexDirection: 'row', alignItems: 'center', diff --git a/src/screens/reader/ChapterContext.tsx b/src/screens/reader/ChapterContext.tsx index 7f7491360..fc53bf5c8 100644 --- a/src/screens/reader/ChapterContext.tsx +++ b/src/screens/reader/ChapterContext.tsx @@ -3,15 +3,22 @@ import { ChapterInfo, NovelInfo } from '@database/types'; import WebView from 'react-native-webview'; import useChapter from './hooks/useChapter'; -type ChapterContextType = ReturnType & { +type ChapterContextType = ReturnType['chapterContext'] & { novel: NovelInfo; - webViewRef: React.RefObject | null>; + webViewRef: React.RefObject | null>; }; const defaultValue = {} as ChapterContextType; const ChapterContext = createContext(defaultValue); +/** + * Whether the reader chrome is hidden. It lives in its own context because it + * changes on every tap, and a context value change re-renders every consumer - + * only the screen that draws the appbar and footer cares about it. + */ +const ReaderChromeHiddenContext = createContext(true); + export function ChapterContextProvider({ children, novel, @@ -22,20 +29,26 @@ export function ChapterContextProvider({ initialChapter: ChapterInfo; }) { const webViewRef = useRef(null); - const chapterHookContent = useChapter(webViewRef, initialChapter, novel); + const { hidden, chapterContext } = useChapter( + webViewRef, + initialChapter, + novel, + ); const contextValue = useMemo( () => ({ novel, webViewRef, - ...chapterHookContent, + ...chapterContext, }), - [novel, webViewRef, chapterHookContent], + [novel, webViewRef, chapterContext], ); return ( - {children} + + {children} + ); } @@ -43,3 +56,7 @@ export function ChapterContextProvider({ export const useChapterContext = () => { return useContext(ChapterContext); }; + +export const useReaderChromeHidden = () => { + return useContext(ReaderChromeHiddenContext); +}; diff --git a/src/screens/reader/ChapterLoadingScreen/ChapterLoadingScreen.tsx b/src/screens/reader/ChapterLoadingScreen/ChapterLoadingScreen.tsx index c83d8102d..0863366ea 100644 --- a/src/screens/reader/ChapterLoadingScreen/ChapterLoadingScreen.tsx +++ b/src/screens/reader/ChapterLoadingScreen/ChapterLoadingScreen.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { View } from 'react-native'; +import { useMemo } from 'react'; +import { StyleSheet, View } from 'react-native'; import color from 'color'; import SkeletonLines from '../components/SkeletonLines'; @@ -12,27 +12,34 @@ const ChapterLoadingScreen = () => { textSize, lineHeight, } = useChapterReaderSettings(); + const [skeletonColor, highlightColor] = useMemo(() => { + const background = color(backgroundColor); + if (!background.isDark()) { + return [ + background.darken(0.04).toString(), + background.darken(0.08).toString(), + ]; + } + if (background.luminosity() !== 0) { + return [ + background.lighten(0.1).toString(), + background.lighten(0.4).toString(), + ]; + } + return [ + background.negate().darken(0.98).toString(), + background.negate().darken(0.92).toString(), + ]; + }, [backgroundColor]); return ( - + @@ -40,4 +47,10 @@ const ChapterLoadingScreen = () => { ); }; +const styles = StyleSheet.create({ + container: { + flex: 1, + }, +}); + export default ChapterLoadingScreen; diff --git a/src/screens/reader/ReaderScreen.tsx b/src/screens/reader/ReaderScreen.tsx index 30de4f444..c381331f9 100644 --- a/src/screens/reader/ReaderScreen.tsx +++ b/src/screens/reader/ReaderScreen.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useCallback, useState, useEffect } from 'react'; +import { useRef, useCallback, useState, useEffect } from 'react'; import { useChapterGeneralSettings, useTheme } from '@hooks/persisted'; import ReaderAppbar from './components/ReaderAppbar'; @@ -10,17 +10,30 @@ import ChapterDrawer from './components/ChapterDrawer'; import ChapterLoadingScreen from './ChapterLoadingScreen/ChapterLoadingScreen'; import { ErrorScreenV2 } from '@components'; import { ChapterScreenProps } from '@navigators/types'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import KeepScreenAwake from './components/KeepScreenAwake'; -import { ChapterContextProvider, useChapterContext } from './ChapterContext'; +import { + ChapterContextProvider, + useChapterContext, + useReaderChromeHidden, +} from './ChapterContext'; import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types'; import { useBackHandler } from '@hooks/index'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { StyleSheet, View } from 'react-native'; +import { Keyboard, Share, StyleSheet, View } from 'react-native'; import { Drawer } from 'react-native-drawer-layout'; +import { EMPTY_READER_SEARCH_RESULT, ReaderSearchResult } from './types'; +import * as Linking from 'expo-linking'; +import { resolveUrl } from '@services/plugin/fetch'; const Chapter = ({ route, navigation }: ChapterScreenProps) => { const [open, setOpen] = useState(false); + /** + * The drawer renders a list of every chapter in the novel and is mounted + * off-screen by `Drawer`. Mounting it up front competes with the chapter load + * for the JS thread, so it is only created once the drawer is actually used. + */ + const [drawerMounted, setDrawerMounted] = useState(false); useBackHandler(() => { if (open) { @@ -31,19 +44,28 @@ const Chapter = ({ route, navigation }: ChapterScreenProps) => { }); const openDrawer = useCallback(() => { + setDrawerMounted(true); setOpen(true); }, []); + const closeDrawer = useCallback(() => setOpen(false), []); + + const renderDrawerContent = useCallback( + () => (drawerMounted ? : null), + [closeDrawer, drawerMounted], + ); + return ( setOpen(true)} - onClose={() => setOpen(false)} - renderDrawerContent={() => } + onOpen={openDrawer} + onClose={closeDrawer} + renderDrawerContent={renderDrawerContent} > { const { left, right } = useSafeAreaInsets(); - const { novel, chapter } = useChapterContext(); + const { + novel, + chapter, + onUserInteraction, + loading, + error, + webViewRef, + hideHeader, + refetch, + } = useChapterContext(); + const hidden = useReaderChromeHidden(); const readerSheetRef = useRef(null); const theme = useTheme(); const { pageReader = false, keepScreenOn } = useChapterGeneralSettings(); - const [bookmarked, setBookmarked] = useState(chapter.bookmark ?? false); + const [bookmarked, setBookmarked] = useState( + chapter.bookmark ?? false, + ); + const [searchVisible, setSearchVisible] = useState(false); + const [searchResult, setSearchResult] = useState( + EMPTY_READER_SEARCH_RESULT, + ); + const [searchText, setSearchTextState] = useState(''); + const searchTextRef = useRef(''); + /** + * The settings sheet mounts a tab view (including the TTS engine/voice + * pickers), which is far too much work to do while the chapter is loading. + */ + const [readerSheetMounted, setReaderSheetMounted] = useState(false); + const pendingSheetPresentRef = useRef(false); + + const setSearchText = useCallback((text: string) => { + searchTextRef.current = text; + setSearchTextState(text); + }, []); + + const resetSearchResult = useCallback(() => { + setSearchResult(EMPTY_READER_SEARCH_RESULT); + }, []); + + const resetSearch = useCallback(() => { + setSearchText(''); + resetSearchResult(); + }, [resetSearchResult, setSearchText]); + + const openReaderSheet = useCallback(() => { + if (readerSheetMounted) { + readerSheetRef.current?.present(); + return; + } + pendingSheetPresentRef.current = true; + setReaderSheetMounted(true); + }, [readerSheetMounted]); useEffect(() => { + if (readerSheetMounted && pendingSheetPresentRef.current) { + pendingSheetPresentRef.current = false; + readerSheetRef.current?.present(); + } + }, [readerSheetMounted]); + + useBackHandler( + useCallback(() => { + if (searchVisible) { + setSearchVisible(false); + return true; + } + + return false; + }, [searchVisible]), + ); + + useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect setBookmarked(chapter.bookmark ?? false); }, [chapter]); - const { hidden, loading, error, webViewRef, hideHeader, refetch } = - useChapterContext(); + useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect + setSearchVisible(false); + resetSearch(); + }, [chapter.id, resetSearch]); - const scrollToStart = () => + useEffect(() => { + if (hidden) { + // eslint-disable-next-line react-hooks/set-state-in-effect + setSearchVisible(false); + } + }, [hidden]); + + useEffect(() => { + if (hidden) { + return; + } + + webViewRef.current?.injectJavaScript(` + if (window.reader?.hidden) { + window.reader.hidden.val = ${searchVisible ? 'true' : 'false'}; + } + true; + `); + }, [hidden, searchVisible, webViewRef]); + + const scrollToStart = useCallback(() => { + onUserInteraction(); requestAnimationFrame(() => { webViewRef?.current?.injectJavaScript( !pageReader @@ -85,17 +197,47 @@ export const ChapterContent = ({ window.scrollTo({top:0,behavior:'smooth'}) })()` : `(()=>{ - document.querySelector('chapter').setAttribute('data-page',0); - document.querySelector("chapter").style.transform = 'translate(0%)'; + window.pageReader?.movePage(0); })()`, ); }); + }, [onUserInteraction, pageReader, webViewRef]); const openDrawerI = useCallback(() => { openDrawer(); hideHeader(); }, [hideHeader, openDrawer]); + const handleReaderTouchStart = useCallback(() => { + if (searchVisible) { + Keyboard.dismiss(); + } + }, [searchVisible]); + + const handleReaderPress = useCallback(() => { + onUserInteraction(); + if (searchVisible) { + setSearchVisible(false); + return; + } + hideHeader(); + }, [hideHeader, onUserInteraction, searchVisible]); + + const chapterUrl = resolveUrl(novel.pluginId, chapter.path); + const openChapterInWebView = useCallback(() => { + navigation.navigate('WebviewScreen', { + name: novel.name, + url: chapter.path, + pluginId: novel.pluginId, + }); + }, [chapter.path, navigation, novel.name, novel.pluginId]); + const openChapterInBrowser = useCallback(() => { + void Linking.openURL(chapterUrl); + }, [chapterUrl]); + const shareChapter = useCallback(() => { + void Share.share({ message: chapterUrl }); + }, [chapterUrl]); + if (error) { return ( + {keepScreenOn ? : null} {loading ? ( ) : ( - + )} - + {readerSheetMounted ? ( + + ) : null} {!hidden ? ( <> - + {!searchVisible ? ( + + ) : null} ) : null} @@ -155,4 +313,5 @@ export default Chapter; const styles = StyleSheet.create({ container: { flex: 1 }, + drawer: { backgroundColor: 'transparent' }, }); diff --git a/src/screens/reader/components/ChapterDrawer/RenderListChapter.tsx b/src/screens/reader/components/ChapterDrawer/RenderListChapter.tsx index 24d10ff6e..319570e58 100644 --- a/src/screens/reader/components/ChapterDrawer/RenderListChapter.tsx +++ b/src/screens/reader/components/ChapterDrawer/RenderListChapter.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { View, Pressable, TextStyle, StyleProp, ViewStyle } from 'react-native'; import { Text } from 'react-native-paper'; -import color from 'color'; import { ChapterInfo } from '@database/types'; import { ThemeColors } from '@theme/types'; @@ -17,35 +16,49 @@ type Props = { styles: Styles; theme: ThemeColors; chapterId: number; - onPress: () => void; + /** Takes the chapter so the caller can pass a stable handler. */ + onPress: (chapter: ChapterInfo) => void; }; -const renderListChapter = ({ +/** + * A component rather than a render function so that rows can bail out of + * re-rendering: the chapter list is re-created whenever reading progress is + * written, which happens continuously while a chapter is open. + */ +const RenderListChapter = ({ item, styles, theme, onPress, chapterId, }: Props) => { + const isCurrentChapter = item.id === chapterId; + return ( onPress(item)} style={styles.chapterCtn} > {item.name} @@ -54,7 +67,13 @@ const renderListChapter = ({ {item.releaseTime} @@ -64,4 +83,5 @@ const renderListChapter = ({ ); }; -export default renderListChapter; + +export default React.memo(RenderListChapter); diff --git a/src/screens/reader/components/ChapterDrawer/__tests__/ChapterDrawer.test.tsx b/src/screens/reader/components/ChapterDrawer/__tests__/ChapterDrawer.test.tsx new file mode 100644 index 000000000..4c837f1dc --- /dev/null +++ b/src/screens/reader/components/ChapterDrawer/__tests__/ChapterDrawer.test.tsx @@ -0,0 +1,160 @@ +import { + render, + screen, + fireEvent, + waitFor, +} from '@testing-library/react-native'; +import ChapterDrawer from '..'; + +const mockUseNovelValue = jest.fn(); +const mockUseNovelActions = jest.fn(); +const mockUseChapterContext = jest.fn(); + +jest.mock('@screens/novel/NovelContext', () => ({ + useNovelValue: (key: string) => mockUseNovelValue(key), + useNovelActions: () => mockUseNovelActions(), +})); + +jest.mock('@screens/reader/ChapterContext', () => ({ + useChapterContext: () => mockUseChapterContext(), +})); + +jest.mock('@hooks/persisted', () => ({ + useTheme: () => ({ + surface: '#111', + outline: '#222', + onSurface: '#333', + onSurfaceVariant: '#444', + }), + useAppSettings: () => ({ + defaultChapterSort: 'positionAsc', + }), +})); + +jest.mock('react-native-safe-area-context', () => ({ + useSafeAreaInsets: () => ({ bottom: 0 }), +})); + +jest.mock('@i18n/translations', () => ({ + getString: (key: string) => key, +})); + +jest.mock('@components/index', () => { + const React = require('react'); + const { Pressable, Text, View } = require('react-native'); + + return { + Button: ({ title, onPress }: any) => + React.createElement( + Pressable, + { testID: `btn-${title}`, onPress }, + React.createElement(Text, null, title), + ), + LoadingScreenV2: () => React.createElement(View, { testID: 'loading' }), + }; +}); + +jest.mock('../RenderListChapter', () => { + const React = require('react'); + const { Pressable, Text } = require('react-native'); + + return ({ item, onPress }: any) => + React.createElement( + Pressable, + { testID: `chapter-${item.id}`, onPress }, + React.createElement(Text, null, item.name), + ); +}); + +jest.mock('@legendapp/list/react-native', () => { + const React = require('react'); + const { Pressable, Text, View } = require('react-native'); + + return { + LegendList: ({ data = [], renderItem, onEndReached }: any) => + React.createElement( + View, + null, + ...data.map((item: any, index: number) => + React.createElement( + React.Fragment, + { key: item.id ?? index }, + renderItem({ item, index }), + ), + ), + React.createElement( + Pressable, + { testID: 'legend-end-reached', onPress: () => onEndReached?.() }, + React.createElement(Text, null, 'end'), + ), + ), + }; +}); + +const makeChapter = (id: number, page = '1') => ({ + id, + novelId: 7, + name: `Chapter ${id}`, + path: `/chapter/${id}`, + page, + position: id, + unread: true, + isDownloaded: false, + bookmark: false, + progress: 0, + releaseTime: '2026-01-01', + updatedTime: '2026-01-01', + readTime: '2026-01-01', +}); + +const createStore = (overrides: Record = {}) => { + const state = { + chapters: [makeChapter(1, '1'), makeChapter(2, '2')], + novelSettings: { sort: 'positionAsc', filter: [] }, + pages: ['1', '2'], + fetching: false, + batchInformation: { batch: 0, total: 1, totalChapters: 2 }, + getNextChapterBatch: jest.fn(), + setPageIndex: jest.fn(), + openPage: jest.fn(), + ...overrides, + }; + + return { + getState: () => state, + subscribe: jest.fn(() => () => {}), + state, + }; +}; + +describe('ChapterDrawer (task 12 context boundary cutover)', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('uses novelStore selector-backed page index and pagination batch actions', async () => { + const store = createStore(); + mockUseChapterContext.mockReturnValue({ + chapter: makeChapter(10, '2'), + getChapter: jest.fn(), + setLoading: jest.fn(), + }); + mockUseNovelValue.mockImplementation( + (key: keyof typeof store.state) => store.state[key], + ); + mockUseNovelActions.mockReturnValue({ + getNextChapterBatch: store.state.getNextChapterBatch, + openPage: store.state.openPage, + }); + + render(); + + await waitFor(() => { + expect(store.state.openPage).toHaveBeenCalledWith(1); + }); + + fireEvent.press(screen.getByTestId('legend-end-reached')); + + expect(store.state.getNextChapterBatch).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/screens/reader/components/ChapterDrawer/index.tsx b/src/screens/reader/components/ChapterDrawer/index.tsx index 7fd7e48b4..d964954cb 100644 --- a/src/screens/reader/components/ChapterDrawer/index.tsx +++ b/src/screens/reader/components/ChapterDrawer/index.tsx @@ -1,22 +1,22 @@ -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { StyleSheet, View } from 'react-native'; import { Text } from 'react-native-paper'; import { useAppSettings, useTheme } from '@hooks/persisted'; import { Button, LoadingScreenV2 } from '@components/index'; +import IconButtonV2 from '@components/IconButtonV2/IconButtonV2'; import { EdgeInsets, useSafeAreaInsets } from 'react-native-safe-area-context'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { ThemeColors } from '@theme/types'; -import renderListChapter from './RenderListChapter'; +import RenderListChapter from './RenderListChapter'; import { useChapterContext } from '@screens/reader/ChapterContext'; -import { useNovelContext } from '@screens/novel/NovelContext'; -import { LegendList, LegendListRef, ViewToken } from '@legendapp/list'; +import { + LegendList, + LegendListRef, + ViewToken, +} from '@legendapp/list/react-native'; import { noop } from 'lodash-es'; +import { useNovelActions, useNovelValue } from '@screens/novel/NovelContext'; +import { ChapterInfo } from '@database/types'; type ButtonProperties = { text: string; @@ -28,24 +28,33 @@ type ButtonsProperties = { down: ButtonProperties; }; -const ChapterDrawer = () => { +const viewabilityConfig = { + minimumViewTime: 100, + itemVisiblePercentThreshold: 90, +}; + +type ChapterDrawerProps = { + onClose?: () => void; +}; + +const ChapterDrawer = ({ onClose }: ChapterDrawerProps) => { const { chapter, getChapter, setLoading } = useChapterContext(); - const { - chapters, - novelSettings, - pages, - fetching, - batchInformation, - getNextChapterBatch, - setPageIndex, - } = useNovelContext(); + const chapters = useNovelValue('chapters'); + const novelSettings = useNovelValue('novelSettings'); + const pages = useNovelValue('pages'); + const fetching = useNovelValue('fetching'); + const batchInformation = useNovelValue('batchInformation'); + const { getNextChapterBatch, openPage } = useNovelActions(); + const theme = useTheme(); const insets = useSafeAreaInsets(); const { defaultChapterSort } = useAppSettings(); const listRef = useRef(null); - // ChapterInfo is used via the hooks - const styles = createStylesheet(theme, insets); + const styles = useMemo( + () => createStylesheet(theme, insets), + [theme, insets], + ); const { sort = defaultChapterSort } = novelSettings; const listAscending = sort.endsWith('Asc'); @@ -69,61 +78,100 @@ const ChapterDrawer = () => { if (pageIndex === -1) { pageIndex = 0; } - setPageIndex(pageIndex); - }, [chapter, pages, setPageIndex]); + openPage(pageIndex); + // Only the page matters here; depending on the whole chapter object would + // re-run this on every progress update. + }, [chapter.page, pages, openPage]); - const calculateScrollToIndex = useCallback(() => { + const currentChapterIndex = useMemo(() => { if (chapters.length < 1) { return; } - const indexOfCurrentChapter = - chapters.findIndex(el => { - return el.id === chapter.id; - }) || 0; + const index = chapters.findIndex(el => el.id === chapter.id); + return index >= 0 ? index : 0; + }, [chapter.id, chapters]); - return indexOfCurrentChapter >= 2 ? indexOfCurrentChapter - 2 : 0; - }, [chapters, chapter.id]); + const currentScrollIndex = + currentChapterIndex === undefined + ? undefined + : Math.max(0, currentChapterIndex - 2); - const scrollToIndex = useRef(calculateScrollToIndex()); + /** + * Index the list should sit at, or `undefined` while the chapters are still + * loading. Derived during render (rather than read back from the ref) so the + * list actually appears once the chapters arrive. + */ + const scrollToIndex = useRef(currentScrollIndex); const [footerBtnProps, setButtonProperties] = useState(defaultButtonLayout); const checkViewableItems = useCallback( ({ viewableItems }: { viewableItems: ViewToken[] }) => { - const curChapter = getString( - 'readerScreen.drawer.scrollToCurrentChapter', - ); - const newBtnLayout = Object.create(defaultButtonLayout); + if (viewableItems.length === 0 || currentChapterIndex === undefined) { + return; + } + + const newBtnLayout: ButtonsProperties = { + up: { ...defaultButtonLayout.up }, + down: { ...defaultButtonLayout.down }, + }; + const currentChapterVisible = viewableItems + .map(item => item.index) + .includes(currentChapterIndex); - if (viewableItems.length === 0) return; - const visible = viewableItems - .map(v => v.index) - .includes((scrollToIndex.current ?? 0) + 2); + if (!currentChapterVisible && currentScrollIndex !== undefined) { + const firstVisibleIndex = viewableItems[0].index ?? 0; + const currentChapterButton = { + text: getString('readerScreen.drawer.scrollToCurrentChapter'), + index: currentScrollIndex, + }; - if (!visible && scrollToIndex.current !== undefined) { if ( listAscending - ? (viewableItems[0].index ?? 0) < scrollToIndex.current + 2 - : (viewableItems[0].index ?? 0) > scrollToIndex.current + 2 + ? firstVisibleIndex < currentChapterIndex + : firstVisibleIndex > currentChapterIndex ) { - newBtnLayout.down = { - text: curChapter, - index: scrollToIndex.current, - }; + newBtnLayout.down = currentChapterButton; } else { - newBtnLayout.up = { - text: curChapter, - index: scrollToIndex.current, - }; + newBtnLayout.up = currentChapterButton; } } setButtonProperties(newBtnLayout); }, - [defaultButtonLayout, listAscending], + [ + currentChapterIndex, + currentScrollIndex, + defaultButtonLayout, + listAscending, + ], + ); + + const openChapter = useCallback( + (item: ChapterInfo) => { + setLoading(true); + getChapter(item); + }, + [getChapter, setLoading], ); + + // Every prop here is stable for a given chapter, so unchanged rows can skip + // re-rendering when the chapter list is rebuilt. + const renderItem = useCallback( + ({ item }: { item: ChapterInfo }) => ( + + ), + [chapter.id, openChapter, styles, theme], + ); + const scroll = useCallback((index?: number) => { if (index !== undefined) { listRef.current?.scrollToIndex({ @@ -138,51 +186,48 @@ const ChapterDrawer = () => { }, []); useEffect(() => { - const next = calculateScrollToIndex(); - if (next !== undefined) { + if (currentScrollIndex !== undefined) { if ( scrollToIndex.current === undefined || - next !== scrollToIndex.current + currentScrollIndex !== scrollToIndex.current ) { - scroll(next); + scroll(currentScrollIndex); } - scrollToIndex.current = next; + scrollToIndex.current = currentScrollIndex; } - }, [chapters, chapter.id, calculateScrollToIndex, scroll]); + }, [currentScrollIndex, scroll]); return ( - {getString('common.chapters')} - {scrollToIndex === undefined ? ( + + {getString('common.chapters')} + {onClose ? ( + + ) : null} + + {currentScrollIndex === undefined ? ( ) : ( `chapter_${item.id}_${item.position ?? 'no_pos'}` } - renderItem={val => - renderListChapter({ - item: val.item, - styles, - theme, - chapterId: chapter.id, - onPress: () => { - setLoading(true); - getChapter(val.item); - }, - }) - } - estimatedItemSize={60} - initialScrollIndex={scrollToIndex.current} + renderItem={renderItem} + estimatedItemSize={62} + initialScrollIndex={currentScrollIndex} + contentContainerStyle={styles.listContent} onEndReached={ batchInformation.batch < batchInformation.total && !fetching ? getNextChapterBatch @@ -212,52 +257,62 @@ const ChapterDrawer = () => { const createStylesheet = (theme: ThemeColors, insets: EdgeInsets) => { return StyleSheet.create({ button: { - marginBottom: 12, - marginHorizontal: 16, - marginTop: 4, + marginVertical: 4, }, chapterCtn: { flex: 1, justifyContent: 'center', - paddingHorizontal: 20, + paddingHorizontal: 12, paddingVertical: 10, }, chapterNameCtn: { color: theme.onSurface, - fontSize: 12, + fontSize: 14, + lineHeight: 20, marginBottom: 2, }, drawer: { backgroundColor: theme.surface, flex: 1, - paddingTop: 48, + paddingTop: insets.top, }, drawerElementContainer: { - borderRadius: 50, - margin: 4, - marginHorizontal: 16, + marginVertical: 2, minHeight: 48, overflow: 'hidden', }, footer: { - borderTopColor: theme.outline, - borderTopWidth: 1, - marginTop: 4, - paddingBottom: insets.bottom, + borderTopColor: theme.outlineVariant, + borderTopWidth: StyleSheet.hairlineWidth, + paddingBottom: Math.max(insets.bottom, 8), + paddingHorizontal: 16, paddingTop: 8, }, headerCtn: { - borderBottomColor: theme.outline, + alignItems: 'center', + borderBottomColor: theme.outlineVariant, borderBottomWidth: 1, + flexDirection: 'row', + minHeight: 64, + paddingLeft: 16, + paddingRight: 4, + paddingVertical: 8, + }, + headerTitle: { color: theme.onSurface, - fontSize: 16, - fontWeight: '500', - marginBottom: 4, - padding: 16, + flex: 1, + fontSize: 20, + fontWeight: '600', + lineHeight: 28, + }, + listContent: { + paddingBottom: 8, + paddingTop: 12, }, releaseDateCtn: { color: theme.onSurfaceVariant, - fontSize: 10, + fontSize: 12, + lineHeight: 16, }, }); }; diff --git a/src/screens/reader/components/ReaderAppbar.tsx b/src/screens/reader/components/ReaderAppbar.tsx index 86a8ef279..79f886380 100644 --- a/src/screens/reader/components/ReaderAppbar.tsx +++ b/src/screens/reader/components/ReaderAppbar.tsx @@ -1,9 +1,9 @@ -import React from 'react'; +import React, { useCallback, useState } from 'react'; import { StyleSheet, View } from 'react-native'; import color from 'color'; import { Text } from 'react-native-paper'; -import { IconButtonV2 } from '../../../components'; +import { IconButtonV2, Menu } from '../../../components'; import Animated, { Easing, ReduceMotion, @@ -12,13 +12,26 @@ import Animated, { import { ThemeColors } from '@theme/types'; import { bookmarkChapter } from '@database/queries/ChapterQueries'; import { useChapterContext } from '../ChapterContext'; -import { useNovelContext } from '@screens/novel/NovelContext'; +import { useNovelLayout } from '@screens/novel/NovelContext'; +import ReaderSearchbar from './ReaderSearchbar'; +import { ReaderSearchResult } from '../types'; +import { getString } from '@i18n/translations'; interface ReaderAppbarProps { theme: ThemeColors; goBack: () => void; bookmarked: boolean; setBookmarked: React.Dispatch>; + searchVisible: boolean; + setSearchVisible: React.Dispatch>; + searchText: string; + setSearchText: (text: string) => void; + searchResult: ReaderSearchResult; + resetSearchResult: () => void; + resetSearch: () => void; + openInWebView: () => void; + openInBrowser: () => void; + shareChapter: () => void; } const fastOutSlowIn = Easing.bezier(0.4, 0.0, 0.2, 1.0); @@ -28,9 +41,25 @@ const ReaderAppbar = ({ theme, bookmarked, setBookmarked, + searchVisible, + setSearchVisible, + searchText, + setSearchText, + searchResult, + resetSearchResult, + resetSearch, + openInWebView, + openInBrowser, + shareChapter, }: ReaderAppbarProps) => { const { chapter, novel } = useChapterContext(); - const { statusBarHeight } = useNovelContext(); + const { statusBarHeight } = useNovelLayout(); + const [menuVisible, setMenuVisible] = useState(false); + + const runMenuAction = useCallback((action: () => void) => { + setMenuVisible(false); + action(); + }, []); const entering = () => { 'worklet'; @@ -105,17 +134,65 @@ const ReaderAppbar = ({ {chapter.name} + setSearchVisible(current => !current)} + color={searchVisible ? theme.primary : theme.onSurface} + theme={theme} + /> { bookmarkChapter(chapter.id).then(() => setBookmarked(!bookmarked)); }} - color={theme.onSurface} + color={bookmarked ? theme.primary : theme.onSurface} theme={theme} - style={styles.bookmark} /> + {!novel.isLocal ? ( + setMenuVisible(false)} + anchor={ + setMenuVisible(true)} + color={theme.onSurface} + theme={theme} + /> + } + > + runMenuAction(openInWebView)} + /> + runMenuAction(openInBrowser)} + /> + runMenuAction(shareChapter)} + /> + + ) : null} + {searchVisible ? ( + + ) : null} ); }; @@ -124,11 +201,10 @@ export default ReaderAppbar; const styles = StyleSheet.create({ appbar: { - display: 'flex', + alignItems: 'center', flexDirection: 'row', - }, - bookmark: { - marginEnd: 4, + minHeight: 64, + paddingHorizontal: 4, }, container: { flex: 1, @@ -140,11 +216,15 @@ const styles = StyleSheet.create({ }, content: { flex: 1, + justifyContent: 'center', + paddingHorizontal: 8, }, subtitle: { fontSize: 16, + lineHeight: 20, }, title: { fontSize: 20, + lineHeight: 24, }, }); diff --git a/src/screens/reader/components/ReaderBottomSheet/ReaderBottomSheet.tsx b/src/screens/reader/components/ReaderBottomSheet/ReaderBottomSheet.tsx index 1c3d6eac3..eda948754 100644 --- a/src/screens/reader/components/ReaderBottomSheet/ReaderBottomSheet.tsx +++ b/src/screens/reader/components/ReaderBottomSheet/ReaderBottomSheet.tsx @@ -15,11 +15,12 @@ import React, { } from 'react'; import Color from 'color'; -import { BottomSheetFlashList, BottomSheetView } from '@gorhom/bottom-sheet'; +import { BottomSheetScrollView, BottomSheetView } from '@gorhom/bottom-sheet'; import BottomSheet from '@components/BottomSheet/BottomSheet'; +import { List, TopTabBar } from '@components'; import { useChapterGeneralSettings, useTheme } from '@hooks/persisted'; -import { SceneMap, TabBar, TabView } from 'react-native-tab-view'; -import { getString } from '@strings/translations'; +import { SceneMap, TabView } from 'react-native-tab-view'; +import { getString } from '@i18n/translations'; import ReaderSheetPreferenceItem from './ReaderSheetPreferenceItem'; import TextSizeSlider from './TextSizeSlider'; @@ -28,10 +29,8 @@ import ReaderTextAlignSelector from './ReaderTextAlignSelector'; import ReaderValueChange from './ReaderValueChange'; import ReaderFontPicker from './ReaderFontPicker'; import TTSTab from './TTSTab'; -import { overlay } from 'react-native-paper'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types'; -import { StringMap } from '@strings/types'; +import { StringMap } from '@i18n/types'; type TabViewLabelProps = { route: { @@ -45,29 +44,99 @@ type TabViewLabelProps = { style?: StyleProp; }; -const ReaderTab: React.FC = React.memo(() => ( - }> - - - - - - - - - -)); +const ReaderTab: React.FC = React.memo(() => { + return ( + }> + + + + + + + + + + ); +}); + +interface GeneralPreference { + description?: string; + key: string; + label: string; +} + +const displayPreferences: GeneralPreference[] = [ + { + key: 'fullScreenMode', + label: 'fullscreen', + description: 'fullscreenDescription', + }, + { + key: 'showBatteryAndTime', + label: 'showBatteryAndTime', + description: 'showBatteryAndTimeDescription', + }, + { + key: 'showScrollPercentage', + label: 'showProgressPercentage', + description: 'showProgressPercentageDescription', + }, + { + key: 'verticalSeekbar', + label: 'verticalSeekbar', + description: 'verticalSeekbarDescription', + }, + { + key: 'removeExtraParagraphSpacing', + label: 'removeExtraSpacing', + description: 'removeExtraSpacingDescription', + }, + { + key: 'bionicReading', + label: 'bionicReading', + description: 'bionicReadingDescription', + }, + { key: 'keepScreenOn', label: 'keepScreenOn' }, +]; + +const navigationPreferences: GeneralPreference[] = [ + { + key: 'autoScroll', + label: 'autoscroll', + description: 'autoscrollDescription', + }, + { + key: 'swipeGestures', + label: 'swipeGestures', + description: 'swipeGesturesDescription', + }, + { + key: 'useVolumeButtons', + label: 'volumeButtonsScroll', + description: 'volumeButtonsScrollDescription', + }, + { + key: 'pageReader', + label: 'pageReader', + description: 'pageReaderDescription', + }, + { + key: 'tapToScroll', + label: 'tapToScroll', + description: 'tapToScrollDescription', + }, +]; const GeneralTab: React.FC = React.memo(() => { const theme = useTheme(); @@ -80,28 +149,17 @@ const GeneralTab: React.FC = React.memo(() => { [setChapterGeneralSettings, settings], ); - const preferences = useMemo( - () => [ - { key: 'fullScreenMode', label: 'fullscreen' }, - { key: 'autoScroll', label: 'autoscroll' }, - { key: 'verticalSeekbar', label: 'verticalSeekbar' }, - { key: 'showBatteryAndTime', label: 'showBatteryAndTime' }, - { key: 'showScrollPercentage', label: 'showProgressPercentage' }, - { key: 'swipeGestures', label: 'swipeGestures' }, - { key: 'pageReader', label: 'pageReader' }, - { key: 'removeExtraParagraphSpacing', label: 'removeExtraSpacing' }, - { key: 'useVolumeButtons', label: 'volumeButtonsScroll' }, - { key: 'bionicReading', label: 'bionicReading' }, - { key: 'tapToScroll', label: 'tapToScroll' }, - { key: 'keepScreenOn', label: 'keepScreenOn' }, - ], - [], - ); - - const renderItem = useCallback( - ({ item }: { item: { key: string; label: string } }) => ( + const renderPreference = useCallback( + (item: GeneralPreference) => ( { [settings, theme, toggleSetting], ); + // Two small fixed groups: virtualising them costs more to mount and measure + // than simply drawing them, and this runs while the sheet is opening. return ( - item.key} - renderItem={renderItem} - estimatedItemSize={60} - /> + + + {getString('readerScreen.bottomSheet.display')} + + {displayPreferences.map(renderPreference)} + + {getString('readerScreen.bottomSheet.navigation')} + + {navigationPreferences.map(renderPreference)} + ); }); @@ -134,18 +197,23 @@ const routes = [ { key: 'ttsTab', title: 'TTS' }, ]; +const renderLazyPlaceholder = () => ; + const ReaderBottomSheetV2: React.FC = ({ bottomSheetRef, }) => { const theme = useTheme(); - const { bottom, left, right } = useSafeAreaInsets(); const layout = useWindowDimensions(); - const tabHeaderColor = overlay(2, theme.surface); - const backgroundColor = tabHeaderColor; + const tabHeaderColor = theme.surfaceContainerLow ?? theme.surface; const renderScene = useMemo( - () => SceneMap({ readerTab: ReaderTab, generalTab: GeneralTab, ttsTab: TTSTab }), + () => + SceneMap({ + readerTab: ReaderTab, + generalTab: GeneralTab, + ttsTab: TTSTab, + }), [], ); @@ -153,10 +221,16 @@ const ReaderBottomSheetV2: React.FC = ({ const renderTabBar = useCallback( (props: any) => ( - = ({ }, []); return ( - + = ({ onIndexChange={setIndex} initialLayout={{ width: layout.width }} style={styles.tabView} + // Without this every tab is mounted at once – the TTS tab alone + // enumerates the device's engines and voices over the bridge. + lazy + renderLazyPlaceholder={renderLazyPlaceholder} /> @@ -200,19 +269,16 @@ const ReaderBottomSheetV2: React.FC = ({ export default React.memo(ReaderBottomSheetV2); const styles = StyleSheet.create({ - container: { - borderRadius: 8, - }, readerTab: { - paddingVertical: 8, + gap: 4, + paddingBottom: 20, + paddingTop: 8, }, tabBar: { borderBottomWidth: 0.5, elevation: 0, }, tabView: { - borderTopLeftRadius: 8, - borderTopRightRadius: 8, height: 600, }, flex: { flex: 1 }, diff --git a/src/screens/reader/components/ReaderBottomSheet/ReaderFontPicker.tsx b/src/screens/reader/components/ReaderBottomSheet/ReaderFontPicker.tsx index 8e82ab5ab..cbb34c102 100644 --- a/src/screens/reader/components/ReaderBottomSheet/ReaderFontPicker.tsx +++ b/src/screens/reader/components/ReaderBottomSheet/ReaderFontPicker.tsx @@ -2,7 +2,7 @@ import React, { useCallback } from 'react'; import { StyleSheet, Text, View } from 'react-native'; import { SelectableChip } from '@components/index'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { useChapterReaderSettings, useTheme } from '@hooks/persisted'; import { Font, readerFonts } from '@utils/constants/readerConstants'; @@ -62,7 +62,7 @@ const styles = StyleSheet.create({ flexDirection: 'row', justifyContent: 'center', paddingHorizontal: 16, - paddingVertical: 6, + paddingVertical: 8, }, title: { marginEnd: 16, diff --git a/src/screens/reader/components/ReaderBottomSheet/ReaderSheetPreferenceItem.tsx b/src/screens/reader/components/ReaderBottomSheet/ReaderSheetPreferenceItem.tsx index e4c81a5da..e35194bbc 100644 --- a/src/screens/reader/components/ReaderBottomSheet/ReaderSheetPreferenceItem.tsx +++ b/src/screens/reader/components/ReaderBottomSheet/ReaderSheetPreferenceItem.tsx @@ -4,6 +4,7 @@ import { ThemeColors } from '../../../../theme/types'; import Switch from '@components/Switch/Switch'; interface ReaderSheetPreferenceItemProps { + description?: string; label: string; value: boolean; onPress: () => void; @@ -11,6 +12,7 @@ interface ReaderSheetPreferenceItemProps { } const ReaderSheetPreferenceItem: React.FC = ({ + description, label, value, onPress, @@ -22,9 +24,14 @@ const ReaderSheetPreferenceItem: React.FC = ({ android_ripple={{ color: theme.rippleColor }} onPress={onPress} > - - {label} - + + {label} + {description ? ( + + {description} + + ) : null} + }> @@ -43,6 +50,14 @@ const styles = StyleSheet.create({ paddingVertical: 12, }, label: { + fontSize: 16, + }, + description: { + fontSize: 12, + lineHeight: 18, + marginTop: 2, + }, + textContainer: { flex: 1, paddingRight: 16, }, diff --git a/src/screens/reader/components/ReaderBottomSheet/ReaderTextAlignSelector.tsx b/src/screens/reader/components/ReaderBottomSheet/ReaderTextAlignSelector.tsx index cce2301ed..30eff3c9e 100644 --- a/src/screens/reader/components/ReaderBottomSheet/ReaderTextAlignSelector.tsx +++ b/src/screens/reader/components/ReaderBottomSheet/ReaderTextAlignSelector.tsx @@ -2,9 +2,8 @@ import { StyleSheet, Text, TextStyle, View } from 'react-native'; import React from 'react'; import { useChapterReaderSettings, useTheme } from '@hooks/persisted'; -import { textAlignments } from '@utils/constants/readerConstants'; -import { ToggleButton } from '@components/Common/ToggleButton'; -import { getString } from '@strings/translations'; +import { SegmentedControl, SegmentedControlOption } from '@components'; +import { getString } from '@i18n/translations'; interface ReaderTextAlignSelectorProps { labelStyle?: TextStyle | TextStyle[]; @@ -15,23 +14,42 @@ const ReaderTextAlignSelector: React.FC = ({ }) => { const theme = useTheme(); const { textAlign, setChapterReaderSettings } = useChapterReaderSettings(); + const options: SegmentedControlOption[] = [ + { + icon: 'format-align-left', + label: getString('readerScreen.bottomSheet.alignLeft'), + value: 'left', + }, + { + icon: 'format-align-center', + label: getString('readerScreen.bottomSheet.alignCenter'), + value: 'center', + }, + { + icon: 'format-align-justify', + label: getString('readerScreen.bottomSheet.alignJustify'), + value: 'justify', + }, + { + icon: 'format-align-right', + label: getString('readerScreen.bottomSheet.alignRight'), + value: 'right', + }, + ]; return ( {getString('readerScreen.bottomSheet.textAlign')} - - {textAlignments.map(item => ( - setChapterReaderSettings({ textAlign: item.value })} - /> - ))} - + setChapterReaderSettings({ textAlign: value })} + showCheckIcon={false} + showLabels={false} + theme={theme} + /> ); }; @@ -39,14 +57,9 @@ const ReaderTextAlignSelector: React.FC = ({ export default ReaderTextAlignSelector; const styles = StyleSheet.create({ - buttonContainer: { - flexDirection: 'row', - }, container: { - alignItems: 'center', - flexDirection: 'row', - justifyContent: 'space-between', - marginVertical: 6, + gap: 8, paddingHorizontal: 16, + paddingVertical: 8, }, }); diff --git a/src/screens/reader/components/ReaderBottomSheet/ReaderThemeSelector.tsx b/src/screens/reader/components/ReaderBottomSheet/ReaderThemeSelector.tsx index 15cb1c0fa..14ccef034 100644 --- a/src/screens/reader/components/ReaderBottomSheet/ReaderThemeSelector.tsx +++ b/src/screens/reader/components/ReaderBottomSheet/ReaderThemeSelector.tsx @@ -1,7 +1,7 @@ import { StyleSheet, Text, TextStyle, View } from 'react-native'; import React from 'react'; import { ToggleColorButton } from '@components/Common/ToggleButton'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { presetReaderThemes } from '@utils/constants/readerConstants'; import { useChapterReaderSettings, useTheme } from '@hooks/persisted'; import { FlatList } from 'react-native-gesture-handler'; @@ -43,6 +43,7 @@ const ReaderThemeSelector: React.FC = ({ } backgroundColor={item.backgroundColor} textColor={item.textColor} + theme={theme} onPress={() => setChapterReaderSettings({ theme: item.backgroundColor, @@ -63,17 +64,10 @@ export default ReaderThemeSelector; const styles = StyleSheet.create({ container: { - alignItems: 'center', - flexDirection: 'row', - justifyContent: 'space-between', - marginVertical: 8, paddingHorizontal: 16, - }, - scrollView: { - flexGrow: 1, - justifyContent: 'flex-end', + paddingVertical: 8, }, title: { - marginRight: 16, + marginBottom: 8, }, }); diff --git a/src/screens/reader/components/ReaderBottomSheet/ReaderValueChange.tsx b/src/screens/reader/components/ReaderBottomSheet/ReaderValueChange.tsx index d3b4b9b92..520bd51fa 100644 --- a/src/screens/reader/components/ReaderBottomSheet/ReaderValueChange.tsx +++ b/src/screens/reader/components/ReaderBottomSheet/ReaderValueChange.tsx @@ -2,7 +2,7 @@ import { StyleSheet, Text, TextStyle, View } from 'react-native'; import React from 'react'; import { useChapterReaderSettings, useTheme } from '@hooks/persisted'; -import { IconButtonV2 } from '@components'; +import { Slider } from '@components'; import { ChapterReaderSettings } from '@hooks/persisted/useSettings'; type ValueKey = Exclude< @@ -31,45 +31,34 @@ const ReaderValueChange: React.FC = ({ decimals = 1, min = 1.3, max = 2, - unit = '%', + unit = '×', }) => { const theme = useTheme(); const { setChapterReaderSettings, ...settings } = useChapterReaderSettings(); return ( - - {label} - - - - setChapterReaderSettings({ - [valueKey]: Math.max(min, settings[valueKey] - valueChange), - }) - } - theme={theme} - /> + + + {label} + {`${((settings[valueKey] * 10) / 10).toFixed(decimals)}${unit}`} - = max} - onPress={() => - setChapterReaderSettings({ - [valueKey]: Math.min(max, settings[valueKey] + valueChange), - }) - } - theme={theme} - /> + `${value.toFixed(decimals)}${unit}`} + accessibilityLabel={label} + onSlidingComplete={value => + setChapterReaderSettings({ [valueKey]: value }) + } + /> ); }; @@ -77,20 +66,17 @@ const ReaderValueChange: React.FC = ({ export default ReaderValueChange; const styles = StyleSheet.create({ - buttonContainer: { + labelRow: { alignItems: 'center', flexDirection: 'row', + justifyContent: 'space-between', }, container: { - alignItems: 'center', - flexDirection: 'row', - justifyContent: 'space-between', - marginVertical: 6, paddingHorizontal: 16, + paddingVertical: 8, }, value: { - paddingHorizontal: 4, + fontVariant: ['tabular-nums'], textAlign: 'center', - width: 60, }, }); diff --git a/src/screens/reader/components/ReaderBottomSheet/TTSTab.tsx b/src/screens/reader/components/ReaderBottomSheet/TTSTab.tsx index d3d897a20..19cdb9b65 100644 --- a/src/screens/reader/components/ReaderBottomSheet/TTSTab.tsx +++ b/src/screens/reader/components/ReaderBottomSheet/TTSTab.tsx @@ -1,25 +1,24 @@ import React, { useEffect, useState, useCallback, useMemo } from 'react'; -import { View, StyleSheet, Text, ScrollView, TouchableOpacity } from 'react-native'; +import { Pressable, View, StyleSheet, Text, ScrollView } from 'react-native'; import { BottomSheetScrollView } from '@gorhom/bottom-sheet'; -import Slider from '@react-native-community/slider'; -import { getAvailableVoicesAsync, Voice } from 'expo-speech'; +import { Dialog, List, Slider } from '@components'; import { getLocales } from 'expo-localization'; +import { Tts, TtsEngine, TtsVoice } from '@modules/nitro-tts'; import { useTheme, useChapterGeneralSettings, useChapterReaderSettings, } from '@hooks/persisted'; -import { getString } from '@strings/translations'; -import { List, Button } from '@components/index'; -import { Portal, Modal, Chip } from 'react-native-paper'; +import { getString } from '@i18n/translations'; +import { Chip } from 'react-native-paper'; import ReaderSheetPreferenceItem from './ReaderSheetPreferenceItem'; interface VoicePickerModalProps { visible: boolean; onDismiss: () => void; - voices: Voice[]; - onSelect: (voice: Voice) => void; - currentVoice?: Voice; + voices: TtsVoice[]; + onSelect: (voice?: TtsVoice) => void; + currentVoice?: TtsVoice; } const VoicePickerModal: React.FC = ({ @@ -27,7 +26,7 @@ const VoicePickerModal: React.FC = ({ onDismiss, voices, onSelect, - currentVoice + currentVoice, }) => { const theme = useTheme(); const [selectedLanguages, setSelectedLanguages] = useState([]); @@ -56,14 +55,12 @@ const VoicePickerModal: React.FC = ({ if (selectedLanguages.length === 0) { // Show system language voices by default return voices.filter(voice => { - if (voice.name === 'System') return true; const lang = voice.language?.split('-')[0]; return lang === systemLocale; }); } return voices.filter(voice => { - if (voice.name === 'System') return true; const lang = voice.language?.split('-')[0]; return lang && selectedLanguages.includes(lang); }); @@ -79,28 +76,19 @@ const VoicePickerModal: React.FC = ({ }); }; - useEffect(() => { - // Reset to system language when modal opens - if (visible) { - setSelectedLanguages([]); - } - }, [visible]); + const handleDismiss = () => { + setSelectedLanguages([]); + onDismiss(); + }; return ( - - - - Select Voice - - - {/* Language Filter */} + + Select Voice + Filter by language: @@ -114,7 +102,8 @@ const VoicePickerModal: React.FC = ({ const isSelected = selectedLanguages.includes(lang); const isSystemLang = lang === systemLocale; const showingSystemOnly = selectedLanguages.length === 0; - const isActive = isSelected || (showingSystemOnly && isSystemLang); + const isActive = + isSelected || (showingSystemOnly && isSystemLang); return ( = ({ onPress={() => toggleLanguage(lang)} style={[ styles.languageChip, - isActive && { backgroundColor: theme.primary } + isActive && { backgroundColor: theme.primary }, ]} textStyle={[ styles.languageChipText, - { color: isActive ? theme.onPrimary : theme.onSurface } + { color: isActive ? theme.onPrimary : theme.onSurface }, ]} > {lang.toUpperCase()} @@ -137,78 +126,212 @@ const VoicePickerModal: React.FC = ({ })} - - {/* Voice List */} + + + { + onSelect(undefined); + handleDismiss(); + }} + > + + + System default + + + {!currentVoice ? ( + + ✓ + + ) : null} + {filteredVoices.length === 0 ? ( - + No voices available for selected languages ) : ( - filteredVoices.map((voice: Voice, index: number) => ( - ( + { onSelect(voice); - onDismiss(); + handleDismiss(); }} > - + {voice.name} - {voice.language && ( - + {voice.language ? ( + {voice.language} - )} + ) : null} - {currentVoice?.identifier === voice.identifier && ( - - )} - + {currentVoice?.identifier === voice.identifier ? ( + + ✓ + + ) : null} + )) )} + + + Cancel + + + ); +}; - - )} - ListEmptyComponent={emptyComponent} - /> + + item.id} + renderItem={({ item }) => ( + + )} + ListEmptyComponent={emptyComponent} + /> + - )} - ListEmptyComponent={emptyComponent} - /> + + item + '_' + index} + renderItem={({ item }) => ( + + )} + ListEmptyComponent={emptyComponent} + /> + + - + ); }; diff --git a/src/screens/settings/SettingsLibraryScreen/GlobalUpdateCategoriesDialog.tsx b/src/screens/settings/SettingsLibraryScreen/GlobalUpdateCategoriesDialog.tsx new file mode 100644 index 000000000..94ad51574 --- /dev/null +++ b/src/screens/settings/SettingsLibraryScreen/GlobalUpdateCategoriesDialog.tsx @@ -0,0 +1,147 @@ +import { useCallback } from 'react'; +import { + FlatList, + ListRenderItemInfo, + Pressable, + StyleSheet, + Text, +} from 'react-native'; +import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; + +import { Dialog } from '@components'; +import type { Category } from '@database/types'; +import { useTheme } from '@hooks/persisted'; +import { getString } from '@i18n/translations'; + +interface GlobalUpdateCategoriesDialogProps { + categories: Category[]; + excludedCategoryIds: number[]; + includedCategoryIds: number[]; + visible: boolean; + onCancel: () => void; + onChange: ( + includedCategoryIds: number[], + excludedCategoryIds: number[], + ) => void; + onSave: () => void; +} + +const categoryKey = (category: Category) => category.id.toString(); + +const GlobalUpdateCategoriesDialog = ({ + categories, + excludedCategoryIds, + includedCategoryIds, + visible, + onCancel, + onChange, + onSave, +}: GlobalUpdateCategoriesDialogProps) => { + const theme = useTheme(); + + const renderCategory = useCallback( + ({ item }: ListRenderItemInfo) => { + const isIncluded = includedCategoryIds.includes(item.id); + const isExcluded = excludedCategoryIds.includes(item.id); + const icon = isExcluded + ? 'close-box' + : isIncluded + ? 'checkbox-marked' + : 'checkbox-blank-outline'; + + const toggleCategory = () => { + if (isExcluded) { + onChange( + includedCategoryIds, + excludedCategoryIds.filter(categoryId => categoryId !== item.id), + ); + } else if (isIncluded) { + onChange( + includedCategoryIds.filter(categoryId => categoryId !== item.id), + [...excludedCategoryIds, item.id], + ); + } else { + onChange([...includedCategoryIds, item.id], excludedCategoryIds); + } + }; + + return ( + + + + {item.name} + + + ); + }, + [ + excludedCategoryIds, + includedCategoryIds, + onChange, + theme.onSurface, + theme.onSurfaceVariant, + theme.primary, + theme.rippleColor, + ], + ); + + return ( + + + + {getString('generalSettingsScreen.globalUpdateCategories')} + + + {getString('generalSettingsScreen.globalUpdateCategoriesDescription')} + + + + + + + + {getString('common.cancel')} + + {getString('common.ok')} + + + ); +}; + +export default GlobalUpdateCategoriesDialog; + +const styles = StyleSheet.create({ + category: { + alignItems: 'center', + flexDirection: 'row', + gap: 16, + paddingHorizontal: 16, + paddingVertical: 12, + }, + categoryName: { + fontSize: 16, + }, + list: { + maxHeight: 420, + }, +}); diff --git a/src/screens/settings/SettingsLibraryScreen/SettingsLibraryScreen.tsx b/src/screens/settings/SettingsLibraryScreen/SettingsLibraryScreen.tsx index 3f33d7b30..86c21ebd1 100644 --- a/src/screens/settings/SettingsLibraryScreen/SettingsLibraryScreen.tsx +++ b/src/screens/settings/SettingsLibraryScreen/SettingsLibraryScreen.tsx @@ -1,59 +1,447 @@ -import React from 'react'; -import { Appbar, List } from '@components'; -import { getString } from '@strings/translations'; +import { useCallback, useState } from 'react'; +import { ScrollView, StyleSheet } from 'react-native'; + +import { Appbar, List, SafeAreaView } from '@components'; import { useBoolean } from '@hooks'; -import { useCategories, useTheme } from '@hooks/persisted'; -import { useNavigation } from '@react-navigation/native'; +import { + useAppSettings, + useCategories, + useLastUpdate, + useLibrarySettings, + useTheme, +} from '@hooks/persisted'; +import { getString } from '@i18n/translations'; +import type { StringMap } from '@i18n/types'; +import { LibrarySettingsScreenProps } from '@navigators/types'; +import { + DisplayModes, + displayModesList, + LibrarySortOrder, +} from '@screens/library/constants/constants'; import { Portal } from 'react-native-paper'; +import { + configureAutomaticLibraryUpdates, + type AutomaticLibraryUpdateInterval, +} from '@services/backgroundTasks'; +import { showToast } from '@utils/showToast'; + +import AutomaticUpdatesDialog from './AutomaticUpdatesDialog'; +import DefaultChapterSortModal from '../components/DefaultChapterSortModal'; +import SettingSwitch from '../components/SettingSwitch'; import DefaultCategoryDialog from './DefaultCategoryDialog'; +import GlobalUpdateCategoriesDialog from './GlobalUpdateCategoriesDialog'; +import SmartUpdateDialog from './SmartUpdateDialog'; +import DisplayModeModal from './modals/DisplayModeModal'; +import GridSizeModal from './modals/GridSizeModal'; +import NovelBadgesModal from './modals/NovelBadgesModal'; +import NovelSortModal from './modals/NovelSortModal'; + +type LibrarySortLabel = Extract< + keyof StringMap, + `libraryScreen.bottomSheet.sortOrders.${string}` +>; + +const SORT_ORDER_LABELS: Record = { + name: 'libraryScreen.bottomSheet.sortOrders.alphabetically', + chaptersDownloaded: 'libraryScreen.bottomSheet.sortOrders.download', + chaptersUnread: 'libraryScreen.bottomSheet.sortOrders.totalChapters', + id: 'libraryScreen.bottomSheet.sortOrders.dateAdded', + lastReadAt: 'libraryScreen.bottomSheet.sortOrders.lastRead', + lastUpdatedAt: 'libraryScreen.bottomSheet.sortOrders.lastUpdated', +}; + +const AUTOMATIC_UPDATE_LABELS: Record< + AutomaticLibraryUpdateInterval, + keyof StringMap +> = { + 0: 'generalSettingsScreen.automaticUpdatesOff', + 12: 'generalSettingsScreen.automaticUpdatesEvery12Hours', + 24: 'generalSettingsScreen.automaticUpdatesDaily', + 48: 'generalSettingsScreen.automaticUpdatesEvery2Days', + 72: 'generalSettingsScreen.automaticUpdatesEvery3Days', + 168: 'generalSettingsScreen.automaticUpdatesWeekly', +}; -const SettingsLibraryScreen = () => { +const SettingsLibraryScreen = ({ navigation }: LibrarySettingsScreenProps) => { const theme = useTheme(); - const { goBack, navigate } = useNavigation(); + const { + displayMode = DisplayModes.Comfortable, + novelsPerRow = 3, + showDownloadBadges = true, + showNumberOfNovels = false, + showUnreadBadges = true, + sortOrder = LibrarySortOrder.DateAdded_DESC, + defaultCategoryId, + globalUpdateExcludeCategoryIds = [], + globalUpdateIncludeCategoryIds = [], + setLibrarySettings, + } = useLibrarySettings(); + const { + automaticLibraryUpdateIntervalHours = 0, + defaultChapterSort, + downloadNewChapters, + refreshNovelMetadata, + setAppSettings, + smartUpdateSkipCompleted = false, + smartUpdateSkipUnstarted = false, + smartUpdateSkipWithUnread = false, + updateLibraryOnLaunch, + useLibraryFAB, + } = useAppSettings(); + const { showLastUpdateTime, setShowLastUpdateTime } = useLastUpdate(); const { categories } = useCategories(); + const displayModal = useBoolean(); + const automaticUpdatesDialog = useBoolean(); const defaultCategoryDialog = useBoolean(); + const globalUpdateCategoriesDialog = useBoolean(); + const gridSizeModal = useBoolean(); + const novelBadgesModal = useBoolean(); + const novelSortModal = useBoolean(); + const smartUpdateDialog = useBoolean(); + const defaultChapterSortModal = useBoolean(); + const appDefaultCategory = categories.find(category => category.id === 1); + const selectedDefaultCategory = categories.find( + category => category.id === defaultCategoryId, + ); + const selectableCategories = categories.filter( + category => category.id === 1 || category.id > 2, + ); + const globalUpdateCategories = categories.filter( + category => category.id !== 2, + ); + const [draftIncludedCategoryIds, setDraftIncludedCategoryIds] = useState< + number[] + >([]); + const [draftExcludedCategoryIds, setDraftExcludedCategoryIds] = useState< + number[] + >([]); + const [draftSmartUpdateFilters, setDraftSmartUpdateFilters] = useState({ + skipCompleted: false, + skipUnstarted: false, + skipWithUnread: false, + }); - const setDefaultCategoryId = (categoryId: number) => { - // TODO: update default category + const setDefaultCategory = (categoryId: number) => { + setLibrarySettings({ + defaultCategoryId: categoryId === 1 ? undefined : categoryId, + }); + defaultCategoryDialog.setFalse(); + }; - categoryId; + const showGlobalUpdateCategoriesDialog = () => { + setDraftIncludedCategoryIds(globalUpdateIncludeCategoryIds); + setDraftExcludedCategoryIds(globalUpdateExcludeCategoryIds); + globalUpdateCategoriesDialog.setTrue(); }; + const updateDraftCategoryFilters = useCallback( + (includedCategoryIds: number[], excludedCategoryIds: number[]) => { + setDraftIncludedCategoryIds(includedCategoryIds); + setDraftExcludedCategoryIds(excludedCategoryIds); + }, + [], + ); + + const saveGlobalUpdateCategories = () => { + setLibrarySettings({ + globalUpdateExcludeCategoryIds: draftExcludedCategoryIds, + globalUpdateIncludeCategoryIds: draftIncludedCategoryIds, + }); + globalUpdateCategoriesDialog.setFalse(); + }; + + const showSmartUpdateDialog = () => { + setDraftSmartUpdateFilters({ + skipCompleted: smartUpdateSkipCompleted, + skipUnstarted: smartUpdateSkipUnstarted, + skipWithUnread: smartUpdateSkipWithUnread, + }); + smartUpdateDialog.setTrue(); + }; + + const saveSmartUpdateFilters = () => { + setAppSettings({ + smartUpdateSkipCompleted: draftSmartUpdateFilters.skipCompleted, + smartUpdateSkipUnstarted: draftSmartUpdateFilters.skipUnstarted, + smartUpdateSkipWithUnread: draftSmartUpdateFilters.skipWithUnread, + }); + smartUpdateDialog.setFalse(); + }; + + const getCategoryFilterDescription = ( + categoryIds: number[], + emptyLabel: string, + ) => { + if (categoryIds.length === 0) { + return emptyLabel; + } + + const names = globalUpdateCategories + .filter(category => categoryIds.includes(category.id)) + .map(category => category.name); + return names.length > 0 ? names.join(', ') : getString('common.none'); + }; + + const includedCategoriesDescription = getCategoryFilterDescription( + globalUpdateIncludeCategoryIds, + getString('common.all'), + ); + const excludedCategoriesDescription = getCategoryFilterDescription( + globalUpdateExcludeCategoryIds, + getString('common.none'), + ); + const smartUpdateDescription = [ + smartUpdateSkipWithUnread + ? getString('generalSettingsScreen.smartUpdateSkipWithUnread') + : null, + smartUpdateSkipUnstarted + ? getString('generalSettingsScreen.smartUpdateSkipUnstarted') + : null, + smartUpdateSkipCompleted + ? getString('generalSettingsScreen.smartUpdateSkipCompleted') + : null, + ] + .filter(Boolean) + .join(', '); + + const setAutomaticUpdateInterval = async ( + intervalHours: AutomaticLibraryUpdateInterval, + ) => { + try { + await configureAutomaticLibraryUpdates(intervalHours); + setAppSettings({ automaticLibraryUpdateIntervalHours: intervalHours }); + automaticUpdatesDialog.setFalse(); + } catch (error) { + showToast(error instanceof Error ? error.message : String(error)); + } + }; + + const sortOrderParts = sortOrder.split(' '); + const sortOrderLabel = + SORT_ORDER_LABELS[sortOrderParts[0]] ?? + 'libraryScreen.bottomSheet.sortOrders.dateAdded'; + + const badgeDescription = [ + showDownloadBadges + ? getString('libraryScreen.bottomSheet.display.download') + : null, + showUnreadBadges + ? getString('libraryScreen.bottomSheet.display.unread') + : null, + showNumberOfNovels + ? getString('libraryScreen.bottomSheet.display.numberOfItems') + : null, + ] + .filter(Boolean) + .join(', '); + return ( - <> + + + + + {getString('common.display')} + + + + + + {getString('library')} + navigation.navigate('Categories')} + theme={theme} + /> + + + setAppSettings({ updateLibraryOnLaunch: !updateLibraryOnLaunch }) + } + theme={theme} + /> + setAppSettings({ useLibraryFAB: !useLibraryFAB })} + theme={theme} + /> + + + {getString('generalSettingsScreen.globalUpdate')} + + + + + + setAppSettings({ refreshNovelMetadata: !refreshNovelMetadata }) + } + theme={theme} + /> + setShowLastUpdateTime(!showLastUpdateTime)} + theme={theme} + /> + + {getString('generalSettingsScreen.autoDownload')} + + + setAppSettings({ downloadNewChapters: !downloadNewChapters }) + } + theme={theme} + /> + + + + + + + - - navigate('MoreStack', { screen: 'Categories' })} - theme={theme} - /> - category.sort === 1)?.name} - onPress={defaultCategoryDialog.setTrue} - theme={theme} - /> - + + + - + ); }; export default SettingsLibraryScreen; + +const styles = StyleSheet.create({ + paddingBottom: { + paddingBottom: 24, + }, +}); diff --git a/src/screens/settings/SettingsLibraryScreen/SmartUpdateDialog.tsx b/src/screens/settings/SettingsLibraryScreen/SmartUpdateDialog.tsx new file mode 100644 index 000000000..3e3c61a9f --- /dev/null +++ b/src/screens/settings/SettingsLibraryScreen/SmartUpdateDialog.tsx @@ -0,0 +1,73 @@ +import { Checkbox, Dialog } from '@components'; +import type { SmartUpdateFilters } from '@hooks/persisted/useSettings'; +import { useTheme } from '@hooks/persisted'; +import { getString } from '@i18n/translations'; + +interface SmartUpdateDialogProps { + filters: SmartUpdateFilters; + visible: boolean; + onCancel: () => void; + onChange: (filters: SmartUpdateFilters) => void; + onSave: () => void; +} + +const SmartUpdateDialog = ({ + filters, + visible, + onCancel, + onChange, + onSave, +}: SmartUpdateDialogProps) => { + const theme = useTheme(); + + return ( + + + {getString('generalSettingsScreen.smartUpdate')} + + + + onChange({ + ...filters, + skipWithUnread: !filters.skipWithUnread, + }) + } + theme={theme} + /> + + onChange({ + ...filters, + skipUnstarted: !filters.skipUnstarted, + }) + } + theme={theme} + /> + + onChange({ + ...filters, + skipCompleted: !filters.skipCompleted, + }) + } + theme={theme} + /> + + + + {getString('common.cancel')} + + {getString('common.ok')} + + + ); +}; + +export default SmartUpdateDialog; diff --git a/src/screens/settings/SettingsGeneralScreen/modals/DisplayModeModal.tsx b/src/screens/settings/SettingsLibraryScreen/modals/DisplayModeModal.tsx similarity index 59% rename from src/screens/settings/SettingsGeneralScreen/modals/DisplayModeModal.tsx rename to src/screens/settings/SettingsLibraryScreen/modals/DisplayModeModal.tsx index e5e0a3bd0..d0c7b37b5 100644 --- a/src/screens/settings/SettingsGeneralScreen/modals/DisplayModeModal.tsx +++ b/src/screens/settings/SettingsLibraryScreen/modals/DisplayModeModal.tsx @@ -3,15 +3,11 @@ import { displayModesList, } from '@screens/library/constants/constants'; import React from 'react'; -import { Text, StyleSheet } from 'react-native'; -import { Portal } from 'react-native-paper'; - -import { RadioButton } from '@components/RadioButton/RadioButton'; +import { Dialog, RadioButton } from '@components'; import { ThemeColors } from '@theme/types'; import { useLibrarySettings } from '@hooks/persisted'; -import { getString } from '@strings/translations'; -import { Modal } from '@components'; +import { getString } from '@i18n/translations'; interface DisplayModeModalProps { displayMode: DisplayModes; @@ -29,11 +25,11 @@ const DisplayModeModal: React.FC = ({ const { setLibrarySettings } = useLibrarySettings(); return ( - - - - {getString('generalSettingsScreen.displayMode')} - + + + {getString('generalSettingsScreen.displayMode')} + + {displayModesList.map(mode => ( = ({ theme={theme} /> ))} - - + + ); }; export default DisplayModeModal; - -const styles = StyleSheet.create({ - modalHeader: { - fontSize: 24, - marginBottom: 10, - }, -}); diff --git a/src/screens/settings/SettingsGeneralScreen/modals/GridSizeModal.tsx b/src/screens/settings/SettingsLibraryScreen/modals/GridSizeModal.tsx similarity index 59% rename from src/screens/settings/SettingsGeneralScreen/modals/GridSizeModal.tsx rename to src/screens/settings/SettingsLibraryScreen/modals/GridSizeModal.tsx index 98771b48d..52b67d19c 100644 --- a/src/screens/settings/SettingsGeneralScreen/modals/GridSizeModal.tsx +++ b/src/screens/settings/SettingsLibraryScreen/modals/GridSizeModal.tsx @@ -1,14 +1,10 @@ import React from 'react'; -import { Text, StyleSheet } from 'react-native'; -import { Portal } from 'react-native-paper'; - -import { RadioButton } from '@components/RadioButton/RadioButton'; +import { Dialog, RadioButton } from '@components'; import { ThemeColors } from '@theme/types'; import { useLibrarySettings } from '@hooks/persisted'; -import { getString } from '@strings/translations'; -import { Modal } from '@components'; +import { getString } from '@i18n/translations'; interface GridSizeModalProps { novelsPerRow: number; @@ -34,18 +30,18 @@ const GridSizeModal: React.FC = ({ }; return ( - - - + + + {getString('generalSettingsScreen.gridSize')} - - + + {getString('generalSettingsScreen.gridSizeDesc', { num: novelsPerRow, })} - + + + {Object.keys(gridSizes).map(item => { const it = Number(item); return ( @@ -59,24 +55,9 @@ const GridSizeModal: React.FC = ({ /> ); })} - - + + ); }; export default GridSizeModal; - -const styles = StyleSheet.create({ - modalDescription: { - fontSize: 16, - marginBottom: 16, - }, - modalHeader: { - fontSize: 24, - marginBottom: 10, - }, - slider: { - height: 40, - width: '100%', - }, -}); diff --git a/src/screens/settings/SettingsGeneralScreen/modals/NovelBadgesModal.tsx b/src/screens/settings/SettingsLibraryScreen/modals/NovelBadgesModal.tsx similarity index 70% rename from src/screens/settings/SettingsGeneralScreen/modals/NovelBadgesModal.tsx rename to src/screens/settings/SettingsLibraryScreen/modals/NovelBadgesModal.tsx index 46527efa8..d8515b748 100644 --- a/src/screens/settings/SettingsGeneralScreen/modals/NovelBadgesModal.tsx +++ b/src/screens/settings/SettingsLibraryScreen/modals/NovelBadgesModal.tsx @@ -1,10 +1,7 @@ import React from 'react'; -import { Text, StyleSheet } from 'react-native'; -import { Portal } from 'react-native-paper'; - -import { Checkbox, Modal } from '@components'; -import { getString } from '@strings/translations'; +import { Checkbox, Dialog } from '@components'; +import { getString } from '@i18n/translations'; import { ThemeColors } from '@theme/types'; import { useLibrarySettings } from '@hooks/persisted'; @@ -26,11 +23,14 @@ const NovelBadgesModal: React.FC = ({ setLibrarySettings, } = useLibrarySettings(); return ( - - - - {getString('libraryScreen.bottomSheet.display.badges')} - + + + {getString('libraryScreen.bottomSheet.display.badges')} + + = ({ } theme={theme} /> - - + + ); }; export default NovelBadgesModal; - -const styles = StyleSheet.create({ - modalDescription: { - fontSize: 16, - marginBottom: 16, - }, - modalHeader: { - fontSize: 24, - marginBottom: 10, - }, -}); diff --git a/src/screens/settings/SettingsGeneralScreen/modals/NovelSortModal.tsx b/src/screens/settings/SettingsLibraryScreen/modals/NovelSortModal.tsx similarity index 62% rename from src/screens/settings/SettingsGeneralScreen/modals/NovelSortModal.tsx rename to src/screens/settings/SettingsLibraryScreen/modals/NovelSortModal.tsx index aa3589ce6..3f5afb402 100644 --- a/src/screens/settings/SettingsGeneralScreen/modals/NovelSortModal.tsx +++ b/src/screens/settings/SettingsLibraryScreen/modals/NovelSortModal.tsx @@ -1,7 +1,4 @@ import React from 'react'; -import { Text, StyleSheet } from 'react-native'; - -import { Portal } from 'react-native-paper'; import { LibrarySortOrder, @@ -10,8 +7,8 @@ import { import { ThemeColors } from '@theme/types'; import { SortItem } from '@components/Checkbox/Checkbox'; import { useLibrarySettings } from '@hooks/persisted'; -import { getString } from '@strings/translations'; -import { Modal } from '@components'; +import { getString } from '@i18n/translations'; +import { Dialog } from '@components'; interface NovelSortModalProps { novelSortModalVisible: boolean; @@ -27,11 +24,11 @@ const NovelSortModal: React.FC = ({ const { sortOrder = LibrarySortOrder.DateAdded_DESC, setLibrarySettings } = useLibrarySettings(); return ( - - - - {getString('generalSettingsScreen.sortOrder')} - + + + {getString('generalSettingsScreen.sortOrder')} + + {librarySortOrderList.map(item => ( = ({ } /> ))} - - + + ); }; export default NovelSortModal; - -const styles = StyleSheet.create({ - modalDescription: { - fontSize: 16, - marginBottom: 16, - paddingHorizontal: 24, - }, - modalHeader: { - fontSize: 24, - marginBottom: 10, - paddingHorizontal: 24, - }, - slider: { - height: 40, - width: '100%', - }, -}); diff --git a/src/screens/settings/SettingsReaderScreen/Modals/FontPickerModal.tsx b/src/screens/settings/SettingsReaderScreen/Modals/FontPickerModal.tsx index 65cf32ee4..7854d2638 100644 --- a/src/screens/settings/SettingsReaderScreen/Modals/FontPickerModal.tsx +++ b/src/screens/settings/SettingsReaderScreen/Modals/FontPickerModal.tsx @@ -1,12 +1,11 @@ import React from 'react'; -import { Portal } from 'react-native-paper'; -import { RadioButton } from '@components/RadioButton/RadioButton'; +import { Dialog, RadioButton } from '@components'; import { useChapterReaderSettings, useTheme } from '@hooks/persisted'; import { readerFonts } from '@utils/constants/readerConstants'; -import { Modal } from '@components'; +import { getString } from '@i18n/translations'; interface FontPickerModalProps { visible: boolean; @@ -23,8 +22,11 @@ const FontPickerModal: React.FC = ({ const { setChapterReaderSettings } = useChapterReaderSettings(); return ( - - + + + {getString('readerScreen.bottomSheet.fontStyle')} + + {readerFonts.map(item => ( = ({ theme={theme} /> ))} - - + + + + {getString('common.ok')} + + + ); }; diff --git a/src/screens/settings/SettingsReaderScreen/Modals/VoicePickerModal.tsx b/src/screens/settings/SettingsReaderScreen/Modals/VoicePickerModal.tsx deleted file mode 100644 index 6aa2a84eb..000000000 --- a/src/screens/settings/SettingsReaderScreen/Modals/VoicePickerModal.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React, { useState } from 'react'; - -import { Portal, TextInput, ActivityIndicator } from 'react-native-paper'; -import { RadioButton } from '@components/RadioButton/RadioButton'; - -import { useChapterReaderSettings, useTheme } from '@hooks/persisted'; -import { Voice } from 'expo-speech'; -import { LegendList } from '@legendapp/list'; -import { Modal } from '@components'; -import { StyleSheet } from 'react-native'; - -interface VoicePickerModalProps { - visible: boolean; - onDismiss: () => void; - voices: Voice[]; -} - -const VoicePickerModal: React.FC = ({ - onDismiss, - visible, - voices, -}) => { - const theme = useTheme(); - const [searchedVoices, setSearchedVoices] = useState([]); - const [searchText, setSearchText] = useState(''); - const { setChapterReaderSettings, tts } = useChapterReaderSettings(); - - return ( - - - { - setSearchText(text); - setSearchedVoices( - voices.filter(voice => - voice.name - .toLocaleLowerCase() - .includes(text.toLocaleLowerCase()), - ), - ); - }} - value={searchText} - placeholder="Search voice" - /> - } - ListHeaderComponentStyle={styles.paddingHorizontal} - data={searchText ? searchedVoices : voices} - extraData={tts?.voice} - renderItem={({ item }) => ( - - setChapterReaderSettings({ tts: { ...tts, voice: item } }) - } - label={item.name + ` (${item.language})`} - labelStyle={{ fontFamily: item.name }} - theme={theme} - /> - )} - keyExtractor={(item, index) => - item.identifier || `voice_${index}_${item.name}` - } - estimatedItemSize={64} - ListEmptyComponent={ - - } - /> - - - ); -}; - -export default VoicePickerModal; - -const styles = StyleSheet.create({ - containerStyle: { - flex: 1, - }, - paddingHorizontal: { paddingHorizontal: 12 }, - marginTop: { marginTop: 16 }, -}); diff --git a/src/screens/settings/SettingsReaderScreen/ReaderTextSize.tsx b/src/screens/settings/SettingsReaderScreen/ReaderTextSize.tsx index 9837fd6c8..9f8abf8af 100644 --- a/src/screens/settings/SettingsReaderScreen/ReaderTextSize.tsx +++ b/src/screens/settings/SettingsReaderScreen/ReaderTextSize.tsx @@ -2,8 +2,8 @@ import { StyleSheet, Text, TextStyle, View } from 'react-native'; import React from 'react'; import { useChapterReaderSettings, useTheme } from '@hooks/persisted'; -import { IconButtonV2 } from '@components/index'; -import { getString } from '@strings/translations'; +import { Slider } from '@components'; +import { getString } from '@i18n/translations'; interface ReaderTextSizeProps { labelStyle?: TextStyle | TextStyle[]; @@ -15,29 +15,27 @@ const ReaderTextSize: React.FC = ({ labelStyle }) => { return ( - - {getString('readerScreen.bottomSheet.textSize')} - - - setChapterReaderSettings({ textSize: textSize - 1 })} - theme={theme} - /> + + + {getString('readerScreen.bottomSheet.textSize')} + - {textSize} + {textSize}px - setChapterReaderSettings({ textSize: textSize + 1 })} - theme={theme} - /> + `${value}px`} + accessibilityLabel={getString('readerScreen.bottomSheet.textSize')} + onSlidingComplete={value => + setChapterReaderSettings({ textSize: value }) + } + /> ); }; @@ -45,18 +43,16 @@ const ReaderTextSize: React.FC = ({ labelStyle }) => { export default ReaderTextSize; const styles = StyleSheet.create({ - buttonContainer: { + labelRow: { alignItems: 'center', flexDirection: 'row', + justifyContent: 'space-between', }, container: { - alignItems: 'center', - flexDirection: 'row', - justifyContent: 'space-between', marginVertical: 6, paddingHorizontal: 16, }, value: { - paddingHorizontal: 24, + fontVariant: ['tabular-nums'], }, }); diff --git a/src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx b/src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx index 6cad22048..e901002c4 100644 --- a/src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx +++ b/src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx @@ -1,12 +1,18 @@ import { View, StatusBar, StyleSheet, useWindowDimensions } from 'react-native'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { BottomSheetModal } from '@gorhom/bottom-sheet'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types'; import { useNavigation } from '@react-navigation/native'; import WebView from 'react-native-webview'; import { FAB } from 'react-native-paper'; +import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; +import { + TabView, + type TabBarProps, + type TabDescriptor, +} from 'react-native-tab-view'; import { dummyHTML } from './utils'; -import { Appbar, SafeAreaView } from '@components/index'; +import { Appbar, SafeAreaView, TopTabBar } from '@components/index'; import BottomSheet from '@components/BottomSheet/BottomSheet'; import { @@ -14,19 +20,48 @@ import { useChapterReaderSettings, useTheme, } from '@hooks/persisted'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import color from 'color'; import { useBatteryLevel } from 'react-native-device-info'; -import * as Speech from 'expo-speech'; -import TabBar, { Tab } from './components/TabBar'; import DisplayTab from './tabs/DisplayTab'; import ThemeTab from './tabs/ThemeTab'; import NavigationTab from './tabs/NavigationTab'; import AccessibilityTab from './tabs/AccessibilityTab'; import AdvancedTab from './tabs/AdvancedTab'; +import { useTtsSession } from '@screens/reader/hooks/useTtsSession'; +import type { TtsSettings } from '@modules/nitro-tts'; + +type ReaderSettingsRoute = { + key: 'display' | 'theme' | 'navigation' | 'accessibility' | 'advanced'; + title: string; + icon: React.ComponentProps['name']; +}; + +const routes: ReaderSettingsRoute[] = [ + { key: 'display', title: 'Display', icon: 'format-size' }, + { key: 'theme', title: 'Theme', icon: 'palette-outline' }, + { + key: 'navigation', + title: 'Navigation', + icon: 'gesture-swipe-horizontal', + }, + { + key: 'accessibility', + title: 'Accessibility', + icon: 'account-voice', + }, + { key: 'advanced', title: 'Advanced', icon: 'code-braces' }, +]; + +const tabOptions: TabDescriptor = { + icon: ({ route, color: iconColor }) => ( + + ), + label: () => null, +}; export type TextAlignments = | 'left' @@ -38,25 +73,26 @@ export type TextAlignments = type WebViewPostEvent = { type: string; - data?: { [key: string]: string | number }; + data?: unknown; }; +const toNativeTtsSettings = ( + settings: ReturnType['tts'], +): TtsSettings => ({ + engineName: settings?.engine?.name, + voiceIdentifier: settings?.voice?.identifier, + rate: settings?.rate ?? 1, + pitch: settings?.pitch ?? 1, +}); + const SettingsReaderScreen = () => { const theme = useTheme(); const navigation = useNavigation(); - const webViewRef = useRef(null); - const bottomSheetRef = useRef(null); + const webViewRef = useRef>(null); + const bottomSheetRef = useRef(null); const { bottom, right } = useSafeAreaInsets(); - const { height: screenHeight } = useWindowDimensions(); - const [activeTab, setActiveTab] = useState('display'); - - const tabs: Tab[] = [ - { id: 'display', label: 'Display', icon: 'format-size' }, - { id: 'theme', label: 'Theme', icon: 'palette-outline' }, - { id: 'navigation', label: 'Navigation', icon: 'gesture-swipe-horizontal' }, - { id: 'accessibility', label: 'Accessibility', icon: 'account-voice' }, - { id: 'advanced', label: 'Advanced', icon: 'code-braces' }, - ]; + const { height: screenHeight, width: screenWidth } = useWindowDimensions(); + const [tabIndex, setTabIndex] = useState(0); const novel = { 'artist': null, @@ -95,6 +131,14 @@ const SettingsReaderScreen = () => { const batteryLevel = useBatteryLevel(); const readerSettings = useChapterReaderSettings(); const chapterGeneralSettings = useChapterGeneralSettings(); + const { + command: runTtsCommand, + loadAndPlay, + progress: ttsProgress, + seekTo: seekTts, + state: ttsState, + updateSettings: updateTtsSettings, + } = useTtsSession(); const BOTTOM_SHEET_HEIGHT = screenHeight * 0.7; const assetsUriPrefix = useMemo( @@ -127,11 +171,12 @@ const SettingsReaderScreen = () => { --theme-outline: ${theme.outline}; --theme-rippleColor: ${theme.rippleColor}; } - + @font-face { font-family: ${readerSettings.fontFamily}; - src: url("file:///android_asset/fonts/${readerSettings.fontFamily - }.ttf"); + src: url("file:///android_asset/fonts/${ + readerSettings.fontFamily + }.ttf"); } @@ -141,31 +186,78 @@ const SettingsReaderScreen = () => { const readerBackgroundColor = readerSettings.theme; useEffect(() => { - return () => { - Speech.stop(); - }; - }, []); + updateTtsSettings(toNativeTtsSettings(readerSettings.tts)); + }, [readerSettings.tts, updateTtsSettings]); + + useEffect(() => { + webViewRef.current?.injectJavaScript(` + window.tts?.setPlaybackState?.(${JSON.stringify(ttsState)}); + true; + `); + if (ttsState === 'completed') { + webViewRef.current?.injectJavaScript('window.tts?.complete?.(); true;'); + } + }, [ttsState]); + + useEffect(() => { + if (ttsProgress.total > 0) { + webViewRef.current?.injectJavaScript(` + window.tts?.setActiveIndex?.(${ttsProgress.index}); + true; + `); + } + }, [ttsProgress]); const openBottomSheet = () => { bottomSheetRef.current?.present(); }; - const renderTabContent = () => { - switch (activeTab) { - case 'display': - return ; - case 'theme': - return ; - case 'navigation': - return ; - case 'accessibility': - return ; - case 'advanced': - return ; - default: - return ; - } - }; + const renderTabContent = useCallback( + ({ route }: { route: ReaderSettingsRoute }) => { + switch (route.key) { + case 'display': + return ; + case 'theme': + return ; + case 'navigation': + return ; + case 'accessibility': + return ; + case 'advanced': + return ; + default: + return ; + } + }, + [], + ); + + const renderTabBar = useCallback( + (props: TabBarProps) => ( + + ), + [ + theme.outlineVariant, + theme.onSurfaceVariant, + theme.primary, + theme.rippleColor, + theme.surface, + theme.surfaceContainerLow, + ], + ); return ( { {/* Large Preview Area */} - ref={webViewRef} originWhitelist={['*']} allowFileAccess={true} @@ -205,23 +297,57 @@ const SettingsReaderScreen = () => { } setHidden(!hidden); break; - case 'speak': - if (event.data && typeof event.data === 'string') { - Speech.speak(event.data, { - onDone() { - webViewRef.current?.injectJavaScript('tts.next?.()'); - }, - voice: readerSettings.tts?.voice?.identifier, - pitch: readerSettings.tts?.pitch || 1, - rate: readerSettings.tts?.rate || 1, - }); - } else { - webViewRef.current?.injectJavaScript('tts.next?.()'); - } + case 'tts-queue': { + const payload = event.data as + | { queue?: unknown; startIndex?: unknown } + | undefined; + const queue = Array.isArray(payload?.queue) + ? payload.queue.filter( + (item): item is string => + typeof item === 'string' && item.trim().length > 0, + ) + : []; + const startIndex = + typeof payload?.startIndex === 'number' + ? payload.startIndex + : 0; + void loadAndPlay( + queue, + startIndex, + { + novelName: novel.name, + chapterName: chapter.name, + coverUri: novel.cover, + }, + toNativeTtsSettings(readerSettings.tts), + ); break; - case 'stop-speak': - Speech.stop(); + } + case 'tts-command': { + if (!event.data || typeof event.data !== 'object') { + break; + } + const data = event.data as { + command?: unknown; + index?: unknown; + }; + switch (data.command) { + case 'next': + case 'pause': + case 'play': + case 'previous': + case 'replay': + case 'stop': + runTtsCommand(data.command); + break; + case 'seekTo': + if (typeof data.index === 'number') { + seekTts(data.index); + } + break; + } break; + } } }} source={{ @@ -231,8 +357,9 @@ const SettingsReaderScreen = () => { ${webViewCSS} - +
    ${dummyHTML}
    @@ -240,24 +367,24 @@ const SettingsReaderScreen = () => { @@ -292,34 +419,19 @@ const SettingsReaderScreen = () => { - - {/* Drag Handle */} - - - - - {/* Tab Bar */} - + - - {/* Tab Content */} - {renderTabContent()}
    @@ -336,19 +448,6 @@ const styles = StyleSheet.create({ bottomSheetContent: { flex: 1, }, - dragHandleContainer: { - alignItems: 'center', - paddingVertical: 12, - }, - dragHandle: { - width: 32, - height: 4, - borderRadius: 2, - opacity: 0.4, - }, - tabContent: { - flex: 1, - }, container: { flex: 1, }, @@ -358,4 +457,8 @@ const styles = StyleSheet.create({ webView: { flex: 1, }, + tabBar: { + borderBottomWidth: 1, + elevation: 0, + }, }); diff --git a/src/screens/settings/SettingsReaderScreen/components/TabBar.tsx b/src/screens/settings/SettingsReaderScreen/components/TabBar.tsx deleted file mode 100644 index d66962590..000000000 --- a/src/screens/settings/SettingsReaderScreen/components/TabBar.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; -import { View, Pressable, StyleSheet } from 'react-native'; -import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; -import { ThemeColors } from '@theme/types'; - -export interface Tab { - id: string; - label: string; - icon: React.ComponentProps['name']; -} - -interface TabBarProps { - tabs: Tab[]; - activeTab: string; - onTabChange: (tabId: string) => void; - theme: ThemeColors; -} - -const TabBar: React.FC = ({ - tabs, - activeTab, - onTabChange, - theme, -}) => { - return ( - - {tabs.map(tab => { - const isActive = activeTab === tab.id; - return ( - onTabChange(tab.id)} - android_ripple={{ - color: theme.rippleColor, - borderless: false, - }} - > - - - ); - })} - - ); -}; - -export default TabBar; - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - borderBottomWidth: 1, - borderBottomColor: 'rgba(0, 0, 0, 0.12)', - backgroundColor: 'transparent', // Will be set by theme if needed, but the lint was about static style - }, - tab: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - paddingVertical: 14, - paddingHorizontal: 8, - minHeight: 48, - borderBottomWidth: 2, - }, -}); diff --git a/src/screens/settings/SettingsReaderScreen/tabs/AccessibilityTab.tsx b/src/screens/settings/SettingsReaderScreen/tabs/AccessibilityTab.tsx index 51b3b5ab8..cf217feec 100644 --- a/src/screens/settings/SettingsReaderScreen/tabs/AccessibilityTab.tsx +++ b/src/screens/settings/SettingsReaderScreen/tabs/AccessibilityTab.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { View, StyleSheet } from 'react-native'; import { BottomSheetScrollView } from '@gorhom/bottom-sheet'; import { useChapterGeneralSettings, useTheme } from '@hooks/persisted'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { List } from '@components/index'; import SettingSwitch from '../../components/SettingSwitch'; @@ -29,6 +29,9 @@ const AccessibilityTab: React.FC = () => { setChapterGeneralSettings({ fullScreenMode: !fullScreenMode }) @@ -37,6 +40,9 @@ const AccessibilityTab: React.FC = () => { /> setChapterGeneralSettings({ @@ -47,6 +53,9 @@ const AccessibilityTab: React.FC = () => { /> setChapterGeneralSettings({ @@ -69,6 +78,9 @@ const AccessibilityTab: React.FC = () => { Reading Enhancements setChapterGeneralSettings({ bionicReading: !bionicReading }) diff --git a/src/screens/settings/SettingsReaderScreen/tabs/AdvancedTab.tsx b/src/screens/settings/SettingsReaderScreen/tabs/AdvancedTab.tsx index 5f416bf76..9704b1aff 100644 --- a/src/screens/settings/SettingsReaderScreen/tabs/AdvancedTab.tsx +++ b/src/screens/settings/SettingsReaderScreen/tabs/AdvancedTab.tsx @@ -8,12 +8,12 @@ import { Platform, } from 'react-native'; import { BottomSheetScrollView } from '@gorhom/bottom-sheet'; -import { TextInput, Portal } from 'react-native-paper'; +import { TextInput } from 'react-native-paper'; import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; import * as DocumentPicker from 'expo-document-picker'; -import NativeFile from '@specs/NativeFile'; +import NativeFile from '@modules/native-file'; import { useTheme, useChapterReaderSettings } from '@hooks/persisted'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { ThemeColors } from '@theme/types'; import { Button, ConfirmationDialog } from '@components/index'; import { showToast } from '@utils/showToast'; @@ -77,7 +77,7 @@ if (title) { } else { setChapterReaderSettings({ customJS: jsValue }); } - showToast('Saved'); + showToast(getString('common.saved')); }; const handleReset = () => { @@ -111,12 +111,12 @@ if (title) { if (file.assets) { const tempPath = - NativeFile.getConstants().ExternalCachesDirectoryPath + + NativeFile.ExternalCachesDirectoryPath + '/imported_custom.' + activeCodeTab; - NativeFile.copyFile(file.assets[0].uri, tempPath); - const content = NativeFile.readFile(tempPath); - NativeFile.unlink(tempPath); + await NativeFile.copyFile(file.assets[0].uri, tempPath); + const content = await NativeFile.readFile(tempPath); + await NativeFile.unlink(tempPath); if (activeCodeTab === 'css') { setCssValue(content.trim()); @@ -125,7 +125,7 @@ if (title) { setJsValue(content.trim()); setChapterReaderSettings({ customJS: content.trim() }); } - showToast('Imported'); + showToast(getString('common.imported')); } } catch (error: any) { showToast(error.message); @@ -146,10 +146,10 @@ if (title) { {/* Tab Selector */} setActiveCodeTab('css')} android_ripple={{ color: theme.rippleColor }} > @@ -175,13 +175,21 @@ if (title) { > CSS + {activeCodeTab === 'css' ? ( + + ) : null} setActiveCodeTab('js')} android_ripple={{ color: theme.rippleColor }} > @@ -207,6 +215,14 @@ if (title) { > JS + {activeCodeTab === 'js' ? ( + + ) : null} @@ -258,7 +274,11 @@ if (title) { {/* Action Buttons */} - - - - - + { + removeTracker(logoutTrackerName as any); + hideModal(); + }} + onDismiss={hideModal} + /> +
    @@ -376,20 +352,6 @@ const styles = StyleSheet.create({ screenPadding: { paddingVertical: 8, }, - modalText: { - fontSize: 18, - }, - modalButtonRow: { - flexDirection: 'row', - justifyContent: 'flex-end', - }, - modalButton: { - marginTop: 30, - }, - modalButtonLabel: { - letterSpacing: 0, - textTransform: 'none', - }, logoContainer: { paddingLeft: 16, justifyContent: 'center', diff --git a/src/screens/settings/components/ConnectionModal.tsx b/src/screens/settings/components/ConnectionModal.tsx index 845a1805c..b4d955d7b 100644 --- a/src/screens/settings/components/ConnectionModal.tsx +++ b/src/screens/settings/components/ConnectionModal.tsx @@ -1,8 +1,7 @@ -import { Button, Modal } from '@components'; -import { getString } from '@strings/translations'; +import { Dialog } from '@components'; +import { getString } from '@i18n/translations'; import { ThemeColors } from '@theme/types'; import React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; import { TextInput } from 'react-native-paper'; interface ConnectionModalProps { @@ -29,50 +28,42 @@ const ConnectionModal: React.FC = ({ setPort, }) => { return ( - - - {title} - - - - - - - - - + + + + + + ); }; export default TrackerLoginDialog; const styles = StyleSheet.create({ - container: { - padding: 8, - }, - title: { - fontSize: 24, - marginBottom: 24, - fontWeight: '500', - }, input: { height: 48, borderWidth: 1, @@ -152,16 +133,4 @@ const styles = StyleSheet.create({ marginBottom: 16, marginTop: -8, }, - buttonRow: { - flexDirection: 'row', - justifyContent: 'flex-end', - marginTop: 8, - }, - button: { - marginLeft: 8, - }, - buttonLabel: { - letterSpacing: 0, - textTransform: 'none', - }, }); diff --git a/src/screens/updates/UpdatesScreen.tsx b/src/screens/updates/UpdatesScreen.tsx index 3987ee20a..79f02bb06 100644 --- a/src/screens/updates/UpdatesScreen.tsx +++ b/src/screens/updates/UpdatesScreen.tsx @@ -1,4 +1,4 @@ -import React, { memo, Suspense, useEffect } from 'react'; +import React, { memo, useCallback, useEffect, useMemo } from 'react'; import dayjs from 'dayjs'; import { RefreshControl, SectionList, StyleSheet, Text } from 'react-native'; @@ -10,20 +10,23 @@ import { } from '@components'; import { useSearch } from '@hooks'; -import { useTheme } from '@hooks/persisted'; -import { getString } from '@strings/translations'; +import { useAppSettings, useTheme } from '@hooks/persisted'; +import { getString } from '@i18n/translations'; import { ThemeColors } from '@theme/types'; -import UpdatesSkeletonLoading from './components/UpdatesSkeletonLoading'; -import UpdateNovelCard from './components/UpdateNovelCard'; +import UpdateNovelChapterGroup from './components/UpdateNovelChapterGroup'; import { deleteChapter } from '@database/queries/ChapterQueries'; import { showToast } from '@utils/showToast'; -import ServiceManager from '@services/ServiceManager'; +import { backgroundTasks } from '@services/backgroundTasks'; import { UpdateScreenProps } from '@navigators/types'; import { UpdateOverview } from '@database/types'; import { useUpdateContext } from '@components/Context/UpdateContext'; +import { formatDate } from '@utils/dateFormat'; +import { useFocusEffect } from '@react-navigation/native'; const UpdatesScreen = ({ navigation }: UpdateScreenProps) => { const theme = useTheme(); + const { dateFormat = 'default', relativeTimestamps = true } = + useAppSettings(); const { updatesOverview, getUpdates, @@ -36,6 +39,40 @@ const UpdatesScreen = ({ navigation }: UpdateScreenProps) => { setSearchText(text); }; + useFocusEffect( + useCallback(() => { + void getUpdates(); + }, [getUpdates]), + ); + + const sections = useMemo( + () => + updatesOverview + .filter(update => + searchText + ? update.novelName.toLowerCase().includes(searchText.toLowerCase()) + : true, + ) + .reduce( + ( + groups: { data: UpdateOverview[]; date: string }[], + update: UpdateOverview, + ) => { + if ( + groups.length === 0 || + groups[groups.length - 1]?.date !== update.updateDate + ) { + groups.push({ data: [update], date: update.updateDate }); + return groups; + } + groups[groups.length - 1]?.data.push(update); + return groups; + }, + [], + ), + [searchText, updatesOverview], + ); + useEffect( () => navigation.addListener('tabPress', e => { @@ -62,8 +99,7 @@ const UpdatesScreen = ({ navigation }: UpdateScreenProps) => { rightIcons={[ { iconName: 'reload', - onPress: () => - ServiceManager.manager.addTask({ name: 'UPDATE_LIBRARY' }), + onPress: () => backgroundTasks.enqueue({ name: 'UPDATE_LIBRARY' }), }, ]} /> @@ -71,7 +107,6 @@ const UpdatesScreen = ({ navigation }: UpdateScreenProps) => { ) : ( @@ -80,51 +115,32 @@ const UpdatesScreen = ({ navigation }: UpdateScreenProps) => { contentContainerStyle={styles.listContainer} renderSectionHeader={({ section: { date } }) => ( - {dayjs(date).calendar()} + {formatDate(date, dateFormat, relativeTimestamps)} )} - sections={updatesOverview - .filter(v => - searchText - ? v.novelName.toLowerCase().includes(searchText.toLowerCase()) - : true, - ) - .reduce( - ( - acc: { data: UpdateOverview[]; date: string }[], - cur: UpdateOverview, - ) => { - if (acc.length === 0 || acc.at(-1)?.date !== cur.updateDate) { - acc.push({ data: [cur], date: cur.updateDate }); - return acc; - } - acc.at(-1)?.data.push(cur); - return acc; - }, - [], - )} - keyExtractor={item => 'updatedGroup' + item.novelId} + sections={sections} + keyExtractor={item => + `updatedGroup-${item.novelId}-${item.updateDate}-${item.updatesPerDay}` + } renderItem={({ item }) => ( - }> - { - deleteChapter( - chapter.pluginId, - chapter.novelId, - chapter.id, - ).then(() => { - showToast( - getString('common.deleted', { - name: chapter.name, - }), - ); - getUpdates(); - }); - }} - chapterListInfo={item} - descriptionText={getString('updatesScreen.updatesLower')} - /> - + { + deleteChapter( + chapter.pluginId, + chapter.novelId, + chapter.id, + ).then(() => { + showToast( + getString('common.deleted', { + name: chapter.name, + }), + ); + getUpdates(); + }); + }} + overview={item} + chapterCountLabel={getString('updatesScreen.updatesLower')} + /> )} ListEmptyComponent={ { - ServiceManager.manager.addTask({ name: 'UPDATE_LIBRARY' }) + backgroundTasks.enqueue({ name: 'UPDATE_LIBRARY' }) } colors={[theme.onPrimary]} progressBackgroundColor={theme.primary} diff --git a/src/screens/updates/__tests__/UpdatesScreen.test.tsx b/src/screens/updates/__tests__/UpdatesScreen.test.tsx new file mode 100644 index 000000000..6fa6600ed --- /dev/null +++ b/src/screens/updates/__tests__/UpdatesScreen.test.tsx @@ -0,0 +1,93 @@ +import { render } from '@testing-library/react-native'; +import { useFocusEffect } from '@react-navigation/native'; + +import UpdatesScreen from '../UpdatesScreen'; + +const mockGetUpdates = jest.fn(); + +jest.mock('@components', () => { + const ReactModule = require('react'); + + return { + EmptyView: () => null, + ErrorScreenV2: () => null, + SearchbarV2: () => null, + SafeAreaView: ({ children }: { children: React.ReactNode }) => + ReactModule.createElement(ReactModule.Fragment, null, children), + }; +}); + +jest.mock('@components/Context/UpdateContext', () => ({ + useUpdateContext: () => ({ + updatesOverview: [], + getUpdates: mockGetUpdates, + lastUpdateTime: undefined, + showLastUpdateTime: true, + error: '', + }), +})); + +jest.mock('@hooks', () => ({ + useSearch: () => ({ + searchText: '', + setSearchText: jest.fn(), + clearSearchbar: jest.fn(), + }), +})); + +jest.mock('@hooks/persisted', () => ({ + useAppSettings: () => ({}), + useTheme: () => ({ + background: '#000000', + onPrimary: '#ffffff', + onSurface: '#ffffff', + primary: '#000000', + }), +})); + +jest.mock('@i18n/translations', () => ({ + getString: (key: string) => key, +})); + +jest.mock('@database/queries/ChapterQueries', () => ({ + deleteChapter: jest.fn(), +})); + +jest.mock('@services/backgroundTasks', () => ({ + backgroundTasks: { enqueue: jest.fn() }, +})); + +jest.mock('@utils/dateFormat', () => ({ + formatDate: (date: string) => date, +})); + +jest.mock('../components/UpdateNovelChapterGroup', () => () => null); + +const mockUseFocusEffect = useFocusEffect as jest.MockedFunction< + typeof useFocusEffect +>; + +describe('UpdatesScreen', () => { + beforeEach(() => { + mockGetUpdates.mockReset(); + }); + + it('reloads the update overview when the Updates screen gains focus', () => { + const navigation = { + addListener: jest.fn(() => jest.fn()), + isFocused: jest.fn(() => false), + navigate: jest.fn(), + }; + + render( + , + ); + + const focusCallback = mockUseFocusEffect.mock.calls.at(-1)?.[0]; + expect(focusCallback).toBeDefined(); + + focusCallback?.(); + + expect(mockGetUpdates).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/screens/updates/components/UpdateNovelCard.tsx b/src/screens/updates/components/UpdateNovelCard.tsx deleted file mode 100644 index fc2dbc664..000000000 --- a/src/screens/updates/components/UpdateNovelCard.tsx +++ /dev/null @@ -1,241 +0,0 @@ -import { Pressable, StyleSheet, View, Image } from 'react-native'; -import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; - -import { - ChapterInfo, - DownloadedChapter, - NovelInfo, - Update, - UpdateOverview, -} from '@database/types'; -import { List } from 'react-native-paper'; -import { NavigationProp, useNavigation } from '@react-navigation/native'; -import ChapterItem from '@screens/novel/components/ChapterItem'; -import { useDownload, useTheme, useUpdates } from '@hooks/persisted'; -import { RootStackParamList } from '@navigators/types'; -import { FlatList } from 'react-native-gesture-handler'; -import { defaultCover } from '@plugins/helpers/constants'; -import { ThemeColors } from '@theme/types'; - -type UpdateCardProps = { - onlyDownloadedChapters?: boolean; - descriptionText: string; - deleteChapter: (chapter: Update | DownloadedChapter) => void; -} & ( - | { chapterList: Update[] | DownloadedChapter[]; chapterListInfo?: undefined } - | { - chapterListInfo: UpdateOverview; - chapterList?: undefined; - } -); - -const UpdateNovelCard: React.FC = ({ - onlyDownloadedChapters = false, - chapterList: chapterListRaw, - chapterListInfo: chapterListInfoRaw, - descriptionText, - deleteChapter, -}) => { - const { navigate } = useNavigation>(); - const { downloadChapter, downloadingChapterIds } = useDownload(); - const { getDetailedUpdates, isLoading } = useUpdates(); - const [chapterList, setChapterList] = useState< - Update[] | DownloadedChapter[] - >(chapterListRaw ?? []); - - const chapterListInfo = chapterListInfoRaw ?? { - novelId: chapterList![0]?.novelId, - novelName: chapterList![0]?.novelName, - updateDate: chapterList![0]?.updatedTime ?? '', - updatesPerDay: chapterList?.length, - novelCover: chapterList![0]?.novelCover ?? '', - }; - - const theme = useTheme(); - - const updateList = useCallback(async () => { - getDetailedUpdates(chapterListInfo.novelId, onlyDownloadedChapters).then( - res => { - if (res.length) { - setChapterList(res); - } - }, - ); - }, [chapterListInfo.novelId, getDetailedUpdates, onlyDownloadedChapters]); - useEffect(() => { - updateList(); - }, [updateList]); - - const handleDownloadChapter = useCallback( - (chapter: ChapterInfo) => { - const update = chapter as Update | DownloadedChapter; - if (chapterListInfo.updatesPerDay) { - downloadChapter( - { - id: update?.novelId, - pluginId: update.pluginId, - name: update.novelName, - } as NovelInfo, - chapter, - ); - } - }, - [chapterListInfo.updatesPerDay, downloadChapter], - ); - - const handleDeleteChapter = useCallback( - (chapter: ChapterInfo) => { - deleteChapter(chapter as Update | DownloadedChapter); - }, - [deleteChapter], - ); - - const navigateToChapter = useCallback( - (chapter: ChapterInfo) => { - const { novelPath, pluginId, novelName } = chapter as - | Update - | DownloadedChapter; - navigate('ReaderStack', { - screen: 'Chapter', - params: { - novel: { - path: novelPath, - pluginId: pluginId, - name: novelName, - } as NovelInfo, - chapter: chapter, - }, - }); - }, - [navigate], - ); - - const navigateToNovel = useCallback(() => { - if (chapterListInfo.updatesPerDay) { - navigate('ReaderStack', { - screen: 'Novel', - params: { - pluginId: chapterList[0].pluginId, - path: chapterList[0].novelPath, - cover: chapterList[0].novelCover ?? undefined, - name: chapterList[0].novelName, - }, - }); - } - }, [chapterList, chapterListInfo.updatesPerDay, navigate]); - - const styles = useMemo(() => createStyles(theme), [theme]); - - const Cover = useCallback(() => { - const uri = chapterListInfo.novelCover || defaultCover; - return ( - - - - ); - }, [ - chapterListInfo.novelCover, - navigateToNovel, - styles.alignSelf, - styles.cover, - ]); - - const coverElement = useMemo( - () => ( - - - - ), - [Cover, styles.novelCover], - ); - - if (chapterListInfo.updatesPerDay > 1) { - return ( - - {chapterList.length > 0 ? ( - 'update' + it.id} - extraData={[chapterList, isLoading]} - style={styles.chapterList} - renderItem={({ item }) => { - return ( - - ); - }} - scrollEnabled={false} - /> - ) : ( - <> - )} - - ); - } else if (chapterListInfo.updatesPerDay > 0 && chapterList[0]) { - return ( - - ); - } - return null; -}; - -export default memo(UpdateNovelCard); - -function createStyles(theme: ThemeColors) { - return StyleSheet.create({ - alignSelf: { alignSelf: 'center' }, - chapterList: { - marginStart: -40, - }, - container: { - alignItems: 'center', - justifyContent: 'space-between', - }, - cover: { - borderRadius: 4, - height: 40, - width: 40, - }, - description: { fontSize: 12 }, - novelCover: { - marginEnd: 16, - }, - padding: { - paddingHorizontal: 16, - paddingVertical: 2, - }, - title: { color: theme.onSurface, fontSize: 14 }, - }); -} diff --git a/src/screens/updates/components/UpdateNovelChapterGroup.tsx b/src/screens/updates/components/UpdateNovelChapterGroup.tsx new file mode 100644 index 000000000..719b5d4d1 --- /dev/null +++ b/src/screens/updates/components/UpdateNovelChapterGroup.tsx @@ -0,0 +1,112 @@ +import React, { + memo, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; + +import { Update, UpdateOverview } from '@database/types'; +import { useDetailedUpdates } from '@hooks/persisted/useUpdates'; +import NovelChapterGroup, { + GroupedNovelChapter, +} from '@screens/novel/components/NovelChapterGroup'; + +interface UpdateNovelChapterGroupProps { + chapterCountLabel: string; + onDeleteChapter: (chapter: GroupedNovelChapter) => void; + overview: UpdateOverview; +} + +const ReactiveUpdateChapters = ({ + novelId, + onChange, + updateDate, +}: { + novelId: number; + onChange: (chapters: Update[]) => void; + updateDate: string; +}) => { + const chapters = useDetailedUpdates(novelId, false, updateDate); + + useEffect(() => onChange(chapters), [chapters, onChange]); + + return null; +}; + +const UpdateNovelChapterGroup: React.FC = ({ + chapterCountLabel, + onDeleteChapter, + overview, +}) => { + const [chapters, setChapters] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [isSubscribed, setIsSubscribed] = useState(false); + const loadStatus = useRef<'idle' | 'loading' | 'loaded'>('idle'); + + const novel = useMemo( + () => ({ + id: overview.novelId, + pluginId: overview.pluginId, + name: overview.novelName, + path: overview.novelPath, + cover: overview.novelCover, + inLibrary: overview.inLibrary, + }), + [overview], + ); + + const loadChapters = useCallback(() => { + if (loadStatus.current !== 'idle') { + return; + } + + loadStatus.current = 'loading'; + setIsLoading(true); + setIsSubscribed(true); + }, []); + + const updateChapters = useCallback((nextChapters: Update[]) => { + setChapters(nextChapters); + setIsLoading(false); + loadStatus.current = 'loaded'; + }, []); + + useEffect(() => { + let loadTimer: ReturnType | undefined; + + if (overview.updatesPerDay === 1) { + loadTimer = setTimeout(() => void loadChapters(), 0); + } + + return () => { + if (loadTimer) { + clearTimeout(loadTimer); + } + }; + }, [loadChapters, overview.updatesPerDay]); + + return ( + <> + + {isSubscribed ? ( + + ) : null} + + ); +}; + +export default memo(UpdateNovelChapterGroup); diff --git a/src/screens/updates/components/UpdatesSkeletonLoading.tsx b/src/screens/updates/components/UpdatesSkeletonLoading.tsx index cf1e2de4f..be71275d0 100644 --- a/src/screens/updates/components/UpdatesSkeletonLoading.tsx +++ b/src/screens/updates/components/UpdatesSkeletonLoading.tsx @@ -1,50 +1,50 @@ import React, { memo } from 'react'; -import { StyleSheet, View } from 'react-native'; -import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder'; -import { LinearGradient } from 'expo-linear-gradient'; +import { StyleSheet, useWindowDimensions, View } from 'react-native'; import { ThemeColors } from '@theme/types'; import useLoadingColors from '@utils/useLoadingColors'; -import { useAppSettings } from '@hooks/persisted/index'; import Animated, { FadeIn } from 'react-native-reanimated'; +import ShimmerPlaceholder from '@components/Skeleton/ShimmerPlaceholder'; + +const SKELETON_ITEMS = Array.from({ length: 8 }); interface Props { theme: ThemeColors; } const UpdatesSkeletonLoading: React.FC = ({ theme }) => { - const { disableLoadingAnimations } = useAppSettings(); - const ShimmerPlaceHolder = createShimmerPlaceholder(LinearGradient); - - const [highlightColor, backgroundColor] = useLoadingColors(theme); + const { width } = useWindowDimensions(); + const textWidth = Math.max(80, width - 120); + const [highlightColor, backgroundColor, disableLoadingAnimations] = + useLoadingColors(theme); - const renderLoadingChapter = (item: number, index: number) => { + const renderLoadingChapter = (_: unknown, index: number) => { return ( - - + - - + - - = ({ theme }) => { ); }; - const items = []; - for (let index = 0; index < Math.random() * 8 + 4; index++) { - items.push(0); - } - return ( - - {items.map(renderLoadingChapter)} + + {SKELETON_ITEMS.map(renderLoadingChapter)} ); }; @@ -102,6 +100,10 @@ const styles = StyleSheet.create({ marginBottom: 2, marginTop: 5, }, + textCtn: { + flex: 1, + overflow: 'hidden', + }, }); export default memo(UpdatesSkeletonLoading); diff --git a/src/screens/updates/components/__tests__/UpdateNovelChapterGroup.test.tsx b/src/screens/updates/components/__tests__/UpdateNovelChapterGroup.test.tsx new file mode 100644 index 000000000..cb8ca12f4 --- /dev/null +++ b/src/screens/updates/components/__tests__/UpdateNovelChapterGroup.test.tsx @@ -0,0 +1,149 @@ +import { + act, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react-native'; + +import { Update, UpdateOverview } from '@database/types'; +import UpdateNovelChapterGroup from '../UpdateNovelChapterGroup'; + +const mockUseDetailedUpdates = jest.fn(); +const mockNovelChapterGroup = jest.fn(); + +jest.mock('@hooks/persisted/useUpdates', () => ({ + useDetailedUpdates: (...args: unknown[]) => mockUseDetailedUpdates(...args), +})); + +jest.mock('@screens/novel/components/NovelChapterGroup', () => { + const ReactModule = jest.requireActual('react'); + const { Pressable } = + jest.requireActual('react-native'); + + return { + __esModule: true, + default: (props: { onExpand?: () => void }) => { + mockNovelChapterGroup(props); + return ReactModule.createElement(Pressable, { + onPress: props.onExpand, + testID: 'novel-chapter-group', + }); + }, + }; +}); + +const overview: UpdateOverview = { + inLibrary: true, + novelId: 42, + pluginId: 'source-id', + novelName: 'Example Novel', + novelPath: '/example-novel', + novelCover: null, + updateDate: '2026-07-24', + updatesPerDay: 2, +}; + +describe('UpdateNovelChapterGroup', () => { + beforeEach(() => { + mockUseDetailedUpdates.mockReset(); + mockNovelChapterGroup.mockReset(); + mockUseDetailedUpdates.mockReturnValue([]); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('subscribes a collapsed group only on its first expansion', () => { + render( + , + ); + + expect(mockUseDetailedUpdates).not.toHaveBeenCalled(); + expect(mockNovelChapterGroup.mock.calls.at(-1)?.[0].novel).toMatchObject({ + inLibrary: true, + }); + + fireEvent.press(screen.getByTestId('novel-chapter-group')); + + expect(mockUseDetailedUpdates).toHaveBeenCalledWith( + overview.novelId, + false, + overview.updateDate, + ); + + fireEvent.press(screen.getByTestId('novel-chapter-group')); + + expect(mockUseDetailedUpdates).toHaveBeenCalledTimes(1); + }); + + it('loads a single-chapter group immediately because it has no accordion', async () => { + jest.useFakeTimers(); + + render( + , + ); + + expect(mockUseDetailedUpdates).not.toHaveBeenCalled(); + + await act(async () => { + jest.runOnlyPendingTimers(); + await Promise.resolve(); + }); + + expect(mockUseDetailedUpdates).toHaveBeenCalledTimes(1); + }); + + it('updates a loaded chapter when its reactive query changes', async () => { + const initialChapters = [ + { + id: 1, + isDownloaded: false, + name: 'Chapter 1', + } as Update, + ]; + let setReactiveChapters: + | React.Dispatch> + | undefined; + mockUseDetailedUpdates.mockImplementation(() => { + const ReactModule = jest.requireActual('react'); + const [reactiveChapters, setChapters] = + ReactModule.useState(initialChapters); + setReactiveChapters = setChapters; + return reactiveChapters; + }); + + render( + , + ); + + fireEvent.press(screen.getByTestId('novel-chapter-group')); + + expect( + mockNovelChapterGroup.mock.calls.at(-1)?.[0].chapters[0], + ).toMatchObject({ id: 1, isDownloaded: false }); + + act(() => { + setReactiveChapters?.([{ ...initialChapters[0], isDownloaded: true }]); + }); + + await waitFor(() => + expect( + mockNovelChapterGroup.mock.calls.at(-1)?.[0].chapters[0], + ).toMatchObject({ id: 1, isDownloaded: true }), + ); + }); +}); diff --git a/src/services/ServiceManager.ts b/src/services/ServiceManager.ts deleted file mode 100644 index 9246715bf..000000000 --- a/src/services/ServiceManager.ts +++ /dev/null @@ -1,475 +0,0 @@ -import BackgroundService from 'react-native-background-actions'; -import * as Notifications from 'expo-notifications'; - -import { getMMKVObject, setMMKVObject } from '@utils/mmkv/mmkv'; -import { importEpub } from './epub/import'; -import { getString } from '@strings/translations'; -import { updateLibrary } from './updates'; -import { DriveFile } from '@api/drive/types'; -import { createDriveBackup, driveRestore } from './backup/drive'; -import { - createSelfHostBackup, - SelfHostData, - selfHostRestore, -} from './backup/selfhost'; -import { createBackup, restoreBackup } from './backup/local'; -import { migrateNovel, MigrateNovelData } from './migrate/migrateNovel'; -import { downloadChapter } from './download/downloadChapter'; -import { askForPostNotificationsPermission } from '@utils/askForPostNoftificationsPermission'; - -type taskNames = - | 'IMPORT_EPUB' - | 'UPDATE_LIBRARY' - | 'DRIVE_BACKUP' - | 'DRIVE_RESTORE' - | 'SELF_HOST_BACKUP' - | 'SELF_HOST_RESTORE' - | 'LOCAL_BACKUP' - | 'LOCAL_RESTORE' - | 'MIGRATE_NOVEL' - | 'DOWNLOAD_CHAPTER'; - -export type BackgroundTask = - | { - name: 'IMPORT_EPUB'; - data: { - filename: string; - uri: string; - }; - } - | { - name: 'UPDATE_LIBRARY'; - data?: { - categoryId?: number; - categoryName?: string; - }; - } - | { name: 'DRIVE_BACKUP'; data: DriveFile } - | { name: 'DRIVE_RESTORE'; data: DriveFile } - | { name: 'SELF_HOST_BACKUP'; data: SelfHostData } - | { name: 'SELF_HOST_RESTORE'; data: SelfHostData } - | { name: 'LOCAL_BACKUP' } - | { name: 'LOCAL_RESTORE' } - | { name: 'MIGRATE_NOVEL'; data: MigrateNovelData } - | DownloadChapterTask; -export type DownloadChapterTask = { - name: 'DOWNLOAD_CHAPTER'; - data: { chapterId: number; novelName: string; chapterName: string }; -}; - -export type BackgroundTaskMetadata = { - name: string; - isRunning: boolean; - progress: number | undefined; - progressText: string | undefined; -}; - -export type QueuedBackgroundTask = { - task: BackgroundTask; - meta: BackgroundTaskMetadata; - id: string; -}; - -function makeId() { - return ( - Math.random().toString(36).substring(2, 15) + - Math.random().toString(36).substring(2, 15) - ); -} - -export default class ServiceManager { - STORE_KEY = 'APP_SERVICE'; - lastNotifUpdate = 0; - currentPendingUpdate = 0; - private static instance?: ServiceManager; - - private constructor() {} - - static get manager() { - if (!this.instance) { - this.instance = new ServiceManager(); - } - return this.instance; - } - - get isRunning() { - return BackgroundService.isRunning(); - } - - isMultiplicableTask(task: BackgroundTask) { - if (!task?.name) { - return false; - } - return ( - ['DOWNLOAD_CHAPTER', 'IMPORT_EPUB', 'MIGRATE_NOVEL'] as Array< - BackgroundTask['name'] - > - ).includes(task.name); - } - - async start() { - if (!this.isRunning) { - const notificationsAllowed = await askForPostNotificationsPermission(); - if (!notificationsAllowed) return; - BackgroundService.start(ServiceManager.launch, { - taskName: 'app_services', - taskTitle: 'App Service', - taskDesc: getString('common.preparing'), - taskIcon: { name: 'notification_icon', type: 'drawable' }, - color: '#00adb5', - linkingURI: 'lnreader://', - }).catch(error => { - Notifications.scheduleNotificationAsync({ - content: { - title: getString('backupScreen.drive.backupInterruped'), - body: error.message, - }, - trigger: null, - }); - BackgroundService.stop(); - }); - } - } - - setMeta( - transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, - ) { - const taskList = [...this.getTaskList()]; - if (taskList.length === 0 || !taskList[0]?.meta) { - return; - } - - taskList[0] = { - ...taskList[0], - meta: transformer(taskList[0].meta), - }; - - if ( - taskList[0].meta?.isRunning && - taskList[0].task?.name !== 'DOWNLOAD_CHAPTER' - ) { - const now = Date.now(); - if (now - this.lastNotifUpdate > 1000) { - const delay = 1000 - now - this.lastNotifUpdate; - const id = ++this.currentPendingUpdate; - setTimeout(() => { - if (this.currentPendingUpdate !== id) { - return; - } - BackgroundService.updateNotification({ - taskTitle: taskList[0].meta?.name || 'Unknown Task', - taskDesc: taskList[0].meta?.progressText ?? '', - progressBar: { - indeterminate: taskList[0].meta?.progress === undefined, - value: (taskList[0].meta?.progress || 0) * 100, - max: 100, - }, - }); - }, delay); - } else { - this.lastNotifUpdate = now; - BackgroundService.updateNotification({ - taskTitle: taskList[0].meta?.name || 'Unknown Task', - taskDesc: taskList[0].meta?.progressText ?? '', - progressBar: { - indeterminate: taskList[0].meta?.progress === undefined, - value: (taskList[0].meta?.progress || 0) * 100, - max: 100, - }, - }); - } - } - - setMMKVObject(this.STORE_KEY, taskList); - } - - //gets the progress bar for download chapters notification - getProgressForNotification( - currentTask: QueuedBackgroundTask, - startingTasks: QueuedBackgroundTask[], - ) { - let i = null; - let count = 0; - for (const task of startingTasks) { - if ( - task.task?.name === 'DOWNLOAD_CHAPTER' && - task.meta?.name === currentTask.meta?.name - ) { - if (task.id === currentTask.id) { - i = count; - } - count++; - } else { - if (i !== null) { - break; - } - count = 0; - } - } - if (i === null) { - return null; - } - return (i / count) * 100; - } - - async executeTask( - task: QueuedBackgroundTask, - startingTasks: QueuedBackgroundTask[], - ) { - // Safety check for old format tasks - if (!task?.task?.name) { - return; - } - - const progress = - task.task.name === 'DOWNLOAD_CHAPTER' - ? this.getProgressForNotification(task, startingTasks) - : null; - await BackgroundService.updateNotification({ - taskTitle: task.meta?.name || 'Unknown Task', - taskDesc: task.meta?.progressText ?? '', - progressBar: { - indeterminate: progress === null, - max: 100, - value: progress == null ? 0 : progress, - }, - }); - this.lastNotifUpdate = Date.now(); - this.currentPendingUpdate = 0; - - switch (task.task.name) { - case 'IMPORT_EPUB': - return importEpub(task.task.data, this.setMeta.bind(this)); - case 'UPDATE_LIBRARY': - return updateLibrary(task.task.data || {}, this.setMeta.bind(this)); - case 'DRIVE_BACKUP': - return createDriveBackup(task.task.data, this.setMeta.bind(this)); - case 'DRIVE_RESTORE': - return driveRestore(task.task.data, this.setMeta.bind(this)); - case 'SELF_HOST_BACKUP': - return createSelfHostBackup(task.task.data, this.setMeta.bind(this)); - case 'SELF_HOST_RESTORE': - return selfHostRestore(task.task.data, this.setMeta.bind(this)); - case 'LOCAL_BACKUP': - return createBackup(this.setMeta.bind(this)); - case 'LOCAL_RESTORE': - return restoreBackup(this.setMeta.bind(this)); - case 'MIGRATE_NOVEL': - return migrateNovel(task.task.data, this.setMeta.bind(this)); - case 'DOWNLOAD_CHAPTER': - return downloadChapter(task.task.data, this.setMeta.bind(this)); - } - } - - static async launch() { - // retrieve class instance because this is running in different context - const manager = ServiceManager.manager; - const doneTasks: Record = { - 'IMPORT_EPUB': 0, - 'UPDATE_LIBRARY': 0, - 'DRIVE_BACKUP': 0, - 'DRIVE_RESTORE': 0, - 'SELF_HOST_BACKUP': 0, - 'SELF_HOST_RESTORE': 0, - 'LOCAL_BACKUP': 0, - 'LOCAL_RESTORE': 0, - 'MIGRATE_NOVEL': 0, - 'DOWNLOAD_CHAPTER': 0, - }; - const startingTasks = manager.getTaskList(); - const tasksSet = new Set(startingTasks.map(t => t.id)); - while (BackgroundService.isRunning()) { - const currentTasks = manager.getTaskList(); - const currentTask = currentTasks[0]; - if (!currentTask) { - break; - } - - //Add any newly queued tasks to the starting tasks list - const newtasks = currentTasks.filter(t => !tasksSet.has(t.id)); - startingTasks.push(...newtasks); - newtasks.forEach(t => tasksSet.add(t.id)); - - try { - // Safety check - getTaskList() should already handle conversion, but double-check - if (!currentTask?.task?.name) { - // Skip invalid tasks - setMMKVObject(manager.STORE_KEY, manager.getTaskList().slice(1)); - continue; - } - await manager.executeTask(currentTask, startingTasks); - doneTasks[currentTask.task.name] += 1; - } catch (error: any) { - await Notifications.scheduleNotificationAsync({ - content: { - title: currentTask.meta?.name || 'Task Error', - body: error?.message || String(error), - }, - trigger: null, - }); - } finally { - setMMKVObject(manager.STORE_KEY, manager.getTaskList().slice(1)); - } - } - - if (manager.getTaskList().length === 0) { - await Notifications.scheduleNotificationAsync({ - content: { - title: getString('common.done'), - body: Object.keys(doneTasks) - .filter(key => doneTasks[key as BackgroundTask['name']] > 0) - .map( - key => - `${getString(`notifications.${key as taskNames}`)}: ${ - doneTasks[key as BackgroundTask['name']] - }`, - ) - .join('\n'), - }, - trigger: null, - }); - } - } - - getTaskName(task: BackgroundTask) { - if (!task?.name) { - return 'Unknown Task'; - } - switch (task.name) { - case 'DOWNLOAD_CHAPTER': - return `${getString('notifications.DOWNLOAD_CHAPTER')}: ${ - task.data?.novelName || '' - }`; - case 'IMPORT_EPUB': - return `${getString('notifications.IMPORT_EPUB')}: ${ - task.data?.filename || '' - }`; - case 'MIGRATE_NOVEL': - return `${getString('notifications.MIGRATE_NOVEL')}: ${ - task.data?.fromNovel?.name || '' - }`; - case 'UPDATE_LIBRARY': - if (task.data !== undefined) { - return `${getString('notifications.UPDATE_LIBRARY')}: ${ - task.data?.categoryName || '' - }`; - } - return getString('notifications.UPDATE_LIBRARY'); - case 'DRIVE_BACKUP': - return getString('notifications.DRIVE_BACKUP'); - case 'DRIVE_RESTORE': - return getString('notifications.DRIVE_RESTORE'); - case 'SELF_HOST_BACKUP': - return getString('notifications.SELF_HOST_BACKUP'); - case 'SELF_HOST_RESTORE': - return getString('notifications.SELF_HOST_RESTORE'); - case 'LOCAL_BACKUP': - return getString('notifications.LOCAL_BACKUP'); - case 'LOCAL_RESTORE': - return getString('notifications.LOCAL_RESTORE'); - default: - return 'Unknown Task'; - } - } - - getTaskList() { - const tasks = getMMKVObject>(this.STORE_KEY) || []; - - const convertedTasks = tasks - .map(task => { - if (task?.task && task?.meta && task?.id) { - return task as QueuedBackgroundTask; - } - - if (task?.name && !task?.task) { - const backgroundTask = task as BackgroundTask; - return { - task: backgroundTask, - meta: { - name: this.getTaskName(backgroundTask), - isRunning: false, - progress: undefined, - progressText: - backgroundTask.name === 'DOWNLOAD_CHAPTER' - ? (backgroundTask as DownloadChapterTask).data?.chapterName - : undefined, - }, - id: makeId(), - } as QueuedBackgroundTask; - } - - return null; - }) - .filter((task): task is QueuedBackgroundTask => task !== null); - - const hasOldFormat = tasks.some(task => task?.name && !task?.task); - - if (hasOldFormat) { - setMMKVObject(this.STORE_KEY, convertedTasks); - } - - return convertedTasks; - } - - addTask(tasks: BackgroundTask | BackgroundTask[]) { - const currentTasks = this.getTaskList(); - - const addableTasks = (Array.isArray(tasks) ? tasks : [tasks]).filter( - task => - this.isMultiplicableTask(task) || - !currentTasks.some(_t => _t.task?.name === task.name), - ); - if (addableTasks.length) { - const newTasks: QueuedBackgroundTask[] = addableTasks.map(task => ({ - task, - meta: { - name: this.getTaskName(task), - isRunning: false, - progress: undefined, - progressText: - task.name === 'DOWNLOAD_CHAPTER' - ? task.data?.chapterName - : undefined, - }, - id: makeId(), - })); - - setMMKVObject(this.STORE_KEY, currentTasks.concat(newTasks)); - this.start(); - } - } - - removeTasksByName(name: BackgroundTask['name']) { - const taskList = this.getTaskList(); - if (taskList[0]?.task?.name === name) { - this.pause(); - setMMKVObject( - this.STORE_KEY, - taskList.filter(t => t.task?.name !== name), - ); - this.resume(); - } else { - setMMKVObject( - this.STORE_KEY, - taskList.filter(t => t.task?.name !== name), - ); - } - } - - clearTaskList() { - setMMKVObject(this.STORE_KEY, []); - } - - pause() { - BackgroundService.stop(); - } - - resume() { - this.start(); - } - - stop() { - BackgroundService.stop(); - this.clearTaskList(); - } -} diff --git a/src/services/Trackers/__tests__/myAnimeList.test.ts b/src/services/Trackers/__tests__/myAnimeList.test.ts new file mode 100644 index 000000000..3de303d3e --- /dev/null +++ b/src/services/Trackers/__tests__/myAnimeList.test.ts @@ -0,0 +1,76 @@ +import { myAnimeListTracker } from '../myAnimeList'; + +jest.mock('expo-linking', () => ({ + createURL: jest.fn(() => 'lnreader://tracker/MAL'), +})); +jest.mock('expo-web-browser', () => ({ + openAuthSessionAsync: jest.fn(), +})); + +const auth = { + accessToken: 'access-token', + expiresAt: new Date(), +}; + +describe('MyAnimeList tracker', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('creates a default list entry when the manga is not tracked yet', async () => { + const fetchMock = jest + .spyOn(global, 'fetch') + .mockResolvedValueOnce({ + json: jest.fn().mockResolvedValue({ id: 123, num_chapters: 10 }), + } as unknown as Response) + .mockResolvedValueOnce({ + json: jest.fn().mockResolvedValue({ + status: 'reading', + num_chapters_read: 0, + score: 0, + }), + } as unknown as Response); + + await expect( + myAnimeListTracker.getUserListEntry(123, auth), + ).resolves.toEqual({ + status: 'CURRENT', + progress: 0, + score: 0, + totalChapters: 10, + }); + + expect(fetchMock).toHaveBeenNthCalledWith( + 2, + 'https://api.myanimelist.net/v2/manga/123/my_list_status', + expect.objectContaining({ + method: 'PUT', + body: 'status=reading&is_rereading=false&num_chapters_read=0&score=0', + }), + ); + }); + + it('returns an existing list entry without updating it', async () => { + const fetchMock = jest.spyOn(global, 'fetch').mockResolvedValueOnce({ + json: jest.fn().mockResolvedValue({ + num_chapters: 10, + my_list_status: { + status: 'plan_to_read', + num_chapters_read: 0, + score: 0, + }, + }), + } as unknown as Response); + + await expect( + myAnimeListTracker.getUserListEntry(123, auth), + ).resolves.toEqual({ + status: 'PLANNING', + progress: 0, + score: 0, + totalChapters: 10, + }); + + expect(fetchMock).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/services/Trackers/aniList.ts b/src/services/Trackers/aniList.ts index 50b72d43d..618021ee5 100644 --- a/src/services/Trackers/aniList.ts +++ b/src/services/Trackers/aniList.ts @@ -1,15 +1,13 @@ import * as Linking from 'expo-linking'; import * as WebBrowser from 'expo-web-browser'; -import Config from 'react-native-config'; +import { ANILIST_CLIENT_ID } from '@env'; import { AuthenticationResult, Tracker } from './index'; const apiEndpoint = 'https://graphql.anilist.co'; -const clientId = Config.ANILIST_CLIENT_ID; +const clientId = ANILIST_CLIENT_ID; const redirectUri = Linking.createURL('tracker/AL'); -const authUrl = `https://anilist.co/api/v2/oauth/authorize?client_id=${clientId}&response_type=token`; - const searchQuery = `query($search: String) { Page { media(search: $search, type: MANGA, format: NOVEL, sort: POPULARITY_DESC) { @@ -46,6 +44,11 @@ const updateListEntryMutation = `mutation($id: Int!, $status: MediaListStatus, $ export const aniListTracker = { authenticate: async () => { + if (!clientId) { + throw new Error('AniList client ID is not configured'); + } + + const authUrl = `https://anilist.co/api/v2/oauth/authorize?client_id=${clientId}&response_type=token`; const result = await WebBrowser.openAuthSessionAsync(authUrl, redirectUri); if (result.type === 'success') { const { url } = result; diff --git a/src/services/Trackers/myAnimeList.ts b/src/services/Trackers/myAnimeList.ts index 0791db13f..aa5b10cdb 100644 --- a/src/services/Trackers/myAnimeList.ts +++ b/src/services/Trackers/myAnimeList.ts @@ -1,14 +1,18 @@ import * as Linking from 'expo-linking'; import * as WebBrowser from 'expo-web-browser'; -import Config from 'react-native-config'; -import { Tracker, UserListStatus } from './index'; - -const clientId = Config.MYANIMELIST_CLIENT_ID; +import { MYANIMELIST_CLIENT_ID } from '@env'; +import { + AuthenticationResult, + Tracker, + UserListEntry, + UserListStatus, +} from './index'; + +const clientId = MYANIMELIST_CLIENT_ID; const baseOAuthUrl = 'https://myanimelist.net/v1/oauth2/authorize'; const tokenUrl = 'https://myanimelist.net/v1/oauth2/token'; const baseApiUrl = 'https://api.myanimelist.net/v2'; const challenge = pkceChallenger(); -const authUrl = `${baseOAuthUrl}?response_type=code&client_id=${clientId}&code_challenge_method=plain&code_challenge=${challenge}`; const redirectUri = Linking.createURL('tracker/MAL'); export const malToNormalized: Record = { @@ -29,6 +33,11 @@ const normalizedToMal: Record = { export const myAnimeListTracker: Tracker = { authenticate: async () => { + if (!clientId) { + throw new Error('MyAnimeList client ID is not configured'); + } + + const authUrl = `${baseOAuthUrl}?response_type=code&client_id=${clientId}&code_challenge_method=plain&code_challenge=${challenge}`; const result = await WebBrowser.openAuthSessionAsync(authUrl, redirectUri); if (result.type !== 'success') { @@ -53,7 +62,7 @@ export const myAnimeListTracker: Tracker = { body: new URLSearchParams({ client_id: clientId, grant_type: 'authorization_code', - code, + code: code, code_verifier: challenge, }).toString(), }); @@ -117,54 +126,73 @@ export const myAnimeListTracker: Tracker = { }); const data = await response.json(); - return { - status: malToNormalized[data.my_list_status?.status] || 'CURRENT', - score: data.my_list_status?.score || 0, - progress: data.my_list_status?.num_chapters_read || 0, - totalChapters: data.num_chapters, - }; - }, - updateUserListEntry: async (id, payload, auth) => { - let status = payload.status - ? normalizedToMal[payload.status] - : normalizedToMal.CURRENT; - let repeating = 'false'; - if (status.includes(';')) { - const split = status.split(';'); - status = split[0]; - repeating = split[1]; - } - - const url = `${baseApiUrl}/manga/${id}/my_list_status`; - const res = await fetch(url, { - method: 'PUT', - headers: { - Authorization: `Bearer ${auth.accessToken}`, - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: new URLSearchParams({ - status, - is_rereading: repeating, - num_chapters_read: '' + payload.progress!, - score: '' + payload.score!, - }).toString(), - }); - - const data = await res.json(); - let normalizedStatus = malToNormalized[data.status]; - if (!normalizedStatus && data.is_rereading) { - normalizedStatus = 'REPEATING'; + if (!data.my_list_status) { + const newListEntry = await updateMyAnimeListEntry( + id, + { status: 'CURRENT', progress: 0, score: 0 }, + auth, + ); + return { + ...newListEntry, + totalChapters: data.num_chapters, + }; } return { - status: normalizedStatus, - progress: data.num_chapters_read, - score: data.score, + status: malToNormalized[data.my_list_status.status] || 'CURRENT', + score: data.my_list_status.score || 0, + progress: data.my_list_status.num_chapters_read || 0, + totalChapters: data.num_chapters, }; }, + updateUserListEntry: updateMyAnimeListEntry, }; +async function updateMyAnimeListEntry( + id: number | string, + payload: Partial, + auth: AuthenticationResult, +) { + let status = payload.status + ? normalizedToMal[payload.status] + : normalizedToMal.CURRENT; + let repeating = 'false'; + if (status.includes(';')) { + const split = status.split(';'); + status = split[0]; + repeating = split[1]; + } + + const url = `${baseApiUrl}/manga/${id}/my_list_status`; + const res = await fetch(url, { + method: 'PUT', + headers: { + Authorization: `Bearer ${auth.accessToken}`, + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + status, + is_rereading: repeating, + num_chapters_read: String(payload.progress ?? 0), + score: String(payload.score ?? 0), + }).toString(), + }); + + const data = await res.json(); + + let normalizedStatus = malToNormalized[data.status]; + if (!normalizedStatus && data.is_rereading) { + normalizedStatus = 'REPEATING'; + } + + return { + status: normalizedStatus || payload.status || 'CURRENT', + progress: data.num_chapters_read ?? payload.progress ?? 0, + score: data.score ?? payload.score ?? 0, + }; +} + function pkceChallenger() { const MAX_LENGTH = 88; let code = ''; diff --git a/src/services/backgroundTasks/BackgroundTaskQueue.ts b/src/services/backgroundTasks/BackgroundTaskQueue.ts new file mode 100644 index 000000000..d1e87eda6 --- /dev/null +++ b/src/services/backgroundTasks/BackgroundTaskQueue.ts @@ -0,0 +1,257 @@ +import { DeviceEventEmitter } from 'react-native'; + +import NativeBackgroundTasks from '@modules/native-background-tasks'; +import { getString } from '@i18n/translations'; +import { askForPostNotificationsPermission } from '@utils/askForPostNoftificationsPermission'; +import { getMMKVObject, setMMKVObject } from '@utils/mmkv/mmkv'; +import { showToast } from '@utils/showToast'; +import type { + BackgroundTask, + BackgroundTaskMetadata, + QueuedBackgroundTask, +} from './contracts'; +import { executeBackgroundTask } from './executeTask'; +import { + ACTIVE_BACKGROUND_TASK_STATES, + allowsDuplicateTask, + createBackgroundTaskMetadata, + fromNativeTaskRecord, + getBackgroundTaskQueueName, + willTaskWaitInQueue, +} from './taskDefinitions'; +import { BACKGROUND_TASKS_STORE_KEY } from './constants'; + +const makeTemporaryId = () => + `pending-${Date.now()}-${Math.random().toString(36).slice(2)}`; + +export class BackgroundTaskQueue { + private interruptedTasks = new Map(); + private notificationPermissionRequest?: Promise; + + constructor() { + DeviceEventEmitter.addListener( + 'LNReaderTaskInterrupted', + ({ taskId, action }: { taskId: string; action: 'pause' | 'cancel' }) => { + this.interruptedTasks.set(taskId, action); + }, + ); + } + + get isRunning() { + return this.getSnapshot().some( + task => task.state === 'running' || task.state === 'queued', + ); + } + + getSnapshot() { + return ( + getMMKVObject(BACKGROUND_TASKS_STORE_KEY) || [] + ); + } + + async refresh() { + const records = await NativeBackgroundTasks.getTasks(); + const queue = records + .filter(record => ACTIVE_BACKGROUND_TASK_STATES.has(record.state)) + .map(fromNativeTaskRecord); + this.store(queue); + return queue; + } + + enqueue = (tasks: BackgroundTask | BackgroundTask[]) => { + for (const task of Array.isArray(tasks) ? tasks : [tasks]) { + this.enqueueOne(task, true).catch(() => undefined); + } + }; + + private enqueueSilently = (tasks: BackgroundTask | BackgroundTask[]) => { + for (const task of Array.isArray(tasks) ? tasks : [tasks]) { + this.enqueueOne(task, false).catch(() => undefined); + } + }; + + async pauseAll() { + const tasks = this.getSnapshot().filter( + task => task.state === 'running' || task.state === 'queued', + ); + tasks.forEach(task => this.interruptedTasks.set(task.id, 'pause')); + await Promise.all(tasks.map(task => NativeBackgroundTasks.pause(task.id))); + await this.refresh(); + } + + async resumeAll() { + const tasks = this.getSnapshot().filter(task => task.state === 'paused'); + const results = await Promise.allSettled( + tasks.map(task => NativeBackgroundTasks.resume(task.id)), + ); + results.forEach((result, index) => { + if (result.status === 'fulfilled') { + this.interruptedTasks.delete(tasks[index].id); + } + }); + await this.refresh(); + } + + async cancelByType(name: BackgroundTask['name']) { + const tasks = this.getSnapshot().filter(task => task.task.name === name); + tasks.forEach(task => this.interruptedTasks.set(task.id, 'cancel')); + await Promise.all(tasks.map(task => NativeBackgroundTasks.cancel(task.id))); + await this.refresh(); + } + + async cancelAll() { + const tasks = this.getSnapshot(); + tasks.forEach(task => this.interruptedTasks.set(task.id, 'cancel')); + await Promise.all(tasks.map(task => NativeBackgroundTasks.cancel(task.id))); + this.store([]); + } + + async run(taskId: string, task: BackgroundTask, checkpoint?: string) { + const queue = this.getSnapshot(); + if (!queue.some(item => item.id === taskId)) { + queue.push({ + id: taskId, + task, + state: 'running', + meta: createBackgroundTaskMetadata(task, true), + }); + this.store(queue); + } + + try { + await executeBackgroundTask( + task, + transformer => this.updateProgress(taskId, transformer), + this.enqueueSilently, + { + checkpoint, + updateCheckpoint: value => { + if (this.interruptedTasks.get(taskId) === 'cancel') { + this.throwIfInterrupted(taskId); + } + return NativeBackgroundTasks.updateCheckpoint(taskId, value); + }, + }, + ); + this.throwIfInterrupted(taskId); + const completedTask = this.getSnapshot().find(item => item.id === taskId); + await NativeBackgroundTasks.complete( + taskId, + completedTask?.meta.completionText ?? + getString('notifications.taskCompleted'), + ); + } catch (error) { + await NativeBackgroundTasks.fail( + taskId, + getString('notifications.taskFailed', { + error: error instanceof Error ? error.message : String(error), + }), + false, + ); + if (!this.interruptedTasks.has(taskId)) { + throw error; + } + } finally { + this.finishLocalExecution(taskId); + } + } + + private async enqueueOne(task: BackgroundTask, showQueuedToast: boolean) { + this.notificationPermissionRequest ??= askForPostNotificationsPermission(); + await this.notificationPermissionRequest; + + const current = this.getSnapshot(); + if ( + !allowsDuplicateTask(task) && + current.some(item => item.task.name === task.name) + ) { + return; + } + + const pending: QueuedBackgroundTask = { + id: makeTemporaryId(), + task, + state: 'queued', + meta: createBackgroundTaskMetadata(task, false), + }; + const shouldShowQueuedToast = + showQueuedToast && willTaskWaitInQueue(task, current); + this.store([...current, pending]); + + try { + const id = await NativeBackgroundTasks.enqueue( + task.name, + JSON.stringify(task), + pending.meta.name, + pending.meta.progressText || getString('common.preparing'), + allowsDuplicateTask(task), + getBackgroundTaskQueueName(task), + ); + const latest = this.getSnapshot().filter(item => item.id !== pending.id); + if (!latest.some(item => item.id === id)) { + latest.push({ ...pending, id }); + } + this.store(latest); + if (shouldShowQueuedToast) { + showToast( + getString('notifications.taskQueued', { task: pending.meta.name }), + ); + } + } catch (error) { + this.store(this.getSnapshot().filter(item => item.id !== pending.id)); + showToast( + `${pending.meta.name}: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + } + } + + private updateProgress( + taskId: string, + transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, + ) { + this.throwIfInterrupted(taskId); + + const queue = this.getSnapshot(); + const index = queue.findIndex(task => task.id === taskId); + if (index < 0) return; + const meta = transformer(queue[index].meta); + queue[index] = { ...queue[index], meta, state: 'running' }; + this.store(queue); + NativeBackgroundTasks.updateProgress( + taskId, + meta.progress ?? -1, + meta.progressText ?? '', + ).catch(() => undefined); + } + + private throwIfInterrupted(taskId: string) { + const interruption = this.interruptedTasks.get(taskId); + if (interruption) throw new Error(`Background task ${interruption}`); + } + + private finishLocalExecution(taskId: string) { + const interruption = this.interruptedTasks.get(taskId); + if (interruption === 'pause') { + this.store( + this.getSnapshot().map(item => + item.id === taskId + ? { + ...item, + state: 'paused', + meta: { ...item.meta, isRunning: false }, + } + : item, + ), + ); + } else { + this.store(this.getSnapshot().filter(item => item.id !== taskId)); + } + this.interruptedTasks.delete(taskId); + } + + private store(tasks: QueuedBackgroundTask[]) { + setMMKVObject(BACKGROUND_TASKS_STORE_KEY, tasks); + } +} diff --git a/src/services/backgroundTasks/__tests__/BackgroundTaskQueue.test.ts b/src/services/backgroundTasks/__tests__/BackgroundTaskQueue.test.ts new file mode 100644 index 000000000..17b745efa --- /dev/null +++ b/src/services/backgroundTasks/__tests__/BackgroundTaskQueue.test.ts @@ -0,0 +1,186 @@ +import NativeBackgroundTasks from '@modules/native-background-tasks'; +import { showToast } from '@utils/showToast'; +import { BackgroundTaskQueue } from '../BackgroundTaskQueue'; +import { executeBackgroundTask } from '../executeTask'; + +let mockStoredTasks: unknown[] = []; + +jest.mock('@modules/native-background-tasks', () => ({ + __esModule: true, + default: { + complete: jest.fn(), + enqueue: jest.fn().mockResolvedValue('native-task-1'), + fail: jest.fn(), + updateProgress: jest.fn().mockResolvedValue(undefined), + }, +})); + +jest.mock('../executeTask', () => ({ + executeBackgroundTask: jest.fn(), +})); + +jest.mock('@utils/askForPostNoftificationsPermission', () => ({ + askForPostNotificationsPermission: jest.fn().mockResolvedValue(true), +})); + +jest.mock('@utils/mmkv/mmkv', () => ({ + getMMKVObject: jest.fn(() => mockStoredTasks), + setMMKVObject: jest.fn((_key: string, value: unknown[]) => { + mockStoredTasks = value; + }), +})); + +jest.mock('@i18n/translations', () => ({ + getString: (key: string, options?: Record) => + key === 'notifications.taskFailed' + ? `Failed: ${options?.error}` + : key === 'notifications.taskQueued' + ? `${options?.task} queued` + : key === 'notifications.LOCAL_RESTORE' + ? 'Local restore' + : key === 'notifications.DOWNLOAD_CHAPTER' + ? 'Download' + : key === 'common.preparing' + ? 'Preparing' + : 'Completed', +})); + +jest.mock('@utils/showToast', () => ({ + showToast: jest.fn(), +})); + +const task = { + name: 'LOCAL_RESTORE' as const, + data: { sourceUri: 'file://backup.zip' }, +}; + +describe('BackgroundTaskQueue completion notifications', () => { + beforeEach(() => { + mockStoredTasks = []; + jest.clearAllMocks(); + }); + + it('passes a task-provided completion summary to the native notification', async () => { + jest + .mocked(executeBackgroundTask) + .mockImplementation(async (_task, updateProgress) => { + updateProgress(meta => ({ + ...meta, + completionText: 'Backup restored with warnings', + })); + }); + + await new BackgroundTaskQueue().run('restore-1', task); + + expect(NativeBackgroundTasks.complete).toHaveBeenCalledWith( + 'restore-1', + 'Backup restored with warnings', + ); + }); + + it('localizes failure text before handing it to the native notification', async () => { + jest + .mocked(executeBackgroundTask) + .mockRejectedValueOnce(new Error('Invalid backup')); + + await expect( + new BackgroundTaskQueue().run('restore-2', task), + ).rejects.toThrow('Invalid backup'); + expect(NativeBackgroundTasks.fail).toHaveBeenCalledWith( + 'restore-2', + 'Failed: Invalid backup', + false, + ); + }); + + it('shows queued feedback when the task must wait in its lane', async () => { + const downloadTask = { + name: 'DOWNLOAD_CHAPTER' as const, + data: { + novelName: 'Example Novel', + novelId: 42, + pluginId: 'source-a', + chapters: [{ chapterId: 7, chapterName: 'Chapter 7' }], + }, + }; + mockStoredTasks = [ + { + id: 'existing-task', + task: downloadTask, + state: 'running', + meta: { + name: 'Download: Example Novel', + isRunning: true, + progress: undefined, + progressText: undefined, + }, + }, + ]; + + new BackgroundTaskQueue().enqueue(downloadTask); + await Promise.resolve(); + await Promise.resolve(); + + expect(NativeBackgroundTasks.enqueue).toHaveBeenCalledWith( + downloadTask.name, + JSON.stringify(downloadTask), + 'Download: Example Novel', + 'Chapter 7', + true, + 'lnreader-background-task:download:source-a', + ); + expect(showToast).toHaveBeenCalledWith('Download: Example Novel queued'); + }); + + it('does not show queued feedback when the task can start immediately', async () => { + new BackgroundTaskQueue().enqueue(task); + await Promise.resolve(); + await Promise.resolve(); + + expect(NativeBackgroundTasks.enqueue).toHaveBeenCalled(); + expect(showToast).not.toHaveBeenCalled(); + }); + + it('keeps progress updates scoped to concurrently running tasks', async () => { + const firstTask = { + name: 'LOCAL_RESTORE' as const, + data: { sourceUri: 'file://first.zip' }, + }; + const secondTask = { + name: 'LOCAL_RESTORE' as const, + data: { sourceUri: 'file://second.zip' }, + }; + const resolvers: (() => void)[] = []; + + jest + .mocked(executeBackgroundTask) + .mockImplementation(async (runningTask, updateProgress) => { + if (runningTask.name !== 'LOCAL_RESTORE') { + throw new Error('Unexpected task type'); + } + updateProgress(meta => ({ + ...meta, + progressText: runningTask.data.sourceUri, + })); + await new Promise(resolve => resolvers.push(resolve)); + }); + + const firstRun = new BackgroundTaskQueue().run('first', firstTask); + const secondRun = new BackgroundTaskQueue().run('second', secondTask); + await Promise.resolve(); + + expect(NativeBackgroundTasks.updateProgress).toHaveBeenCalledWith( + 'first', + -1, + 'file://first.zip', + ); + expect(NativeBackgroundTasks.updateProgress).toHaveBeenCalledWith( + 'second', + -1, + 'file://second.zip', + ); + + resolvers.forEach(resolve => resolve()); + await Promise.all([firstRun, secondRun]); + }); +}); diff --git a/src/services/backgroundTasks/__tests__/automaticBackupSchedule.test.ts b/src/services/backgroundTasks/__tests__/automaticBackupSchedule.test.ts new file mode 100644 index 000000000..9d961d2eb --- /dev/null +++ b/src/services/backgroundTasks/__tests__/automaticBackupSchedule.test.ts @@ -0,0 +1,72 @@ +import NativeBackgroundTasks from '@modules/native-background-tasks'; +import { askForPostNotificationsPermission } from '@utils/askForPostNoftificationsPermission'; +import { + AUTOMATIC_BACKUP_INTERVALS, + configureAutomaticBackups, +} from '../automaticBackupSchedule'; + +jest.mock('@modules/native-background-tasks', () => ({ + __esModule: true, + default: { + cancelAutomaticBackups: jest.fn(), + scheduleAutomaticBackups: jest.fn(), + }, +})); + +jest.mock('@utils/askForPostNoftificationsPermission', () => ({ + askForPostNotificationsPermission: jest.fn(), +})); + +jest.mock('@i18n/translations', () => ({ + getString: (key: string) => key, +})); + +const mockNativeBackgroundTasks = jest.mocked(NativeBackgroundTasks); +const mockAskForPostNotificationsPermission = jest.mocked( + askForPostNotificationsPermission, +); + +describe('automatic backup scheduling', () => { + it('exposes the supported fixed intervals', () => { + expect(AUTOMATIC_BACKUP_INTERVALS).toEqual([0, 6, 12, 24, 48, 168]); + }); + + it('cancels scheduled work when automatic backups are disabled', async () => { + await configureAutomaticBackups(0); + + expect( + mockNativeBackgroundTasks.cancelAutomaticBackups, + ).toHaveBeenCalledTimes(1); + expect( + mockNativeBackgroundTasks.scheduleAutomaticBackups, + ).not.toHaveBeenCalled(); + expect(mockAskForPostNotificationsPermission).not.toHaveBeenCalled(); + }); + + it('requests notification permission and schedules the selected interval', async () => { + await configureAutomaticBackups(12); + + expect(mockAskForPostNotificationsPermission).toHaveBeenCalledTimes(1); + expect( + mockNativeBackgroundTasks.scheduleAutomaticBackups, + ).toHaveBeenCalledWith( + 12, + 'notifications.LOCAL_BACKUP', + 'common.preparing', + '', + ); + }); + + it('passes the selected backup directory to native scheduling', async () => { + await configureAutomaticBackups(24, 'content://backup-folder'); + + expect( + mockNativeBackgroundTasks.scheduleAutomaticBackups, + ).toHaveBeenCalledWith( + 24, + 'notifications.LOCAL_BACKUP', + 'common.preparing', + 'content://backup-folder', + ); + }); +}); diff --git a/src/services/backgroundTasks/__tests__/libraryUpdateSchedule.test.ts b/src/services/backgroundTasks/__tests__/libraryUpdateSchedule.test.ts new file mode 100644 index 000000000..788225347 --- /dev/null +++ b/src/services/backgroundTasks/__tests__/libraryUpdateSchedule.test.ts @@ -0,0 +1,60 @@ +import NativeBackgroundTasks from '@modules/native-background-tasks'; +import { askForPostNotificationsPermission } from '@utils/askForPostNoftificationsPermission'; +import { + AUTOMATIC_LIBRARY_UPDATE_INTERVALS, + configureAutomaticLibraryUpdates, +} from '../libraryUpdateSchedule'; + +jest.mock('@modules/native-background-tasks', () => ({ + __esModule: true, + default: { + cancelLibraryUpdates: jest.fn(), + scheduleLibraryUpdates: jest.fn(), + }, +})); + +jest.mock('@utils/askForPostNoftificationsPermission', () => ({ + askForPostNotificationsPermission: jest.fn(), +})); + +jest.mock('@i18n/translations', () => ({ + getString: (key: string) => key, +})); + +const mockNativeBackgroundTasks = jest.mocked(NativeBackgroundTasks); +const mockAskForPostNotificationsPermission = jest.mocked( + askForPostNotificationsPermission, +); + +describe('automatic library update scheduling', () => { + it('exposes the supported fixed intervals', () => { + expect(AUTOMATIC_LIBRARY_UPDATE_INTERVALS).toEqual([ + 0, 12, 24, 48, 72, 168, + ]); + }); + + it('cancels scheduled work when automatic updates are disabled', async () => { + await configureAutomaticLibraryUpdates(0); + + expect( + mockNativeBackgroundTasks.cancelLibraryUpdates, + ).toHaveBeenCalledTimes(1); + expect( + mockNativeBackgroundTasks.scheduleLibraryUpdates, + ).not.toHaveBeenCalled(); + expect(mockAskForPostNotificationsPermission).not.toHaveBeenCalled(); + }); + + it('requests notification permission and schedules the selected interval', async () => { + await configureAutomaticLibraryUpdates(24); + + expect(mockAskForPostNotificationsPermission).toHaveBeenCalledTimes(1); + expect( + mockNativeBackgroundTasks.scheduleLibraryUpdates, + ).toHaveBeenCalledWith( + 24, + 'notifications.UPDATE_LIBRARY', + 'common.preparing', + ); + }); +}); diff --git a/src/services/backgroundTasks/__tests__/taskDefinitions.test.ts b/src/services/backgroundTasks/__tests__/taskDefinitions.test.ts new file mode 100644 index 000000000..43ba17f37 --- /dev/null +++ b/src/services/backgroundTasks/__tests__/taskDefinitions.test.ts @@ -0,0 +1,190 @@ +import type { NativeBackgroundTaskRecord } from '@modules/native-background-tasks'; +import type { BackgroundTask } from '../contracts'; +import { + allowsDuplicateTask, + createBackgroundTaskMetadata, + fromNativeTaskRecord, + getBackgroundTaskQueueName, + getBackgroundTaskTitle, + getDownloadProgressKey, + willTaskWaitInQueue, +} from '../taskDefinitions'; + +jest.mock('@i18n/translations', () => ({ + getString: (key: string) => key, +})); + +describe('background task definitions', () => { + it.each([ + 'DOWNLOAD_CHAPTER', + 'IMPORT_EPUB', + 'MIGRATE_NOVEL', + ])('allows duplicate %s tasks', name => { + expect(allowsDuplicateTask({ name } as BackgroundTask)).toBe(true); + }); + + it('prevents duplicate singleton task types', () => { + expect( + allowsDuplicateTask({ name: 'UPDATE_LIBRARY' } as BackgroundTask), + ).toBe(false); + }); + + it('derives user-facing download metadata', () => { + const task: BackgroundTask = { + name: 'DOWNLOAD_CHAPTER', + data: { + novelName: 'Example Novel', + novelId: 42, + pluginId: 'source-a', + chapters: [{ chapterId: 42, chapterName: 'Chapter 7' }], + }, + }; + + expect(getBackgroundTaskTitle(task)).toBe( + 'notifications.DOWNLOAD_CHAPTER: Example Novel', + ); + expect(createBackgroundTaskMetadata(task, false)).toEqual({ + name: 'notifications.DOWNLOAD_CHAPTER: Example Novel', + isRunning: false, + progress: undefined, + progressText: 'Chapter 7', + }); + }); + + it('serializes downloads from one plugin but separates other plugin lanes', () => { + const createDownload = (pluginId: string): BackgroundTask => ({ + name: 'DOWNLOAD_CHAPTER', + data: { + novelName: 'Example Novel', + novelId: 42, + pluginId, + chapters: [{ chapterId: 42, chapterName: 'Chapter 7' }], + }, + }); + + expect(getBackgroundTaskQueueName(createDownload('source-a'))).toBe( + getBackgroundTaskQueueName(createDownload('source-a')), + ); + expect(getBackgroundTaskQueueName(createDownload('source-a'))).not.toBe( + getBackgroundTaskQueueName(createDownload('source-b')), + ); + }); + + it('uses independent lanes for different non-download task types', () => { + expect(getBackgroundTaskQueueName({ name: 'UPDATE_LIBRARY' })).not.toBe( + getBackgroundTaskQueueName({ + name: 'LOCAL_RESTORE', + data: { sourceUri: 'file://backup.zip' }, + }), + ); + }); + + it('only reports queueing when a lane or the global limit blocks a task', () => { + const createDownload = (pluginId: string): BackgroundTask => ({ + name: 'DOWNLOAD_CHAPTER', + data: { + novelName: pluginId, + pluginId, + chapters: [{ chapterId: 42, chapterName: 'Chapter 7' }], + }, + }); + const queuedDownload = (pluginId: string) => ({ + id: pluginId, + task: createDownload(pluginId), + state: 'queued' as const, + meta: createBackgroundTaskMetadata(createDownload(pluginId), false), + }); + + expect(willTaskWaitInQueue(createDownload('source-a'), [])).toBe(false); + expect( + willTaskWaitInQueue(createDownload('source-a'), [ + queuedDownload('source-a'), + ]), + ).toBe(true); + expect( + willTaskWaitInQueue(createDownload('source-d'), [ + queuedDownload('source-a'), + queuedDownload('source-b'), + queuedDownload('source-c'), + ]), + ).toBe(true); + }); + + it('derives metadata for a multi-file EPUB import', () => { + const task: BackgroundTask = { + name: 'IMPORT_EPUB', + data: { + files: [ + { filename: 'First.epub', uri: 'file://first' }, + { filename: 'Second.epub', uri: 'file://second' }, + ], + }, + }; + + expect(getBackgroundTaskTitle(task)).toBe('notifications.IMPORT_EPUB (2)'); + expect(createBackgroundTaskMetadata(task, false).progressText).toBe( + 'First.epub', + ); + }); + + it('maps a native record into the reactive queue projection', () => { + const task: BackgroundTask = { name: 'UPDATE_LIBRARY' }; + const record: NativeBackgroundTaskRecord = { + id: 'task-1', + type: task.name, + payload: JSON.stringify(task), + title: 'Update library', + state: 'running', + progress: 0.5, + progressText: 'Example Novel', + attempt: 1, + createdAt: 1, + updatedAt: 2, + }; + + expect(fromNativeTaskRecord(record)).toEqual({ + id: 'task-1', + task, + state: 'running', + meta: { + name: 'Update library', + isRunning: true, + progress: 0.5, + progressText: 'Example Novel', + }, + }); + }); + + it('derives progress keys only for the requested novel downloads', () => { + const createDownload = ( + id: string, + novelId: number, + progress?: number, + ) => ({ + id, + task: { + name: 'DOWNLOAD_CHAPTER' as const, + data: { + novelName: `Novel ${novelId}`, + novelId, + chapters: [{ chapterId: novelId, chapterName: 'Chapter 1' }], + }, + }, + state: 'running' as const, + meta: { + name: `Novel ${novelId}`, + isRunning: true, + progress, + progressText: 'Chapter 1', + }, + }); + const tasks: (ReturnType | BackgroundTask)[] = [ + { name: 'UPDATE_LIBRARY' }, + createDownload('first', 1, 0.5), + createDownload('second', 2), + ]; + + expect(getDownloadProgressKey(tasks, 1)).toBe('first:running:0.5'); + expect(getDownloadProgressKey(tasks, 2)).toBe('second:running:pending'); + }); +}); diff --git a/src/services/backgroundTasks/automaticBackupSchedule.ts b/src/services/backgroundTasks/automaticBackupSchedule.ts new file mode 100644 index 000000000..19e50a184 --- /dev/null +++ b/src/services/backgroundTasks/automaticBackupSchedule.ts @@ -0,0 +1,26 @@ +import NativeBackgroundTasks from '@modules/native-background-tasks'; +import { getString } from '@i18n/translations'; +import { askForPostNotificationsPermission } from '@utils/askForPostNoftificationsPermission'; + +export const AUTOMATIC_BACKUP_INTERVALS = [0, 6, 12, 24, 48, 168] as const; + +export type AutomaticBackupInterval = + (typeof AUTOMATIC_BACKUP_INTERVALS)[number]; + +export const configureAutomaticBackups = async ( + intervalHours: AutomaticBackupInterval, + directoryUri?: string, +) => { + if (intervalHours === 0) { + await NativeBackgroundTasks.cancelAutomaticBackups(); + return; + } + + await askForPostNotificationsPermission(); + await NativeBackgroundTasks.scheduleAutomaticBackups( + intervalHours, + getString('notifications.LOCAL_BACKUP'), + getString('common.preparing'), + directoryUri ?? '', + ); +}; diff --git a/src/services/backgroundTasks/backgroundTasks.ts b/src/services/backgroundTasks/backgroundTasks.ts new file mode 100644 index 000000000..514cb90ef --- /dev/null +++ b/src/services/backgroundTasks/backgroundTasks.ts @@ -0,0 +1,3 @@ +import { BackgroundTaskQueue } from './BackgroundTaskQueue'; + +export const backgroundTasks = new BackgroundTaskQueue(); diff --git a/src/services/backgroundTasks/constants.ts b/src/services/backgroundTasks/constants.ts new file mode 100644 index 000000000..da9ecce65 --- /dev/null +++ b/src/services/backgroundTasks/constants.ts @@ -0,0 +1 @@ +export const BACKGROUND_TASKS_STORE_KEY = 'APP_SERVICE'; diff --git a/src/services/backgroundTasks/contracts.ts b/src/services/backgroundTasks/contracts.ts new file mode 100644 index 000000000..851b9d414 --- /dev/null +++ b/src/services/backgroundTasks/contracts.ts @@ -0,0 +1,134 @@ +import type { DriveFile } from '@api/drive/types'; +import type { NovelInfo } from '@database/types'; +import type { NativeBackgroundTaskRecord } from '@modules/native-background-tasks'; +import type { + EpubExportChapter, + EpubExportMetadata, +} from '@modules/nitro-epub'; +import type { BackupOptions } from '@services/backup/options'; + +export type SelfHostData = { + host: string; + backupFolder: string; + options?: BackupOptions; +}; + +export type DriveBackupData = + | DriveFile + | { + backupFolder: DriveFile; + options?: BackupOptions; + }; + +export type MigrationNovelPreference = 'current' | 'destination'; + +export type MigrationNovelOptions = { + cover: MigrationNovelPreference; + metadata: MigrationNovelPreference; + redownloadChapters: boolean; +}; + +export type MigrateNovelData = { + pluginId: string; + fromNovel: NovelInfo; + toNovelPath: string; + /** + * Optional for compatibility with migration tasks queued before review + * options were introduced. + */ + options?: MigrationNovelOptions; +}; + +export type EpubImportFile = { + filename: string; + uri: string; +}; + +export type EpubExportData = { + novelName: string; + metadata: EpubExportMetadata; + chapters: EpubExportChapter[]; + destinationUri: string; + fileName: string; +}; + +export type ChapterDownload = { + chapterId: number; + chapterName: string; +}; + +export type BackgroundTask = + | { name: 'IMPORT_EPUB'; data: { files: EpubImportFile[] } } + | { name: 'EXPORT_EPUB'; data: EpubExportData } + | { + name: 'UPDATE_LIBRARY'; + data?: { categoryId?: number; categoryName?: string }; + } + | { name: 'DRIVE_BACKUP'; data: DriveBackupData } + | { name: 'DRIVE_RESTORE'; data: DriveFile } + | { name: 'SELF_HOST_BACKUP'; data: SelfHostData } + | { name: 'SELF_HOST_RESTORE'; data: SelfHostData } + | { + name: 'LOCAL_BACKUP'; + data: { + destinationUri: string; + options?: BackupOptions; + automatic?: boolean; + }; + } + | { name: 'LOCAL_RESTORE'; data: { sourceUri: string } } + | { name: 'MIGRATE_NOVEL'; data: MigrateNovelData } + | DownloadChapterTask; + +export type DownloadChapterTask = { + name: 'DOWNLOAD_CHAPTER'; + data: { + novelName: string; + /** + * Optional for compatibility with download tasks queued before + * per-plugin execution lanes were introduced. + */ + pluginId?: string; + /** + * Optional for compatibility with download tasks persisted before the + * per-novel queue identity was introduced. + */ + novelId?: number; + chapters: ChapterDownload[]; + }; +}; + +export type BackgroundTaskMetadata = { + name: string; + isRunning: boolean; + progress: number | undefined; + progressText: string | undefined; + completionText?: string; +}; + +export type TaskProgressUpdater = ( + transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, +) => void; + +export type BackgroundTaskExecutionContext = { + checkpoint?: string; + updateCheckpoint: (checkpoint: string) => Promise; +}; + +export type BackgroundTaskEnqueuer = ( + tasks: BackgroundTask | BackgroundTask[], +) => void; + +export type QueuedBackgroundTask = { + task: BackgroundTask; + meta: BackgroundTaskMetadata; + id: string; + state?: NativeBackgroundTaskRecord['state']; +}; + +export type HeadlessBackgroundTaskData = { + taskId: string; + type: BackgroundTask['name']; + payload: string; + checkpoint?: string; +}; diff --git a/src/services/backgroundTasks/executeTask.ts b/src/services/backgroundTasks/executeTask.ts new file mode 100644 index 000000000..b2fd3041a --- /dev/null +++ b/src/services/backgroundTasks/executeTask.ts @@ -0,0 +1,56 @@ +import { createDriveBackup, driveRestore } from '../backup/drive'; +import { createBackup, restoreBackup } from '../backup/local'; +import { createSelfHostBackup, selfHostRestore } from '../backup/selfhost'; +import { downloadChapters } from '../download/downloadChapter'; +import { exportEpub } from '../epub/export'; +import { importEpubBatch } from '../epub/import'; +import { migrateNovel } from '../migrate/migrateNovel'; +import { updateLibrary } from '../updates'; +import { getMMKVObject, setMMKVObject } from '@utils/mmkv/mmkv'; +import type { + BackgroundTask, + BackgroundTaskEnqueuer, + BackgroundTaskExecutionContext, + TaskProgressUpdater, +} from './contracts'; + +export const executeBackgroundTask = async ( + task: BackgroundTask, + updateProgress: TaskProgressUpdater, + enqueue: BackgroundTaskEnqueuer, + context: BackgroundTaskExecutionContext, +) => { + switch (task.name) { + case 'IMPORT_EPUB': + return importEpubBatch(task.data, updateProgress); + case 'EXPORT_EPUB': + return exportEpub(task.data, updateProgress); + case 'UPDATE_LIBRARY': + return updateLibrary(task.data || {}, updateProgress, enqueue); + case 'DRIVE_BACKUP': + return createDriveBackup(task.data, updateProgress); + case 'DRIVE_RESTORE': + return driveRestore(task.data, updateProgress); + case 'SELF_HOST_BACKUP': + return createSelfHostBackup(task.data, updateProgress); + case 'SELF_HOST_RESTORE': + return selfHostRestore(task.data, updateProgress); + case 'LOCAL_BACKUP': + await createBackup(task.data, updateProgress); + if (task.data.automatic) { + const settings = + getMMKVObject>('APP_SETTINGS') ?? {}; + setMMKVObject('APP_SETTINGS', { + ...settings, + lastAutomaticBackupAt: Date.now(), + }); + } + return; + case 'LOCAL_RESTORE': + return restoreBackup(task.data, updateProgress); + case 'MIGRATE_NOVEL': + return migrateNovel(task.data, updateProgress, enqueue); + case 'DOWNLOAD_CHAPTER': + return downloadChapters(task.data, updateProgress, context); + } +}; diff --git a/src/services/backgroundTasks/headlessTask.ts b/src/services/backgroundTasks/headlessTask.ts new file mode 100644 index 000000000..fa31be177 --- /dev/null +++ b/src/services/backgroundTasks/headlessTask.ts @@ -0,0 +1,18 @@ +import { initializeDatabase } from '@database/db'; +import { initializeInstalledPlugins } from '@plugins/pluginManager'; +import type { BackgroundTask, HeadlessBackgroundTaskData } from './contracts'; +import { backgroundTasks } from './backgroundTasks'; + +export const runHeadlessBackgroundTask = async ({ + taskId, + payload, + checkpoint, +}: HeadlessBackgroundTaskData) => { + await initializeDatabase(); + await initializeInstalledPlugins(); + await backgroundTasks.run( + taskId, + JSON.parse(payload) as BackgroundTask, + checkpoint, + ); +}; diff --git a/src/services/backgroundTasks/index.ts b/src/services/backgroundTasks/index.ts new file mode 100644 index 000000000..151ec94e6 --- /dev/null +++ b/src/services/backgroundTasks/index.ts @@ -0,0 +1,31 @@ +export { backgroundTasks } from './backgroundTasks'; +export { BACKGROUND_TASKS_STORE_KEY } from './constants'; +export { + AUTOMATIC_LIBRARY_UPDATE_INTERVALS, + configureAutomaticLibraryUpdates, +} from './libraryUpdateSchedule'; +export { + AUTOMATIC_BACKUP_INTERVALS, + configureAutomaticBackups, +} from './automaticBackupSchedule'; +export { runHeadlessBackgroundTask } from './headlessTask'; +export { getDownloadProgressKey } from './taskDefinitions'; +export type { + BackgroundTask, + BackgroundTaskEnqueuer, + BackgroundTaskExecutionContext, + BackgroundTaskMetadata, + ChapterDownload, + DownloadChapterTask, + EpubExportData, + EpubImportFile, + HeadlessBackgroundTaskData, + MigrateNovelData, + MigrationNovelOptions, + MigrationNovelPreference, + QueuedBackgroundTask, + SelfHostData, + TaskProgressUpdater, +} from './contracts'; +export type { AutomaticLibraryUpdateInterval } from './libraryUpdateSchedule'; +export type { AutomaticBackupInterval } from './automaticBackupSchedule'; diff --git a/src/services/backgroundTasks/libraryUpdateSchedule.ts b/src/services/backgroundTasks/libraryUpdateSchedule.ts new file mode 100644 index 000000000..f812fe331 --- /dev/null +++ b/src/services/backgroundTasks/libraryUpdateSchedule.ts @@ -0,0 +1,26 @@ +import NativeBackgroundTasks from '@modules/native-background-tasks'; +import { getString } from '@i18n/translations'; +import { askForPostNotificationsPermission } from '@utils/askForPostNoftificationsPermission'; + +export const AUTOMATIC_LIBRARY_UPDATE_INTERVALS = [ + 0, 12, 24, 48, 72, 168, +] as const; + +export type AutomaticLibraryUpdateInterval = + (typeof AUTOMATIC_LIBRARY_UPDATE_INTERVALS)[number]; + +export const configureAutomaticLibraryUpdates = async ( + intervalHours: AutomaticLibraryUpdateInterval, +) => { + if (intervalHours === 0) { + await NativeBackgroundTasks.cancelLibraryUpdates(); + return; + } + + await askForPostNotificationsPermission(); + await NativeBackgroundTasks.scheduleLibraryUpdates( + intervalHours, + getString('notifications.UPDATE_LIBRARY'), + getString('common.preparing'), + ); +}; diff --git a/src/services/backgroundTasks/taskDefinitions.ts b/src/services/backgroundTasks/taskDefinitions.ts new file mode 100644 index 000000000..ba86e4574 --- /dev/null +++ b/src/services/backgroundTasks/taskDefinitions.ts @@ -0,0 +1,148 @@ +import type { NativeBackgroundTaskRecord } from '@modules/native-background-tasks'; +import { getString } from '@i18n/translations'; +import type { + BackgroundTask, + BackgroundTaskMetadata, + QueuedBackgroundTask, +} from './contracts'; + +export const ACTIVE_BACKGROUND_TASK_STATES = new Set([ + 'queued', + 'running', + 'paused', +]); + +const MULTIPLICABLE_TASKS: BackgroundTask['name'][] = [ + 'DOWNLOAD_CHAPTER', + 'IMPORT_EPUB', + 'EXPORT_EPUB', + 'MIGRATE_NOVEL', +]; + +const BACKGROUND_TASK_QUEUE_PREFIX = 'lnreader-background-task'; +const MAX_CONCURRENT_DOWNLOADS = 3; + +export const allowsDuplicateTask = (task: BackgroundTask) => + MULTIPLICABLE_TASKS.includes(task.name); + +export const getDownloadProgressKey = ( + tasks: (QueuedBackgroundTask | BackgroundTask)[] | undefined, + novelId?: number, +) => + (tasks ?? []) + .filter((task): task is QueuedBackgroundTask => 'task' in task) + .filter( + task => + task.task.name === 'DOWNLOAD_CHAPTER' && + (novelId === undefined || task.task.data.novelId === novelId), + ) + .map( + task => + `${task.id}:${task.state ?? 'queued'}:${ + task.meta.progress ?? 'pending' + }`, + ) + .join('|'); + +export const getBackgroundTaskQueueName = (task: BackgroundTask) => + task.name === 'DOWNLOAD_CHAPTER' + ? `${BACKGROUND_TASK_QUEUE_PREFIX}:download:${ + task.data.pluginId || 'legacy' + }` + : `${BACKGROUND_TASK_QUEUE_PREFIX}:task:${task.name}`; + +export const willTaskWaitInQueue = ( + task: BackgroundTask, + activeTasks: QueuedBackgroundTask[], +) => { + const queueName = getBackgroundTaskQueueName(task); + if ( + activeTasks.some( + activeTask => getBackgroundTaskQueueName(activeTask.task) === queueName, + ) + ) { + return true; + } + + if (task.name !== 'DOWNLOAD_CHAPTER') { + return false; + } + + const activeDownloadLanes = new Set( + activeTasks + .filter( + activeTask => + activeTask.task.name === 'DOWNLOAD_CHAPTER' && + activeTask.state !== 'paused', + ) + .map(activeTask => getBackgroundTaskQueueName(activeTask.task)), + ); + return activeDownloadLanes.size >= MAX_CONCURRENT_DOWNLOADS; +}; + +export const getBackgroundTaskTitle = (task: BackgroundTask) => { + switch (task.name) { + case 'DOWNLOAD_CHAPTER': + return `${getString('notifications.DOWNLOAD_CHAPTER')}: ${ + task.data.novelName + }`; + case 'IMPORT_EPUB': + return task.data.files.length === 1 + ? `${getString('notifications.IMPORT_EPUB')}: ${ + task.data.files[0].filename + }` + : `${getString('notifications.IMPORT_EPUB')} (${ + task.data.files.length + })`; + case 'EXPORT_EPUB': + return `${getString('notifications.EXPORT_EPUB')}: ${ + task.data.novelName + }`; + case 'MIGRATE_NOVEL': + return `${getString('notifications.MIGRATE_NOVEL')}: ${ + task.data.fromNovel.name + }`; + case 'UPDATE_LIBRARY': + return task.data?.categoryName + ? `${getString('notifications.UPDATE_LIBRARY')}: ${ + task.data.categoryName + }` + : getString('notifications.UPDATE_LIBRARY'); + default: + return getString(`notifications.${task.name}`); + } +}; + +export const createBackgroundTaskMetadata = ( + task: BackgroundTask, + isRunning: boolean, +): BackgroundTaskMetadata => ({ + name: getBackgroundTaskTitle(task), + isRunning, + progress: undefined, + progressText: + task.name === 'DOWNLOAD_CHAPTER' + ? task.data.chapters[0]?.chapterName + : task.name === 'IMPORT_EPUB' + ? task.data.files[0]?.filename + : task.name === 'EXPORT_EPUB' + ? getString('novelScreen.epub.preparingExport') + : undefined, +}); + +export const fromNativeTaskRecord = ( + record: NativeBackgroundTaskRecord, +): QueuedBackgroundTask => { + const task = JSON.parse(record.payload) as BackgroundTask; + return { + id: record.id, + task, + state: record.state, + meta: { + name: record.title, + isRunning: record.state === 'running', + progress: record.progress, + progressText: record.progressText, + }, + }; +}; diff --git a/src/services/backup/__tests__/backupResult.test.ts b/src/services/backup/__tests__/backupResult.test.ts new file mode 100644 index 000000000..7e7b301a7 --- /dev/null +++ b/src/services/backup/__tests__/backupResult.test.ts @@ -0,0 +1,52 @@ +import { getBackupCompletionText, type BackupResult } from '../backupResult'; + +jest.mock('@i18n/translations', () => ({ + getString: (key: string, options?: Record) => { + const strings: Record = { + 'backupScreen.backupCreated': 'Backup created successfully', + 'backupScreen.backupCreatedWithWarnings': + 'Backup created with warnings: %{warnings}', + }; + const pluralStrings: Record = { + 'backupScreen.novelsBackupFailedSummary': [ + '%{count} novel failed', + '%{count} novels failed', + ], + 'backupScreen.sectionsBackupFailedSummary': [ + '%{count} backup section failed', + '%{count} backup sections failed', + ], + }; + const pluralString = pluralStrings[key]; + const template = pluralString + ? pluralString[options?.count === 1 ? 0 : 1] + : strings[key] ?? key; + + return Object.entries(options ?? {}).reduce( + (text, [name, value]) => text.replace(`%{${name}}`, String(value)), + template, + ); + }, +})); + +describe('backup result notifications', () => { + it('uses a concise success message when every section is backed up', () => { + const result: BackupResult = { + failedNovelCount: 0, + failedSectionCount: 0, + }; + + expect(getBackupCompletionText(result)).toBe('Backup created successfully'); + }); + + it('aggregates partial backup failures', () => { + expect( + getBackupCompletionText({ + failedNovelCount: 2, + failedSectionCount: 1, + }), + ).toBe( + 'Backup created with warnings: 2 novels failed; 1 backup section failed', + ); + }); +}); diff --git a/src/services/backup/__tests__/local.test.ts b/src/services/backup/__tests__/local.test.ts new file mode 100644 index 000000000..fc8f97072 --- /dev/null +++ b/src/services/backup/__tests__/local.test.ts @@ -0,0 +1,115 @@ +import NativeFile from '@modules/native-file'; +import NativeZipArchive from '@modules/native-zip-archive'; +import { createBackup, restoreBackup } from '../local'; +import { finalizeRestoredPlugins } from '../restoreResult'; +import { prepareBackupData, restoreData } from '../utils'; + +jest.mock('../utils', () => ({ + CACHE_DIR_PATH: '/cache/BackupData', + clearBackupCache: jest.fn(), + prepareBackupData: jest.fn(), + restoreData: jest.fn(), +})); + +jest.mock('../restoreResult', () => ({ + finalizeRestoredPlugins: jest.fn(), + getRestoreCompletionText: jest.fn(), +})); + +jest.mock('../backupResult', () => ({ + getBackupCompletionText: jest.fn(() => 'Backup created'), +})); + +jest.mock('@utils/Storages', () => ({ + NOVEL_STORAGE: '/storage/Novels', + PLUGIN_STORAGE: '/storage/Plugins', + ROOT_STORAGE: '/storage', +})); + +jest.mock('@utils/sleep', () => ({ + sleep: jest.fn().mockResolvedValue(undefined), +})); + +jest.mock('@i18n/translations', () => ({ + getString: (key: string) => key, +})); + +describe('local selective backup', () => { + it('creates archives only for selected file sections', async () => { + jest.mocked(prepareBackupData).mockResolvedValue({ + failedNovelCount: 0, + failedSectionCount: 0, + }); + jest.mocked(NativeZipArchive.zip).mockResolvedValue(undefined); + jest.mocked(NativeFile.copyFile).mockResolvedValue(undefined); + + await createBackup({ + destinationUri: 'content://backup.zip', + options: { + library: true, + settings: true, + plugins: true, + downloadedFiles: false, + }, + }); + + expect(prepareBackupData).toHaveBeenCalledWith('/cache/BackupData', { + library: true, + settings: true, + plugins: true, + downloadedFiles: false, + }); + expect(NativeZipArchive.zip).toHaveBeenCalledWith( + '/storage/Plugins', + '/cache/BackupData/plugins.zip', + ); + expect(NativeZipArchive.zip).not.toHaveBeenCalledWith( + '/storage/Novels', + expect.any(String), + ); + expect(NativeZipArchive.zip).toHaveBeenCalledWith( + '/cache/BackupData', + '/cache/BackupData.zip', + ); + }); + + it('loads restored plugins after their archive is extracted', async () => { + const restoreResult = { + novelCount: 1, + failedNovelCount: 0, + categoryCount: 0, + failedCategoryCount: 0, + settingsRestored: true, + failedSectionCount: 0, + pluginIds: ['restored'], + manifest: { + appVersion: '2.1.0', + formatVersion: 2 as const, + sections: { + library: true, + settings: true, + plugins: true, + downloadedFiles: false, + }, + }, + }; + jest.mocked(restoreData).mockResolvedValueOnce(restoreResult); + jest.mocked(NativeFile.exists).mockResolvedValue(true); + jest.mocked(NativeFile.copyFile).mockResolvedValue(undefined); + jest.mocked(NativeZipArchive.unzip).mockResolvedValue(undefined); + jest.mocked(finalizeRestoredPlugins).mockResolvedValueOnce([]); + + await restoreBackup({ sourceUri: 'content://backup.zip' }); + + expect(NativeZipArchive.unzip).toHaveBeenCalledWith( + '/cache/BackupData/plugins.zip', + '/storage/Plugins', + ); + expect(finalizeRestoredPlugins).toHaveBeenCalledWith(restoreResult); + expect( + jest.mocked(finalizeRestoredPlugins).mock.invocationCallOrder[0], + ).toBeGreaterThan( + jest.mocked(NativeZipArchive.unzip).mock.invocationCallOrder[1], + ); + }); +}); diff --git a/src/services/backup/__tests__/options.test.ts b/src/services/backup/__tests__/options.test.ts new file mode 100644 index 000000000..33fff6b8b --- /dev/null +++ b/src/services/backup/__tests__/options.test.ts @@ -0,0 +1,63 @@ +import { + areAllBackupOptionsSelected, + DEFAULT_BACKUP_OPTIONS, + hasSelectedBackupOption, + resolveBackupOptions, +} from '../options'; +import { getSelectedBackupFileSections } from '../fileSections'; +import { ZipBackupName } from '../types'; + +jest.mock('@utils/Storages', () => ({ + NOVEL_STORAGE: '/storage/Novels', + PLUGIN_STORAGE: '/storage/Plugins', +})); + +describe('backup options', () => { + it('preserves the existing full-backup behavior by default', () => { + expect(resolveBackupOptions()).toEqual(DEFAULT_BACKUP_OPTIONS); + expect(areAllBackupOptionsSelected(resolveBackupOptions())).toBe(true); + }); + + it('prevents downloaded files from being selected without library data', () => { + expect( + resolveBackupOptions({ + library: false, + settings: true, + plugins: false, + downloadedFiles: true, + }), + ).toEqual({ + library: false, + settings: true, + plugins: false, + downloadedFiles: false, + }); + }); + + it('requires at least one selected section', () => { + expect( + hasSelectedBackupOption({ + library: false, + settings: false, + plugins: false, + downloadedFiles: false, + }), + ).toBe(false); + }); + + it('maps selected file sections to independent archives', () => { + expect( + getSelectedBackupFileSections({ + library: true, + settings: true, + plugins: true, + downloadedFiles: false, + }), + ).toEqual([ + { + archiveName: ZipBackupName.PLUGINS, + storagePath: '/storage/Plugins', + }, + ]); + }); +}); diff --git a/src/services/backup/__tests__/restoreResult.test.ts b/src/services/backup/__tests__/restoreResult.test.ts new file mode 100644 index 000000000..4f35b0001 --- /dev/null +++ b/src/services/backup/__tests__/restoreResult.test.ts @@ -0,0 +1,167 @@ +import NativeFile from '@modules/native-file'; +import { reloadInstalledPlugins } from '@plugins/pluginManager'; +import { + finalizeRestoredPlugins, + getMissingRestorePluginIds, + getRestoreCompletionText, + type RestoreResult, +} from '../restoreResult'; + +jest.mock('@i18n/translations', () => ({ + getString: (key: string, options?: Record) => { + const strings: Record = { + 'backupScreen.settingsRestoreFailedSummary': 'settings failed', + 'backupScreen.missingPluginsAfterRestore': 'Missing plugins: %{plugins}', + }; + const pluralStrings: Record = { + 'backupScreen.backupRestoredSummary': [ + 'Restored %{count} novel', + 'Restored %{count} novels', + ], + 'backupScreen.backupRestoredWithWarnings': [ + 'Restored %{count} novel with warnings: %{warnings}', + 'Restored %{count} novels with warnings: %{warnings}', + ], + 'backupScreen.novelsRestoreFailedSummary': [ + '%{count} novel failed', + '%{count} novels failed', + ], + 'backupScreen.categoriesRestoreFailedSummary': [ + '%{count} category failed', + '%{count} categories failed', + ], + 'backupScreen.sectionsRestoreFailedSummary': [ + '%{count} backup section failed', + '%{count} backup sections failed', + ], + }; + const pluralString = pluralStrings[key]; + const template = pluralString + ? pluralString[options?.count === 1 ? 0 : 1] + : strings[key] ?? key; + + return Object.entries(options ?? {}).reduce( + (text, [name, value]) => text.replace(`%{${name}}`, String(value)), + template, + ); + }, +})); + +jest.mock('@plugins/pluginManager', () => ({ + LOCAL_PLUGIN_ID: 'local', + reloadInstalledPlugins: jest.fn(), +})); + +jest.mock('@utils/Storages', () => ({ + PLUGIN_STORAGE: '/storage/Plugins', +})); + +const successfulResult: RestoreResult = { + novelCount: 4, + failedNovelCount: 0, + categoryCount: 2, + failedCategoryCount: 0, + settingsRestored: true, + failedSectionCount: 0, + pluginIds: ['installed'], + manifest: { + appVersion: '2.1.0', + formatVersion: 2, + sections: { + library: true, + settings: true, + plugins: true, + downloadedFiles: true, + }, + }, +}; + +describe('restore result notifications', () => { + beforeEach(() => { + jest.mocked(reloadInstalledPlugins).mockReset(); + }); + + it('uses a concise success message when the restore has no warnings', () => { + expect(getRestoreCompletionText(successfulResult, [])).toBe( + 'Restored 4 novels', + ); + }); + + it('aggregates restore failures and missing plugin identifiers', () => { + expect( + getRestoreCompletionText( + { + ...successfulResult, + failedNovelCount: 2, + failedCategoryCount: 1, + settingsRestored: false, + failedSectionCount: 1, + }, + ['source.one', 'source.two'], + ), + ).toBe( + 'Restored 4 novels with warnings: 2 novels failed; 1 category failed; settings failed; 1 backup section failed; Missing plugins: source.one, source.two', + ); + }); + + it('checks each referenced plugin once and ignores local novels', async () => { + jest + .mocked(NativeFile.exists) + .mockImplementation(async path => path.includes('installed')); + + await expect( + getMissingRestorePluginIds(['local', 'installed', 'missing', 'missing']), + ).resolves.toEqual(['missing']); + expect(NativeFile.exists).toHaveBeenCalledTimes(2); + }); + + it('reloads restored plugin bundles and reports bundles that fail to load', async () => { + jest.mocked(reloadInstalledPlugins).mockResolvedValueOnce(['invalid']); + jest + .mocked(NativeFile.exists) + .mockImplementation(async path => path.includes('installed')); + + await expect( + finalizeRestoredPlugins({ + ...successfulResult, + pluginIds: ['installed', 'missing'], + }), + ).resolves.toEqual(['missing', 'invalid']); + }); + + it('does not reload plugins when that section was omitted', async () => { + await finalizeRestoredPlugins({ + ...successfulResult, + pluginIds: [], + manifest: { + ...successfulResult.manifest, + sections: { + ...successfulResult.manifest.sections, + plugins: false, + }, + }, + }); + + expect(reloadInstalledPlugins).not.toHaveBeenCalled(); + }); + + it('does not describe an intentionally omitted library as zero novels', () => { + expect( + getRestoreCompletionText( + { + ...successfulResult, + novelCount: 0, + manifest: { + ...successfulResult.manifest, + sections: { + ...successfulResult.manifest.sections, + library: false, + downloadedFiles: false, + }, + }, + }, + [], + ), + ).toBe('backupScreen.backupRestored'); + }); +}); diff --git a/src/services/backup/__tests__/utils.test.ts b/src/services/backup/__tests__/utils.test.ts new file mode 100644 index 000000000..3856e3c63 --- /dev/null +++ b/src/services/backup/__tests__/utils.test.ts @@ -0,0 +1,215 @@ +import { + _restoreNovelAndChapters, + getAllNovels, +} from '@database/queries/NovelQueries'; +import { getNovelChapters } from '@database/queries/ChapterQueries'; +import { + _restoreCategory, + getAllNovelCategories, + getCategoriesFromDb, +} from '@database/queries/CategoryQueries'; +import NativeFile from '@modules/native-file'; +import { MMKVStorage } from '@utils/mmkv/mmkv'; +import { prepareBackupData, restoreData } from '../utils'; +import type { BackupOptions } from '../options'; + +jest.mock('@database/queries/NovelQueries', () => ({ + _restoreNovelAndChapters: jest.fn(), + getAllNovels: jest.fn(), +})); + +jest.mock('@database/queries/ChapterQueries', () => ({ + getNovelChapters: jest.fn(), +})); + +jest.mock('@database/queries/CategoryQueries', () => ({ + _restoreCategory: jest.fn(), + getAllNovelCategories: jest.fn(), + getCategoriesFromDb: jest.fn(), +})); + +jest.mock('@hooks/persisted/useSelfHost', () => ({ + SELF_HOST_BACKUP: 'SELF_HOST_BACKUP', +})); + +jest.mock('@hooks/persisted/migrations/trackerMigration', () => ({ + OLD_TRACKED_NOVEL_PREFIX: 'OLD_TRACKED_NOVEL_PREFIX', +})); + +jest.mock('@hooks/persisted/useUpdates', () => ({ + LAST_UPDATE_TIME: 'LAST_UPDATE_TIME', +})); + +jest.mock('@utils/mmkv/mmkv', () => ({ + MMKVStorage: { + getAllKeys: jest.fn(() => []), + getBoolean: jest.fn(), + getString: jest.fn(), + set: jest.fn(), + }, +})); + +jest.mock('@i18n/translations', () => ({ + getString: (key: string) => key, +})); + +jest.mock('@plugins/pluginManager', () => ({ + INSTALLED_PLUGINS_KEY: 'INSTALL_PLUGINS', +})); + +jest.mock('@utils/Storages', () => ({ + ROOT_STORAGE: '/storage', +})); + +const pluginOnlyOptions: BackupOptions = { + library: false, + settings: false, + plugins: true, + downloadedFiles: false, +}; + +describe('selective backup data', () => { + beforeEach(() => { + jest.mocked(NativeFile.exists).mockResolvedValue(false); + jest.mocked(NativeFile.mkdir).mockResolvedValue(undefined); + jest.mocked(NativeFile.writeFile).mockResolvedValue(undefined); + jest.mocked(getAllNovels).mockResolvedValue([]); + jest.mocked(getNovelChapters).mockResolvedValue([]); + jest.mocked(getCategoriesFromDb).mockResolvedValue([]); + jest.mocked(getAllNovelCategories).mockResolvedValue([]); + }); + + it('writes the selected sections to the v2 manifest', async () => { + await prepareBackupData('/cache', pluginOnlyOptions); + + expect(NativeFile.writeFile).toHaveBeenCalledTimes(2); + expect(NativeFile.writeFile).toHaveBeenCalledWith( + '/cache/Version.json', + expect.stringContaining( + '"sections":{"library":false,"settings":false,"plugins":true,"downloadedFiles":false}', + ), + ); + expect(getAllNovels).not.toHaveBeenCalled(); + expect(getCategoriesFromDb).not.toHaveBeenCalled(); + expect(NativeFile.writeFile).toHaveBeenCalledWith( + '/cache/Plugins.json', + '[]', + ); + }); + + it('does not warn about sections intentionally omitted by the manifest', async () => { + jest + .mocked(NativeFile.readFile) + .mockResolvedValueOnce( + JSON.stringify({ + appVersion: '2.1.0', + formatVersion: 2, + sections: pluginOnlyOptions, + }), + ) + .mockResolvedValueOnce('[]'); + jest + .mocked(NativeFile.exists) + .mockImplementation(async path => path.endsWith('/Plugins.json')); + + const result = await restoreData('/cache'); + + expect(result).toMatchObject({ + failedNovelCount: 0, + failedCategoryCount: 0, + failedSectionCount: 0, + settingsRestored: true, + manifest: { + formatVersion: 2, + sections: pluginOnlyOptions, + }, + }); + expect(_restoreNovelAndChapters).not.toHaveBeenCalled(); + expect(_restoreCategory).not.toHaveBeenCalled(); + expect(MMKVStorage.set).toHaveBeenCalledWith('INSTALL_PLUGINS', '[]'); + }); + + it('clears file-backed metadata when downloaded files are omitted', async () => { + jest.mocked(getAllNovels).mockResolvedValueOnce([ + { + id: 1, + name: 'Example', + path: '/example', + pluginId: 'source', + cover: 'file:///storage/Novels/source/1/cover.png', + }, + ]); + jest.mocked(getNovelChapters).mockResolvedValueOnce([ + { + id: 10, + novelId: 1, + path: '/chapter-1', + name: 'Chapter 1', + isDownloaded: true, + }, + ] as Awaited>); + + await prepareBackupData('/cache', { + library: true, + settings: false, + plugins: false, + downloadedFiles: false, + }); + + const novelWrite = jest + .mocked(NativeFile.writeFile) + .mock.calls.find(([path]) => path.endsWith('/1.json')); + expect(JSON.parse(novelWrite?.[1] ?? '{}')).toMatchObject({ + cover: null, + chapters: [{ id: 10, isDownloaded: false }], + }); + }); + + it('omits the installed-plugin registry when plugin files are excluded', async () => { + jest + .mocked(MMKVStorage.getAllKeys) + .mockReturnValueOnce(['INSTALL_PLUGINS', 'OTHER_SETTING']); + jest + .mocked(MMKVStorage.getString) + .mockImplementation(key => + key === 'INSTALL_PLUGINS' + ? '[{"id":"source"}]' + : key === 'OTHER_SETTING' + ? 'kept' + : undefined, + ); + + await prepareBackupData('/cache', { + library: false, + settings: true, + plugins: false, + downloadedFiles: false, + }); + + const settingsWrite = jest + .mocked(NativeFile.writeFile) + .mock.calls.find(([path]) => path.endsWith('/Setting.json')); + expect(JSON.parse(settingsWrite?.[1] ?? '{}')).toEqual({ + OTHER_SETTING: 'kept', + }); + }); + + it('treats backups without a section manifest as legacy full backups', async () => { + jest + .mocked(NativeFile.readFile) + .mockResolvedValueOnce(JSON.stringify({ version: '2.0.0' })); + + const result = await restoreData('/cache'); + + expect(result.manifest).toMatchObject({ + appVersion: '2.0.0', + formatVersion: 1, + sections: { + library: true, + settings: true, + plugins: true, + downloadedFiles: true, + }, + }); + }); +}); diff --git a/src/services/backup/backupResult.ts b/src/services/backup/backupResult.ts new file mode 100644 index 000000000..bfd2834ad --- /dev/null +++ b/src/services/backup/backupResult.ts @@ -0,0 +1,33 @@ +import { getString } from '@i18n/translations'; + +export type BackupResult = { + failedNovelCount: number; + failedSectionCount: number; +}; + +export const getBackupCompletionText = (result: BackupResult) => { + const warnings: string[] = []; + + if (result.failedNovelCount > 0) { + warnings.push( + getString('backupScreen.novelsBackupFailedSummary', { + count: result.failedNovelCount, + }), + ); + } + if (result.failedSectionCount > 0) { + warnings.push( + getString('backupScreen.sectionsBackupFailedSummary', { + count: result.failedSectionCount, + }), + ); + } + + if (warnings.length === 0) { + return getString('backupScreen.backupCreated'); + } + + return getString('backupScreen.backupCreatedWithWarnings', { + warnings: warnings.join('; '), + }); +}; diff --git a/src/services/backup/drive/index.ts b/src/services/backup/drive/index.ts index 16396762b..93767ce31 100644 --- a/src/services/backup/drive/index.ts +++ b/src/services/backup/drive/index.ts @@ -1,19 +1,52 @@ import { DriveFile } from '@api/drive/types'; import { sleep } from '@utils/sleep'; import { exists } from '@api/drive'; -import { getString } from '@strings/translations'; -import { CACHE_DIR_PATH, prepareBackupData, restoreData } from '../utils'; +import { getString } from '@i18n/translations'; +import { + CACHE_DIR_PATH, + clearBackupCache, + prepareBackupData, + restoreData, +} from '../utils'; +import { + finalizeRestoredPlugins, + getRestoreCompletionText, +} from '../restoreResult'; +import { getBackupCompletionText } from '../backupResult'; import { download, updateMetadata, uploadMedia } from '@api/drive/request'; import { ZipBackupName } from '../types'; import { ROOT_STORAGE } from '@utils/Storages'; -import { BackgroundTaskMetadata } from '@services/ServiceManager'; +import type { + DriveBackupData, + TaskProgressUpdater, +} from '@services/backgroundTasks/contracts'; +import { getSelectedBackupFileSections } from '../fileSections'; +import { resolveBackupOptions } from '../options'; + +const uploadBackupSection = async ( + sourcePath: string, + name: ZipBackupName, + backupFolder: DriveFile, +) => { + const file = await uploadMedia(sourcePath); + await updateMetadata( + file.id, + { + name, + mimeType: 'application/zip', + parents: [backupFolder.id], + }, + file.parents[0], + ); +}; export const createDriveBackup = async ( - backupFolder: DriveFile, - setMeta: ( - transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, - ) => void, + data: DriveBackupData, + setMeta: TaskProgressUpdater, ) => { + const backupFolder = 'backupFolder' in data ? data.backupFolder : data; + const requestedOptions = 'backupFolder' in data ? data.options : undefined; + const options = resolveBackupOptions(requestedOptions); setMeta(meta => ({ ...meta, isRunning: true, @@ -21,7 +54,7 @@ export const createDriveBackup = async ( progressText: getString('backupScreen.preparingData'), })); - await prepareBackupData(CACHE_DIR_PATH); + const backupResult = await prepareBackupData(CACHE_DIR_PATH, options); setMeta(meta => ({ ...meta, @@ -31,47 +64,35 @@ export const createDriveBackup = async ( await sleep(500); - const file = await uploadMedia(CACHE_DIR_PATH); - - await updateMetadata( - file.id, - { - name: ZipBackupName.DATA, - mimeType: 'application/zip', - parents: [backupFolder.id], - }, - file.parents[0], - ); + await uploadBackupSection(CACHE_DIR_PATH, ZipBackupName.DATA, backupFolder); setMeta(meta => ({ ...meta, progress: 2 / 3, - progressText: getString('backupScreen.uploadingDownloadedFiles'), + progressText: getString('backupScreen.uploadingSelectedFiles'), })); - const file2 = await uploadMedia(ROOT_STORAGE); - await updateMetadata( - file2.id, - { - name: ZipBackupName.DOWNLOAD, - mimeType: 'application/zip', - parents: [backupFolder.id], - }, - file2.parents[0], - ); + for (const section of getSelectedBackupFileSections(options)) { + await uploadBackupSection( + section.storagePath, + section.archiveName, + backupFolder, + ); + } + const completionText = getBackupCompletionText(backupResult); setMeta(meta => ({ ...meta, progress: 3 / 3, isRunning: false, + progressText: completionText, + completionText, })); }; export const driveRestore = async ( backupFolder: DriveFile, - setMeta: ( - transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, - ) => void, + setMeta: TaskProgressUpdater, ) => { setMeta(meta => ({ ...meta, @@ -81,15 +102,11 @@ export const driveRestore = async ( })); const zipDataFile = await exists(ZipBackupName.DATA, false, backupFolder.id); - const zipDownloadFile = await exists( - ZipBackupName.DOWNLOAD, - false, - backupFolder.id, - ); - if (!zipDataFile || !zipDownloadFile) { + if (!zipDataFile) { throw new Error(getString('backupScreen.invalidBackupFolder')); } + await clearBackupCache(); await download(zipDataFile, CACHE_DIR_PATH); await sleep(500); @@ -99,20 +116,47 @@ export const driveRestore = async ( progressText: getString('backupScreen.restoringData'), })); - await restoreData(CACHE_DIR_PATH); + const restoreResult = await restoreData(CACHE_DIR_PATH, setMeta); await sleep(500); setMeta(meta => ({ ...meta, progress: 2 / 3, - progressText: getString('backupScreen.downloadingDownloadedFiles'), + progressText: getString('backupScreen.restoringSelectedFiles'), })); - await download(zipDownloadFile, ROOT_STORAGE); + if (restoreResult.manifest.formatVersion === 1) { + const legacyFile = await exists( + ZipBackupName.DOWNLOAD, + false, + backupFolder.id, + ); + if (!legacyFile) { + throw new Error(getString('backupScreen.invalidBackupFolder')); + } + await download(legacyFile, ROOT_STORAGE); + } else { + for (const section of getSelectedBackupFileSections( + restoreResult.manifest.sections, + )) { + const file = await exists(section.archiveName, false, backupFolder.id); + if (!file) { + throw new Error(getString('backupScreen.invalidBackupFolder')); + } + await download(file, section.storagePath); + } + } + const missingPluginIds = await finalizeRestoredPlugins(restoreResult); + const completionText = getRestoreCompletionText( + restoreResult, + missingPluginIds, + ); setMeta(meta => ({ ...meta, progress: 3 / 3, isRunning: false, + progressText: completionText, + completionText, })); }; diff --git a/src/services/backup/fileSections.ts b/src/services/backup/fileSections.ts new file mode 100644 index 000000000..b5408f277 --- /dev/null +++ b/src/services/backup/fileSections.ts @@ -0,0 +1,29 @@ +import { NOVEL_STORAGE, PLUGIN_STORAGE } from '@utils/Storages'; +import type { BackupOptions } from './options'; +import { ZipBackupName } from './types'; + +export type BackupFileSection = { + archiveName: ZipBackupName; + storagePath: string; +}; + +export const getSelectedBackupFileSections = ( + options: BackupOptions, +): BackupFileSection[] => { + const sections: BackupFileSection[] = []; + + if (options.plugins) { + sections.push({ + archiveName: ZipBackupName.PLUGINS, + storagePath: PLUGIN_STORAGE, + }); + } + if (options.downloadedFiles) { + sections.push({ + archiveName: ZipBackupName.NOVEL_FILES, + storagePath: NOVEL_STORAGE, + }); + } + + return sections; +}; diff --git a/src/services/backup/local/index.ts b/src/services/backup/local/index.ts index 4ecbbeded..5655d71e7 100644 --- a/src/services/backup/local/index.ts +++ b/src/services/backup/local/index.ts @@ -1,26 +1,33 @@ -import { showToast } from '@utils/showToast'; -import dayjs from 'dayjs'; import { - saveDocuments, - pick, - types, - keepLocalCopy, -} from '@react-native-documents/picker'; -import { CACHE_DIR_PATH, prepareBackupData, restoreData } from '../utils'; -import NativeZipArchive from '@specs/NativeZipArchive'; + CACHE_DIR_PATH, + clearBackupCache, + prepareBackupData, + restoreData, +} from '../utils'; +import { + finalizeRestoredPlugins, + getRestoreCompletionText, +} from '../restoreResult'; +import { getBackupCompletionText } from '../backupResult'; +import NativeZipArchive from '@modules/native-zip-archive'; import { ROOT_STORAGE } from '@utils/Storages'; import { ZipBackupName } from '../types'; -import NativeFile from '@specs/NativeFile'; -import { getString } from '@strings/translations'; -import { BackgroundTaskMetadata } from '@services/ServiceManager'; +import NativeFile from '@modules/native-file'; +import { getString } from '@i18n/translations'; +import type { TaskProgressUpdater } from '@services/backgroundTasks/contracts'; import { sleep } from '@utils/sleep'; +import { getSelectedBackupFileSections } from '../fileSections'; +import { resolveBackupOptions, type BackupOptions } from '../options'; export const createBackup = async ( - setMeta?: ( - transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, - ) => void, + { + destinationUri, + options: requestedOptions, + }: { destinationUri: string; options?: BackupOptions }, + setMeta?: TaskProgressUpdater, ) => { try { + const options = resolveBackupOptions(requestedOptions); setMeta?.(meta => ({ ...meta, isRunning: true, @@ -28,20 +35,22 @@ export const createBackup = async ( progressText: getString('backupScreen.preparingData'), })); - await prepareBackupData(CACHE_DIR_PATH); + const backupResult = await prepareBackupData(CACHE_DIR_PATH, options); setMeta?.(meta => ({ ...meta, progress: 1 / 4, - progressText: getString('backupScreen.uploadingDownloadedFiles'), + progressText: getString('backupScreen.preparingSelectedFiles'), })); await sleep(200); - await NativeZipArchive.zip( - ROOT_STORAGE, - CACHE_DIR_PATH + '/' + ZipBackupName.DOWNLOAD, - ); + for (const section of getSelectedBackupFileSections(options)) { + await NativeZipArchive.zip( + section.storagePath, + `${CACHE_DIR_PATH}/${section.archiveName}`, + ); + } setMeta?.(meta => ({ ...meta, @@ -59,36 +68,28 @@ export const createBackup = async ( progressText: getString('backupScreen.savingBackup'), })); - const datetime = dayjs().format('YYYY-MM-DD_HH_mm'); - const fileName = 'lnreader_backup_' + datetime + '.zip'; - - await saveDocuments({ - sourceUris: ['file://' + CACHE_DIR_PATH + '.zip'], - copy: false, - mimeType: 'application/zip', - fileName, - }); + await NativeFile.copyFile(CACHE_DIR_PATH + '.zip', destinationUri); + const completionText = getBackupCompletionText(backupResult); setMeta?.(meta => ({ ...meta, progress: 4 / 4, isRunning: false, + progressText: completionText, + completionText, })); - - showToast(getString('backupScreen.backupCreated')); } catch (error: any) { setMeta?.(meta => ({ ...meta, isRunning: false, })); - showToast(error.message); + throw error; } }; export const restoreBackup = async ( - setMeta?: ( - transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, - ) => void, + { sourceUri }: { sourceUri: string }, + setMeta?: TaskProgressUpdater, ) => { try { setMeta?.(meta => ({ @@ -98,30 +99,9 @@ export const restoreBackup = async ( progressText: getString('backupScreen.downloadingData'), })); - const [result] = await pick({ - mode: 'import', - type: [types.zip], - allowVirtualFiles: true, // TODO: hopefully this just works - }); - - if (NativeFile.exists(CACHE_DIR_PATH)) { - NativeFile.unlink(CACHE_DIR_PATH); - } - - const [localRes] = await keepLocalCopy({ - files: [ - { - uri: result.uri, - fileName: 'backup.zip', - }, - ], - destination: 'cachesDirectory', - }); - if (localRes.status === 'error') { - throw new Error(localRes.copyError); - } - - const localPath = localRes.localUri.replace(/^file:(\/\/)?\//, '/'); + await clearBackupCache(); + const localPath = CACHE_DIR_PATH + '-source.zip'; + await NativeFile.copyFile(sourceUri, localPath); setMeta?.(meta => ({ ...meta, @@ -141,34 +121,51 @@ export const restoreBackup = async ( await sleep(200); - await restoreData(CACHE_DIR_PATH); + const restoreResult = await restoreData(CACHE_DIR_PATH, setMeta); setMeta?.(meta => ({ ...meta, progress: 3 / 4, - progressText: getString('backupScreen.downloadingDownloadedFiles'), + progressText: getString('backupScreen.restoringSelectedFiles'), })); await sleep(200); - // TODO: unlink here too? - await NativeZipArchive.unzip( - CACHE_DIR_PATH + '/' + ZipBackupName.DOWNLOAD, - ROOT_STORAGE, + if (restoreResult.manifest.formatVersion === 1) { + const legacyArchive = CACHE_DIR_PATH + '/' + ZipBackupName.DOWNLOAD; + if (!(await NativeFile.exists(legacyArchive))) { + throw new Error(getString('backupScreen.invalidBackupFolder')); + } + await NativeZipArchive.unzip(legacyArchive, ROOT_STORAGE); + } else { + for (const section of getSelectedBackupFileSections( + restoreResult.manifest.sections, + )) { + const archivePath = `${CACHE_DIR_PATH}/${section.archiveName}`; + if (!(await NativeFile.exists(archivePath))) { + throw new Error(getString('backupScreen.invalidBackupFolder')); + } + await NativeZipArchive.unzip(archivePath, section.storagePath); + } + } + const missingPluginIds = await finalizeRestoredPlugins(restoreResult); + const completionText = getRestoreCompletionText( + restoreResult, + missingPluginIds, ); setMeta?.(meta => ({ ...meta, progress: 4 / 4, isRunning: false, + progressText: completionText, + completionText, })); - - showToast(getString('backupScreen.backupRestored')); } catch (error: any) { setMeta?.(meta => ({ ...meta, isRunning: false, })); - showToast(error.message); + throw error; } }; diff --git a/src/services/backup/options.ts b/src/services/backup/options.ts new file mode 100644 index 000000000..b14b0e33c --- /dev/null +++ b/src/services/backup/options.ts @@ -0,0 +1,33 @@ +export type BackupOptions = { + library: boolean; + settings: boolean; + plugins: boolean; + downloadedFiles: boolean; +}; + +export const DEFAULT_BACKUP_OPTIONS: BackupOptions = { + library: true, + settings: true, + plugins: true, + downloadedFiles: true, +}; + +export const resolveBackupOptions = ( + options?: BackupOptions, +): BackupOptions => { + const resolved = { + ...DEFAULT_BACKUP_OPTIONS, + ...options, + }; + + return { + ...resolved, + downloadedFiles: resolved.library && resolved.downloadedFiles, + }; +}; + +export const hasSelectedBackupOption = (options: BackupOptions) => + Object.values(options).some(Boolean); + +export const areAllBackupOptionsSelected = (options: BackupOptions) => + Object.values(options).every(Boolean); diff --git a/src/services/backup/restoreResult.ts b/src/services/backup/restoreResult.ts new file mode 100644 index 000000000..161854e9c --- /dev/null +++ b/src/services/backup/restoreResult.ts @@ -0,0 +1,107 @@ +import { getString } from '@i18n/translations'; +import NativeFile from '@modules/native-file'; +import { + LOCAL_PLUGIN_ID, + reloadInstalledPlugins, +} from '@plugins/pluginManager'; +import { PLUGIN_STORAGE } from '@utils/Storages'; +import type { ResolvedBackupManifest } from './types'; + +export type RestoreResult = { + novelCount: number; + failedNovelCount: number; + categoryCount: number; + failedCategoryCount: number; + settingsRestored: boolean; + failedSectionCount: number; + pluginIds: string[]; + manifest: ResolvedBackupManifest; +}; + +export const getMissingRestorePluginIds = async (pluginIds: string[]) => { + const uniquePluginIds = [...new Set(pluginIds)].filter( + pluginId => pluginId !== LOCAL_PLUGIN_ID, + ); + const pluginAvailability = await Promise.all( + uniquePluginIds.map(async pluginId => ({ + pluginId, + exists: await NativeFile.exists(`${PLUGIN_STORAGE}/${pluginId}/index.js`), + })), + ); + + return pluginAvailability + .filter(plugin => !plugin.exists) + .map(plugin => plugin.pluginId); +}; + +export const finalizeRestoredPlugins = async (result: RestoreResult) => { + const failedPluginIds = result.manifest.sections.plugins + ? await reloadInstalledPlugins() + : []; + + return [ + ...new Set([ + ...(await getMissingRestorePluginIds(result.pluginIds)), + ...failedPluginIds, + ]), + ]; +}; + +export const getRestoreCompletionText = ( + result: RestoreResult, + missingPluginIds: string[], +) => { + const warnings: string[] = []; + + if (result.failedNovelCount > 0) { + warnings.push( + getString('backupScreen.novelsRestoreFailedSummary', { + count: result.failedNovelCount, + }), + ); + } + if (result.failedCategoryCount > 0) { + warnings.push( + getString('backupScreen.categoriesRestoreFailedSummary', { + count: result.failedCategoryCount, + }), + ); + } + if (!result.settingsRestored) { + warnings.push(getString('backupScreen.settingsRestoreFailedSummary')); + } + if (result.failedSectionCount > 0) { + warnings.push( + getString('backupScreen.sectionsRestoreFailedSummary', { + count: result.failedSectionCount, + }), + ); + } + if (missingPluginIds.length > 0) { + warnings.push( + getString('backupScreen.missingPluginsAfterRestore', { + plugins: missingPluginIds.join(', '), + }), + ); + } + + if (warnings.length === 0) { + if (!result.manifest.sections.library) { + return getString('backupScreen.backupRestored'); + } + return getString('backupScreen.backupRestoredSummary', { + count: result.novelCount, + }); + } + + if (!result.manifest.sections.library) { + return getString('backupScreen.backupRestoredSelectedWithWarnings', { + warnings: warnings.join('; '), + }); + } + + return getString('backupScreen.backupRestoredWithWarnings', { + count: result.novelCount, + warnings: warnings.join('; '), + }); +}; diff --git a/src/services/backup/selfhost/index.ts b/src/services/backup/selfhost/index.ts index 4c3a524f6..3d058e108 100644 --- a/src/services/backup/selfhost/index.ts +++ b/src/services/backup/selfhost/index.ts @@ -1,22 +1,31 @@ import { sleep } from '@utils/sleep'; import { download, upload } from '@api/remote'; -import { getString } from '@strings/translations'; -import { CACHE_DIR_PATH, prepareBackupData, restoreData } from '../utils'; +import { getString } from '@i18n/translations'; +import { + CACHE_DIR_PATH, + clearBackupCache, + prepareBackupData, + restoreData, +} from '../utils'; +import { + finalizeRestoredPlugins, + getRestoreCompletionText, +} from '../restoreResult'; +import { getBackupCompletionText } from '../backupResult'; import { ZipBackupName } from '../types'; import { ROOT_STORAGE } from '@utils/Storages'; -import { BackgroundTaskMetadata } from '@services/ServiceManager'; - -export interface SelfHostData { - host: string; - backupFolder: string; -} +import type { + SelfHostData, + TaskProgressUpdater, +} from '@services/backgroundTasks/contracts'; +import { getSelectedBackupFileSections } from '../fileSections'; +import { resolveBackupOptions } from '../options'; export const createSelfHostBackup = async ( - { host, backupFolder }: SelfHostData, - setMeta: ( - transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, - ) => void, + { host, backupFolder, options: requestedOptions }: SelfHostData, + setMeta: TaskProgressUpdater, ) => { + const options = resolveBackupOptions(requestedOptions); setMeta(meta => ({ ...meta, isRunning: true, @@ -24,7 +33,7 @@ export const createSelfHostBackup = async ( progressText: getString('backupScreen.preparingData'), })); - await prepareBackupData(CACHE_DIR_PATH); + const backupResult = await prepareBackupData(CACHE_DIR_PATH, options); setMeta(meta => ({ ...meta, @@ -39,25 +48,28 @@ export const createSelfHostBackup = async ( setMeta(meta => ({ ...meta, progress: 2 / 3, - progressText: getString('backupScreen.uploadingDownloadedFiles'), + progressText: getString('backupScreen.uploadingSelectedFiles'), })); await sleep(200); - await upload(host, backupFolder, ZipBackupName.DOWNLOAD, ROOT_STORAGE); + for (const section of getSelectedBackupFileSections(options)) { + await upload(host, backupFolder, section.archiveName, section.storagePath); + } + const completionText = getBackupCompletionText(backupResult); setMeta(meta => ({ ...meta, progress: 3 / 3, isRunning: false, + progressText: completionText, + completionText, })); }; export const selfHostRestore = async ( { host, backupFolder }: SelfHostData, - setMeta: ( - transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, - ) => void, + setMeta: TaskProgressUpdater, ) => { setMeta(meta => ({ ...meta, @@ -66,6 +78,7 @@ export const selfHostRestore = async ( progressText: getString('backupScreen.downloadingData'), })); + await clearBackupCache(); await download(host, backupFolder, ZipBackupName.DATA, CACHE_DIR_PATH); setMeta(meta => ({ @@ -76,21 +89,41 @@ export const selfHostRestore = async ( await sleep(200); - await restoreData(CACHE_DIR_PATH); + const restoreResult = await restoreData(CACHE_DIR_PATH, setMeta); setMeta(meta => ({ ...meta, progress: 2 / 3, - progressText: getString('backupScreen.downloadingDownloadedFiles'), + progressText: getString('backupScreen.restoringSelectedFiles'), })); await sleep(200); - await download(host, backupFolder, ZipBackupName.DOWNLOAD, ROOT_STORAGE); + if (restoreResult.manifest.formatVersion === 1) { + await download(host, backupFolder, ZipBackupName.DOWNLOAD, ROOT_STORAGE); + } else { + for (const section of getSelectedBackupFileSections( + restoreResult.manifest.sections, + )) { + await download( + host, + backupFolder, + section.archiveName, + section.storagePath, + ); + } + } + const missingPluginIds = await finalizeRestoredPlugins(restoreResult); + const completionText = getRestoreCompletionText( + restoreResult, + missingPluginIds, + ); setMeta(meta => ({ ...meta, progress: 3 / 3, isRunning: false, + progressText: completionText, + completionText, })); }; diff --git a/src/services/backup/types.ts b/src/services/backup/types.ts index fb119c616..a1fe83079 100644 --- a/src/services/backup/types.ts +++ b/src/services/backup/types.ts @@ -1,11 +1,30 @@ +import type { BackupOptions } from './options'; + export enum ZipBackupName { DATA = 'data.zip', DOWNLOAD = 'download.zip', + NOVEL_FILES = 'novel-files.zip', + PLUGINS = 'plugins.zip', } export enum BackupEntryName { VERSION = 'Version.json', CATEGORY = 'Category.json', SETTING = 'Setting.json', + PLUGIN_METADATA = 'Plugins.json', NOVEL_AND_CHAPTERS = 'NovelAndChapters', } + +export type BackupManifest = { + appVersion: string; + formatVersion: 2; + sections: BackupOptions; +}; + +export type ResolvedBackupManifest = + | BackupManifest + | { + appVersion?: string; + formatVersion: 1; + sections: BackupOptions; + }; diff --git a/src/services/backup/utils.ts b/src/services/backup/utils.ts index 63be2088c..55f914b43 100644 --- a/src/services/backup/utils.ts +++ b/src/services/backup/utils.ts @@ -14,24 +14,43 @@ import { getCategoriesFromDb, } from '@database/queries/CategoryQueries'; import { BackupCategory, BackupNovel } from '@database/types'; -import { BackupEntryName } from './types'; +import { + BackupEntryName, + type BackupManifest, + type ResolvedBackupManifest, +} from './types'; import { ROOT_STORAGE } from '@utils/Storages'; -import ServiceManager from '@services/ServiceManager'; -import NativeFile from '@specs/NativeFile'; -import { showToast } from '@utils/showToast'; -import { getString } from '@strings/translations'; +import { BACKGROUND_TASKS_STORE_KEY } from '@services/backgroundTasks/constants'; +import type { TaskProgressUpdater } from '@services/backgroundTasks/contracts'; +import NativeFile from '@modules/native-file'; +import { getString } from '@i18n/translations'; +import type { RestoreResult } from './restoreResult'; +import type { BackupResult } from './backupResult'; +import { + DEFAULT_BACKUP_OPTIONS, + resolveBackupOptions, + type BackupOptions, +} from './options'; +import { INSTALLED_PLUGINS_KEY } from '@plugins/pluginManager'; const APP_STORAGE_URI = 'file://' + ROOT_STORAGE; export const CACHE_DIR_PATH = - NativeFile.getConstants().ExternalCachesDirectoryPath + '/BackupData'; + NativeFile.ExternalCachesDirectoryPath + '/BackupData'; + +export const clearBackupCache = async (cacheDirPath = CACHE_DIR_PATH) => { + if (await NativeFile.exists(cacheDirPath)) { + await NativeFile.unlink(cacheDirPath); + } +}; -const backupMMKVData = () => { +const backupMMKVData = (options: BackupOptions) => { const excludeKeys = [ - ServiceManager.manager.STORE_KEY, + BACKGROUND_TASKS_STORE_KEY, OLD_TRACKED_NOVEL_PREFIX, SELF_HOST_BACKUP, LAST_UPDATE_TIME, + ...(options.plugins ? [] : [INSTALLED_PLUGINS_KEY]), ]; const keys = MMKVStorage.getAllKeys().filter( key => !excludeKeys.includes(key), @@ -56,221 +75,296 @@ const restoreMMKVData = (data: any) => { } }; -export const prepareBackupData = async (cacheDirPath: string) => { +export const prepareBackupData = async ( + cacheDirPath: string, + requestedOptions?: BackupOptions, +): Promise => { + const options = resolveBackupOptions(requestedOptions); const novelDirPath = cacheDirPath + '/' + BackupEntryName.NOVEL_AND_CHAPTERS; - if (NativeFile.exists(novelDirPath)) { - NativeFile.unlink(novelDirPath); - } + let failedNovelCount = 0; + let failedSectionCount = 0; - NativeFile.mkdir(novelDirPath); // this also creates cacheDirPath + await clearBackupCache(cacheDirPath); + await NativeFile.mkdir(cacheDirPath); // version - try { - NativeFile.writeFile( - cacheDirPath + '/' + BackupEntryName.VERSION, - JSON.stringify({ version: version }), - ); - } catch (error: any) { - showToast( - getString('backupScreen.versionFileWriteFailed', { - error: error?.message || String(error), - }), - ); - throw error; - } + const manifest: BackupManifest = { + appVersion: version, + formatVersion: 2, + sections: options, + }; + await NativeFile.writeFile( + cacheDirPath + '/' + BackupEntryName.VERSION, + JSON.stringify(manifest), + ); // novels - await getAllNovels().then(async novels => { - for (const novel of novels) { - try { - const chapters = await getNovelChapters(novel.id); - NativeFile.writeFile( - novelDirPath + '/' + novel.id + '.json', - JSON.stringify({ - chapters: chapters, - ...novel, - cover: novel.cover?.replace(APP_STORAGE_URI, ''), - }), - ); - } catch (error: any) { - showToast( - getString('backupScreen.novelBackupFailed', { - novelName: novel.name, - error: error?.message, - }), - ); + if (options.library) { + await NativeFile.mkdir(novelDirPath); + await getAllNovels().then(async novels => { + for (const novel of novels) { + try { + const chapters = await getNovelChapters(novel.id); + const backedUpChapters = options.downloadedFiles + ? chapters + : chapters.map(chapter => ({ + ...chapter, + isDownloaded: false, + })); + const isStoredCover = novel.cover?.startsWith(APP_STORAGE_URI); + await NativeFile.writeFile( + novelDirPath + '/' + novel.id + '.json', + JSON.stringify({ + chapters: backedUpChapters, + ...novel, + cover: + !options.downloadedFiles && isStoredCover + ? null + : novel.cover?.replace(APP_STORAGE_URI, ''), + }), + ); + } catch { + failedNovelCount++; + } } - } - }); + }); - // categories - try { - const categories = await getCategoriesFromDb(); - const novelCategories = await getAllNovelCategories(); - NativeFile.writeFile( - cacheDirPath + '/' + BackupEntryName.CATEGORY, - JSON.stringify( - categories.map(category => { - return { - ...category, - novelIds: novelCategories - .filter(nc => nc.categoryId === category.id) - .map(nc => nc.novelId), - }; - }), - ), - ); - } catch (error: any) { - showToast( - getString('backupScreen.categoryFileWriteFailed', { - error: error?.message || String(error), - }), - ); + // categories + try { + const categories = await getCategoriesFromDb(); + const novelCategories = await getAllNovelCategories(); + await NativeFile.writeFile( + cacheDirPath + '/' + BackupEntryName.CATEGORY, + JSON.stringify( + categories.map(category => { + return { + ...category, + novelIds: novelCategories + .filter(nc => nc.categoryId === category.id) + .map(nc => nc.novelId), + }; + }), + ), + ); + } catch { + failedSectionCount++; + } } // settings + if (options.settings) { + try { + await NativeFile.writeFile( + cacheDirPath + '/' + BackupEntryName.SETTING, + JSON.stringify(backupMMKVData(options)), + ); + } catch { + failedSectionCount++; + } + } + + // installed plugin registry + if (options.plugins) { + try { + await NativeFile.writeFile( + cacheDirPath + '/' + BackupEntryName.PLUGIN_METADATA, + MMKVStorage.getString(INSTALLED_PLUGINS_KEY) ?? '[]', + ); + } catch { + failedSectionCount++; + } + } + + return { + failedNovelCount, + failedSectionCount, + }; +}; + +const getBackupManifest = async ( + cacheDirPath: string, +): Promise => { try { - NativeFile.writeFile( - cacheDirPath + '/' + BackupEntryName.SETTING, - JSON.stringify(backupMMKVData()), - ); - } catch (error: any) { - showToast( - getString('backupScreen.settingsFileWriteFailed', { - error: error?.message || String(error), - }), + const fileContent = await NativeFile.readFile( + cacheDirPath + '/' + BackupEntryName.VERSION, ); + const data = JSON.parse(fileContent) as Partial & { + version?: string; + }; + if (data.formatVersion === 2 && data.sections) { + return { + appVersion: data.appVersion ?? data.version ?? '', + formatVersion: 2, + sections: resolveBackupOptions(data.sections), + }; + } + + return { + appVersion: data.version, + formatVersion: 1, + sections: DEFAULT_BACKUP_OPTIONS, + }; + } catch { + return { + formatVersion: 1, + sections: DEFAULT_BACKUP_OPTIONS, + }; } }; -export const restoreData = async (cacheDirPath: string) => { +const updateRestoreProgress = ( + setMeta: TaskProgressUpdater | undefined, + progressText: string, +) => { + setMeta?.(meta => ({ + ...meta, + progressText, + })); +}; + +export const restoreData = async ( + cacheDirPath: string, + setMeta?: TaskProgressUpdater, +): Promise => { + const manifest = await getBackupManifest(cacheDirPath); const novelDirPath = cacheDirPath + '/' + BackupEntryName.NOVEL_AND_CHAPTERS; + const pluginIds = new Set(); // version // nothing to do // novels - showToast(getString('backupScreen.restoringNovels')); + if (manifest.sections.library) { + updateRestoreProgress(setMeta, getString('backupScreen.restoringNovels')); + } let novelCount = 0; let failedCount = 0; + let failedSectionCount = 0; - if (!NativeFile.exists(novelDirPath)) { - showToast(getString('backupScreen.novelDirectoryNotFound')); + if (!manifest.sections.library) { + // Intentionally omitted from this backup. + } else if (!(await NativeFile.exists(novelDirPath))) { + failedSectionCount++; } else { try { - const items = NativeFile.readDir(novelDirPath); - for (const item of items) { - if (!item.isDirectory) { - try { - const fileContent = NativeFile.readFile(item.path); - const backupNovel = JSON.parse(fileContent) as BackupNovel; - - if (!backupNovel.cover?.startsWith('http')) { - backupNovel.cover = APP_STORAGE_URI + backupNovel.cover; - } + const items = (await NativeFile.readDir(novelDirPath)).filter( + item => !item.isDirectory, + ); + for (const [index, item] of items.entries()) { + updateRestoreProgress( + setMeta, + getString('backupScreen.restoringNovelsProgress', { + current: index + 1, + total: items.length, + }), + ); + try { + const fileContent = await NativeFile.readFile(item.path); + const backupNovel = JSON.parse(fileContent) as BackupNovel; + pluginIds.add(backupNovel.pluginId); - await _restoreNovelAndChapters(backupNovel); - novelCount++; - } catch (error: any) { - failedCount++; - const novelName = - item.path.split('/').pop()?.replace('.json', '') || 'Unknown'; - showToast( - getString('backupScreen.novelRestoreFailed', { - novelName: novelName, - error: error?.message || String(error), - }), - ); + if (!backupNovel.cover?.startsWith('http')) { + backupNovel.cover = APP_STORAGE_URI + backupNovel.cover; } + + await _restoreNovelAndChapters(backupNovel); + novelCount++; + } catch { + failedCount++; } } - } catch (error: any) { - showToast( - getString('backupScreen.novelDirectoryReadFailed', { - error: error?.message || String(error), - }), - ); + } catch { + failedSectionCount++; } } - if (failedCount > 0) { - showToast( - getString('backupScreen.novelsRestoredWithErrors', { - count: novelCount, - failedCount: failedCount, - }), - ); - } else { - showToast(getString('backupScreen.novelsRestored', { count: novelCount })); - } // categories - showToast(getString('backupScreen.restoringCategories')); + if (manifest.sections.library) { + updateRestoreProgress( + setMeta, + getString('backupScreen.restoringCategories'), + ); + } const categoryFilePath = cacheDirPath + '/' + BackupEntryName.CATEGORY; let categoryCount = 0; let failedCategoryCount = 0; - if (!NativeFile.exists(categoryFilePath)) { - showToast(getString('backupScreen.categoryFileNotFound')); + if (!manifest.sections.library) { + // Intentionally omitted from this backup. + } else if (!(await NativeFile.exists(categoryFilePath))) { + failedSectionCount++; } else { try { - const fileContent = NativeFile.readFile(categoryFilePath); + const fileContent = await NativeFile.readFile(categoryFilePath); const categories: BackupCategory[] = JSON.parse(fileContent); - for (const category of categories) { + for (const [index, category] of categories.entries()) { + updateRestoreProgress( + setMeta, + getString('backupScreen.restoringCategoriesProgress', { + current: index + 1, + total: categories.length, + }), + ); try { - _restoreCategory(category); + await _restoreCategory(category); categoryCount++; - } catch (error: any) { + } catch { failedCategoryCount++; - showToast( - getString('backupScreen.categoryRestoreFailed', { - categoryName: category.name || category.id.toString(), - error: error?.message || String(error), - }), - ); } } - } catch (error: any) { - showToast( - getString('backupScreen.categoryFileReadFailed', { - error: error?.message || String(error), - }), - ); + } catch { + failedSectionCount++; } } - if (failedCategoryCount > 0) { - showToast( - getString('backupScreen.categoriesRestoredWithErrors', { - count: categoryCount, - failedCount: failedCategoryCount, - }), - ); - } else { - showToast( - getString('backupScreen.categoriesRestored', { - count: categoryCount, - }), - ); - } // settings - showToast(getString('backupScreen.restoringSettings')); + if (manifest.sections.settings) { + updateRestoreProgress(setMeta, getString('backupScreen.restoringSettings')); + } const settingsFilePath = cacheDirPath + '/' + BackupEntryName.SETTING; + let settingsRestored = !manifest.sections.settings; - if (!NativeFile.exists(settingsFilePath)) { - showToast(getString('backupScreen.settingsFileNotFound')); + if (!manifest.sections.settings) { + // Intentionally omitted from this backup. + } else if (!(await NativeFile.exists(settingsFilePath))) { + // Reported as a settings warning in the completion summary. } else { try { - const fileContent = NativeFile.readFile(settingsFilePath); + const fileContent = await NativeFile.readFile(settingsFilePath); const settingsData = JSON.parse(fileContent); restoreMMKVData(settingsData); - showToast(getString('backupScreen.settingsRestored')); - } catch (error: any) { - showToast( - getString('backupScreen.settingsRestoreFailed', { - error: error?.message || String(error), - }), - ); + settingsRestored = true; + } catch { + // Included in the completion warning below. } } + + // installed plugin registry + if (manifest.formatVersion === 2 && manifest.sections.plugins) { + const pluginMetadataPath = + cacheDirPath + '/' + BackupEntryName.PLUGIN_METADATA; + if (!(await NativeFile.exists(pluginMetadataPath))) { + failedSectionCount++; + } else { + try { + const installedPlugins = await NativeFile.readFile(pluginMetadataPath); + JSON.parse(installedPlugins); + MMKVStorage.set(INSTALLED_PLUGINS_KEY, installedPlugins); + } catch { + failedSectionCount++; + } + } + } + + return { + novelCount, + failedNovelCount: failedCount, + categoryCount, + failedCategoryCount, + settingsRestored, + failedSectionCount, + pluginIds: [...pluginIds], + manifest, + }; }; diff --git a/src/services/download/__tests__/downloadCheckpoint.test.ts b/src/services/download/__tests__/downloadCheckpoint.test.ts new file mode 100644 index 000000000..0feb37e29 --- /dev/null +++ b/src/services/download/__tests__/downloadCheckpoint.test.ts @@ -0,0 +1,28 @@ +import { parseDownloadCheckpoint } from '../downloadCheckpoint'; + +describe('download checkpoint', () => { + it('restores the next chapter and previous failures', () => { + expect( + parseDownloadCheckpoint( + JSON.stringify({ nextIndex: 50, failures: ['Chapter 12 failed'] }), + 200, + ), + ).toEqual({ nextIndex: 50, failures: ['Chapter 12 failed'] }); + }); + + it('clamps a checkpoint to the current batch size', () => { + expect( + parseDownloadCheckpoint( + JSON.stringify({ nextIndex: 250, failures: [] }), + 200, + ).nextIndex, + ).toBe(200); + }); + + it.each([undefined, 'invalid', JSON.stringify({ nextIndex: '50' })])( + 'starts from the beginning for an invalid checkpoint', + checkpoint => { + expect(parseDownloadCheckpoint(checkpoint, 200).nextIndex).toBe(0); + }, + ); +}); diff --git a/src/services/download/downloadChapter.ts b/src/services/download/downloadChapter.ts index 3df63e979..0d9e74128 100644 --- a/src/services/download/downloadChapter.ts +++ b/src/services/download/downloadChapter.ts @@ -3,15 +3,20 @@ import { NOVEL_STORAGE } from '@utils/Storages'; import { Plugin } from '@plugins/types'; import { downloadFile } from '@plugins/helpers/fetch'; import { getPlugin } from '@plugins/pluginManager'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { getChapter } from '@database/queries/ChapterQueries'; import { sleep } from '@utils/sleep'; +import { getChapterDownloadCooldownMs } from '@hooks/persisted/useSettings'; import { getNovelById } from '@database/queries/NovelQueries'; import { dbManager } from '@database/db'; import { chapterSchema } from '@database/schema'; -import { BackgroundTaskMetadata } from '@services/ServiceManager'; -import NativeFile from '@specs/NativeFile'; +import type { + BackgroundTaskExecutionContext, + TaskProgressUpdater, +} from '@services/backgroundTasks/contracts'; +import NativeFile from '@modules/native-file'; import { eq } from 'drizzle-orm'; +import { parseDownloadCheckpoint } from './downloadCheckpoint'; const createChapterFolder = async ( path: string, @@ -23,9 +28,9 @@ const createChapterFolder = async ( ): Promise => { const { pluginId, novelId, chapterId } = data; const chapterFolder = `${path}/${pluginId}/${novelId}/${chapterId}`; - NativeFile.mkdir(chapterFolder); + await NativeFile.mkdir(chapterFolder); const nomediaPath = chapterFolder + '/.nomedia'; - NativeFile.writeFile(nomediaPath, ','); + await NativeFile.writeFile(nomediaPath, ','); return chapterFolder; }; @@ -56,20 +61,10 @@ const downloadFiles = async ( } } } - NativeFile.writeFile(folder + '/index.html', loadedCheerio.html()); + await NativeFile.writeFile(folder + '/index.html', loadedCheerio.html()); }; -export const downloadChapter = async ( - { chapterId }: { chapterId: number }, - setMeta: ( - transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, - ) => void, -) => { - setMeta(meta => ({ - ...meta, - isRunning: true, - })); - +const downloadChapter = async (chapterId: number) => { const chapter = await getChapter(chapterId); if (!chapter) { throw new Error('Chapter not found with id: ' + chapterId); @@ -96,14 +91,65 @@ export const downloadChapter = async ( .run(); }); - await sleep(1000); + await sleep(getChapterDownloadCooldownMs()); } else { throw new Error(getString('downloadScreen.chapterEmptyOrScrapeError')); } +}; + +export const downloadChapters = async ( + { + chapters, + }: { + novelName: string; + chapters: { chapterId: number; chapterName: string }[]; + }, + setMeta: TaskProgressUpdater, + context: BackgroundTaskExecutionContext, +) => { + if (!chapters.length) return; + + const checkpoint = parseDownloadCheckpoint( + context.checkpoint, + chapters.length, + ); + const failures = [...checkpoint.failures]; + + for (let index = checkpoint.nextIndex; index < chapters.length; index++) { + const chapter = chapters[index]; + setMeta(meta => ({ + ...meta, + isRunning: true, + progress: index / chapters.length, + progressText: `${index + 1}/${chapters.length} · ${chapter.chapterName}`, + })); + + try { + await downloadChapter(chapter.chapterId); + } catch (error) { + failures.push( + `${chapter.chapterName}: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + } + + await context.updateCheckpoint( + JSON.stringify({ nextIndex: index + 1, failures }), + ); + } setMeta(meta => ({ ...meta, progress: 1, isRunning: false, })); + + if (failures.length) { + throw new Error( + `${failures.length} of ${ + chapters.length + } chapters failed: ${failures.join('; ')}`, + ); + } }; diff --git a/src/services/download/downloadCheckpoint.ts b/src/services/download/downloadCheckpoint.ts new file mode 100644 index 000000000..6aeb63e0e --- /dev/null +++ b/src/services/download/downloadCheckpoint.ts @@ -0,0 +1,29 @@ +export type DownloadCheckpoint = { + nextIndex: number; + failures: string[]; +}; + +export const parseDownloadCheckpoint = ( + checkpoint: string | undefined, + chapterCount: number, +): DownloadCheckpoint => { + if (!checkpoint) return { nextIndex: 0, failures: [] }; + + try { + const parsed = JSON.parse(checkpoint) as Partial; + return { + nextIndex: + typeof parsed.nextIndex === 'number' && + Number.isInteger(parsed.nextIndex) + ? Math.min(Math.max(parsed.nextIndex, 0), chapterCount) + : 0, + failures: Array.isArray(parsed.failures) + ? parsed.failures.filter( + (failure): failure is string => typeof failure === 'string', + ) + : [], + }; + } catch { + return { nextIndex: 0, failures: [] }; + } +}; diff --git a/src/services/epub/__tests__/export.test.ts b/src/services/epub/__tests__/export.test.ts new file mode 100644 index 000000000..772412072 --- /dev/null +++ b/src/services/epub/__tests__/export.test.ts @@ -0,0 +1,123 @@ +import NativeFile from '@modules/native-file'; +import { epub } from '@modules/nitro-epub'; +import type { + BackgroundTaskMetadata, + EpubExportData, +} from '@services/backgroundTasks/contracts'; +import { exportEpub } from '../export'; + +jest.mock('@modules/nitro-epub', () => ({ + epub: { + exportEpub: jest.fn(), + }, +})); + +jest.mock('@i18n/translations', () => ({ + getString: (key: string) => key, +})); + +const data: EpubExportData = { + novelName: 'Example Novel', + destinationUri: '/storage/emulated/0/Download', + fileName: 'Example Novel.epub', + chapters: [ + { + title: 'Chapter 1', + htmlPath: '/novels/1/1/index.html', + novelId: '1', + chapterId: '1', + }, + ], + metadata: { + title: 'Example Novel', + language: 'en', + coverPath: '', + description: '', + author: '', + bookId: 'urn:lnreader:test', + stylesheet: '', + javascript: '', + }, +}; + +const createProgressUpdater = () => { + let metadata: BackgroundTaskMetadata = { + name: 'Exporting EPUB', + isRunning: false, + progress: 0, + progressText: '', + }; + return { + getMetadata: () => metadata, + updateProgress: jest.fn(transformer => { + metadata = transformer(metadata); + }), + }; +}; + +describe('exportEpub', () => { + beforeEach(() => { + jest.spyOn(Date, 'now').mockReturnValue(1234); + jest.mocked(epub.exportEpub).mockResolvedValue({ + outputPath: '/mock/caches/epub-export-1234.epub', + chapterCount: 1, + }); + jest.mocked(NativeFile.copyFileToDirectory).mockResolvedValue({ + uri: '/storage/emulated/0/Download/Example Novel.epub', + size: 4096, + }); + jest.mocked(NativeFile.exists).mockResolvedValue(true); + jest.mocked(NativeFile.unlink).mockResolvedValue(undefined); + }); + + it('replaces the named destination through NativeFile', async () => { + const progress = createProgressUpdater(); + + await exportEpub(data, progress.updateProgress); + + expect(NativeFile.copyFileToDirectory).toHaveBeenCalledWith( + '/mock/caches/epub-export-1234.epub', + '/storage/emulated/0/Download', + 'Example Novel.epub', + 'application/epub+zip', + true, + ); + expect(progress.getMetadata()).toMatchObject({ + isRunning: false, + progress: 1, + completionText: 'novelScreen.epub.exportSuccess', + }); + }); + + it('rejects an empty destination copy instead of reporting success', async () => { + jest.mocked(NativeFile.copyFileToDirectory).mockResolvedValue({ + uri: '/storage/emulated/0/Download/Example Novel.epub', + size: 0, + }); + const progress = createProgressUpdater(); + + await expect(exportEpub(data, progress.updateProgress)).rejects.toThrow( + 'Exported EPUB is empty', + ); + + expect(progress.getMetadata().completionText).toBeUndefined(); + expect(NativeFile.unlink).toHaveBeenCalledWith( + '/mock/caches/epub-export-1234.epub', + ); + }); + + it('cleans up the generated cache file when destination replacement fails', async () => { + jest + .mocked(NativeFile.copyFileToDirectory) + .mockRejectedValue(new Error('Destination replacement failed')); + const progress = createProgressUpdater(); + + await expect(exportEpub(data, progress.updateProgress)).rejects.toThrow( + 'Destination replacement failed', + ); + + expect(NativeFile.unlink).toHaveBeenCalledWith( + '/mock/caches/epub-export-1234.epub', + ); + }); +}); diff --git a/src/services/epub/export.ts b/src/services/epub/export.ts new file mode 100644 index 000000000..ee3f31449 --- /dev/null +++ b/src/services/epub/export.ts @@ -0,0 +1,114 @@ +import NativeFile from '@modules/native-file'; +import { epub } from '@modules/nitro-epub'; + +import { getString } from '@i18n/translations'; +import type { + EpubExportData, + TaskProgressUpdater, +} from '@services/backgroundTasks/contracts'; + +const sanitizeEpubFileName = (fileName: string) => { + const withoutExtension = fileName.trim().replace(/\.epub$/i, ''); + return ( + withoutExtension + .replace(/[\\/:*?"<>|\u0000-\u001f]/g, '') + .replace(/[. ]+$/g, '') + .trim() || 'novel' + ); +}; + +const PROGRESS_UPDATE_INTERVAL_MS = 250; + +export const exportEpub = async ( + data: EpubExportData, + updateProgress: TaskProgressUpdater, +) => { + const { chapters, destinationUri, fileName, metadata } = data; + const tempEpubPath = `${ + NativeFile.ExternalCachesDirectoryPath + }/epub-export-${Date.now()}.epub`; + let lastProgressUpdateAt = 0; + + try { + updateProgress(meta => ({ + ...meta, + isRunning: true, + progress: 0, + progressText: getString('novelScreen.epub.preparingExport'), + })); + + const result = await epub.exportEpub( + metadata, + chapters, + tempEpubPath, + async (completedChapters, totalChapters, chapterTitle) => { + const total = Math.max(1, Math.round(totalChapters)); + const completed = Math.min( + total, + Math.max(0, Math.round(completedChapters)), + ); + const now = Date.now(); + if ( + completed < total && + now - lastProgressUpdateAt < PROGRESS_UPDATE_INTERVAL_MS + ) { + return; + } + lastProgressUpdateAt = now; + + updateProgress(meta => ({ + ...meta, + progress: (completed / total) * 0.95, + progressText: getString('novelScreen.epub.exportingChapter', { + current: completed, + total, + chapter: chapterTitle, + }), + })); + }, + ); + + updateProgress(meta => ({ + ...meta, + progress: 0.95, + progressText: getString('novelScreen.epub.savingExport'), + })); + + const epubFileName = `${sanitizeEpubFileName(fileName)}.epub`; + const copyResult = await NativeFile.copyFileToDirectory( + result.outputPath, + destinationUri, + epubFileName, + 'application/epub+zip', + true, + ); + if (copyResult.size <= 0) { + throw new Error('Exported EPUB is empty'); + } + + const completionText = getString('novelScreen.epub.exportSuccess', { + chapters: result.chapterCount.toString(), + }); + updateProgress(meta => ({ + ...meta, + isRunning: false, + progress: 1, + progressText: completionText, + completionText, + })); + } catch (error) { + updateProgress(meta => ({ + ...meta, + isRunning: false, + })); + throw error; + } finally { + try { + if (await NativeFile.exists(tempEpubPath)) { + await NativeFile.unlink(tempEpubPath); + } + } catch { + // Export cleanup must not replace the original result or error. + } + } +}; diff --git a/src/services/epub/import.ts b/src/services/epub/import.ts index f6ba99b41..f018a8f36 100644 --- a/src/services/epub/import.ts +++ b/src/services/epub/import.ts @@ -4,14 +4,19 @@ import { updateNovelInfo, } from '@database/queries/NovelQueries'; import { LOCAL_PLUGIN_ID } from '@plugins/pluginManager'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; import { NOVEL_STORAGE } from '@utils/Storages'; import { dbManager } from '@database/db'; import { novelSchema, chapterSchema } from '@database/schema'; -import { BackgroundTaskMetadata } from '@services/ServiceManager'; -import NativeFile from '@specs/NativeFile'; -import NativeZipArchive from '@specs/NativeZipArchive'; -import NativeEpub from '@specs/NativeEpub'; +import type { + BackgroundTaskMetadata, + EpubImportFile, + TaskProgressUpdater, +} from '@services/backgroundTasks/contracts'; +import NativeFile from '@modules/native-file'; +import NativeZipArchive from '@modules/native-zip-archive'; +import { epub } from '@modules/nitro-epub'; +import { showToast } from '@utils/showToast'; const decodePath = (path: string) => { try { @@ -39,13 +44,13 @@ const insertLocalNovel = async ( if (insertId !== undefined && insertId >= 0) { await updateNovelCategoryById(insertId, [2]); const novelDir = NOVEL_STORAGE + '/local/' + insertId; - NativeFile.mkdir(novelDir); + await NativeFile.mkdir(novelDir); const newCoverPath = `file://${novelDir}/${cover?.split(/[/\\]/).pop()}`; if (cover) { const decodedPath = decodePath(cover); - if (NativeFile.exists(decodedPath)) { - NativeFile.moveFile(decodedPath, newCoverPath); + if (await NativeFile.exists(decodedPath)) { + await NativeFile.moveFile(decodedPath, newCoverPath); } } await updateNovelInfo({ @@ -89,7 +94,7 @@ const insertLocalChapter = async ( if (insertId !== undefined && insertId >= 0) { let chapterText: string = ''; - chapterText = NativeFile.readFile(decodePath(path)); + chapterText = await NativeFile.readFile(decodePath(path)); if (!chapterText) { return []; } @@ -100,24 +105,19 @@ const insertLocalChapter = async ( return `="file://${novelDir}/${$2.split(/[/\\]/).pop()}"`; }, ); - NativeFile.mkdir(novelDir + '/' + insertId); - NativeFile.writeFile(`${novelDir}/${insertId}/index.html`, chapterText); + await NativeFile.mkdir(novelDir + '/' + insertId); + await NativeFile.writeFile( + `${novelDir}/${insertId}/index.html`, + chapterText, + ); return; } throw new Error(getString('advancedSettingsScreen.chapterInsertFailed')); }; export const importEpub = async ( - { - uri, - filename, - }: { - uri: string; - filename: string; - }, - setMeta: ( - transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, - ) => void, + { uri, filename }: EpubImportFile, + setMeta: TaskProgressUpdater, ) => { setMeta(meta => ({ ...meta, @@ -125,79 +125,134 @@ export const importEpub = async ( progress: 0, })); - const epubFilePath = - NativeFile.getConstants().ExternalCachesDirectoryPath + '/novel.epub'; + const epubFilePath = NativeFile.ExternalCachesDirectoryPath + '/novel.epub'; + const epubDirPath = NativeFile.ExternalCachesDirectoryPath + '/epub'; + try { - NativeFile.copyFile(uri, epubFilePath); - } catch { - throw new Error( - `Failed to read EPUB file "${filename}". The file may have been moved or deleted. Please try importing again.`, + if (await NativeFile.exists(epubDirPath)) { + await NativeFile.unlink(epubDirPath); + } + await NativeFile.mkdir(epubDirPath); + await NativeFile.copyFile(uri, epubFilePath); + await NativeZipArchive.unzip(epubFilePath, epubDirPath); + + const novel = await epub.parseNovelAndChapters(epubDirPath); + if (!novel.name) { + novel.name = filename.replace('.epub', '') || 'Untitled'; + } + const novelId = await insertLocalNovel( + novel.name, + epubDirPath + novel.name, // temporary + novel.cover || '', + novel.author || '', + novel.artist || '', + novel.summary || '', ); - } - const epubDirPath = - NativeFile.getConstants().ExternalCachesDirectoryPath + '/epub'; - if (NativeFile.exists(epubDirPath)) { - NativeFile.unlink(epubDirPath); - } - NativeFile.mkdir(epubDirPath); - await NativeZipArchive.unzip(epubFilePath, epubDirPath); - const novel = NativeEpub.parseNovelAndChapters(epubDirPath); - if (!novel.name) { - novel.name = filename.replace('.epub', '') || 'Untitled'; - } - const novelId = await insertLocalNovel( - novel.name, - epubDirPath + novel.name, // temporary - novel.cover || '', - novel.author || '', - novel.artist || '', - novel.summary || '', - ); - const now = dayjs().toISOString(); - if (novel.chapters) { - for (let i = 0; i < novel.chapters?.length; i++) { - const chapter = novel.chapters[i]; - if (!chapter.name) { - chapter.name = chapter.path.split(/[/\\]/).pop() || 'unknown'; + const now = dayjs().toISOString(); + if (novel.chapters) { + for (let i = 0; i < novel.chapters?.length; i++) { + const chapter = novel.chapters[i]; + if (!chapter.name) { + chapter.name = chapter.path.split(/[/\\]/).pop() || 'unknown'; + } + + setMeta(meta => ({ + ...meta, + progressText: chapter.name, + })); + + await insertLocalChapter(novelId, i, chapter.name, chapter.path, now); + + setMeta(meta => ({ + ...meta, + progress: i / novel.chapters.length, + })); } + } + const novelDir = NOVEL_STORAGE + '/local/' + novelId; - setMeta(meta => ({ - ...meta, - progressText: chapter.name, - })); + setMeta(meta => ({ + ...meta, + progressText: getString('advancedSettingsScreen.importStaticFiles'), + })); - await insertLocalChapter(novelId, i, chapter.name, chapter.path, now); + for (const filePath of novel.imagePaths) { + const decodedPath = decodePath(filePath); - setMeta(meta => ({ - ...meta, - progress: i / novel.chapters.length, - })); + if (await NativeFile.exists(decodedPath)) { + await NativeFile.moveFile( + decodedPath, + novelDir + '/' + filePath.split(/[/\\]/).pop(), + ); + } } + + for (const filePath of novel.cssPaths) { + const decodedPath = decodePath(filePath); + if (await NativeFile.exists(decodedPath)) { + await NativeFile.moveFile( + decodedPath, + novelDir + '/' + filePath.split(/[/\\]/).pop(), + ); + } + } + } catch (error) { + showToast( + getString('advancedSettingsScreen.importFailed'), + (error as Error).message, + ); } - const novelDir = NOVEL_STORAGE + '/local/' + novelId; setMeta(meta => ({ ...meta, - progressText: getString('advancedSettingsScreen.importStaticFiles'), + progress: 1, + isRunning: false, })); +}; - for (const filePath of novel.imagePaths) { - const decodedPath = decodePath(filePath); +export const importEpubBatch = async ( + { files }: { files: EpubImportFile[] }, + setMeta: TaskProgressUpdater, +) => { + if (!files.length) return; - if (NativeFile.exists(decodedPath)) { - NativeFile.moveFile( - decodedPath, - novelDir + '/' + filePath.split(/[/\\]/).pop(), - ); - } - } + const failures: string[] = []; + + for (let index = 0; index < files.length; index++) { + const file = files[index]; + let fileMeta: BackgroundTaskMetadata = { + name: file.filename, + isRunning: true, + progress: 0, + progressText: undefined, + }; + let progressError: unknown; + const updateFileProgress: TaskProgressUpdater = transformer => { + fileMeta = transformer(fileMeta); + try { + setMeta(meta => ({ + ...meta, + isRunning: true, + progress: (index + (fileMeta.progress ?? 0)) / files.length, + progressText: `${index + 1}/${files.length} · ${file.filename}${ + fileMeta.progressText ? ` · ${fileMeta.progressText}` : '' + }`, + })); + } catch (error) { + progressError = error; + throw error; + } + }; + + try { + await importEpub(file, updateFileProgress); + } catch (error) { + if (error === progressError) throw error; - for (const filePath of novel.cssPaths) { - const decodedPath = decodePath(filePath); - if (NativeFile.exists(decodedPath)) { - NativeFile.moveFile( - decodedPath, - novelDir + '/' + filePath.split(/[/\\]/).pop(), + failures.push( + `${file.filename}: ${ + error instanceof Error ? error.message : String(error) + }`, ); } } @@ -207,4 +262,12 @@ export const importEpub = async ( progress: 1, isRunning: false, })); + + if (failures.length) { + throw new Error( + `${failures.length} of ${ + files.length + } EPUB imports failed: ${failures.join('; ')}`, + ); + } }; diff --git a/src/services/migrate/migrateNovel.ts b/src/services/migrate/migrateNovel.ts index a28ff11d0..b48c2f245 100644 --- a/src/services/migrate/migrateNovel.ts +++ b/src/services/migrate/migrateNovel.ts @@ -1,4 +1,4 @@ -import { NovelInfo, ChapterInfo } from '@database/types'; +import { ChapterInfo } from '@database/types'; import { getNovelByPath, insertNovelAndChapters, @@ -8,15 +8,18 @@ import { getNovelChapters } from '@database/queries/ChapterQueries'; import { fetchNovel } from '@services/plugin/fetch'; import { parseChapterNumber } from '@utils/parseChapterNumber'; -import { getMMKVObject, setMMKVObject } from '@utils/mmkv/mmkv'; import { - LAST_READ_PREFIX, - NOVEL_SETTINGS_PREFIX, -} from '@hooks/persisted/useNovel'; -import { sleep } from '@utils/sleep'; -import ServiceManager, { - BackgroundTaskMetadata, -} from '@services/ServiceManager'; + novelPersistence, + type NovelPersistenceInput, +} from '@hooks/persisted/useNovel/store-helper/contracts'; +import type { + BackgroundTaskEnqueuer, + ChapterDownload, + MigrateNovelData, + MigrationNovelOptions, + MigrationNovelPreference, + TaskProgressUpdater, +} from '@services/backgroundTasks/contracts'; import { dbManager } from '@database/db'; import { chapterSchema, @@ -25,12 +28,6 @@ import { } from '@database/schema'; import { eq } from 'drizzle-orm'; -export interface MigrateNovelData { - pluginId: string; - fromNovel: NovelInfo; - toNovelPath: string; -} - const sortChaptersByNumber = (novelName: string, chapters: ChapterInfo[]) => { for (let i = 0; i < chapters.length; ++i) { if (!chapters[i].chapterNumber) { @@ -48,11 +45,30 @@ const sortChaptersByNumber = (novelName: string, chapters: ChapterInfo[]) => { }); }; +const LEGACY_MIGRATION_OPTIONS: MigrationNovelOptions = { + cover: 'current', + metadata: 'current', + redownloadChapters: true, +}; + +const selectMigrationValue = ( + currentValue: string | null | undefined, + destinationValue: string | null | undefined, + preference: MigrationNovelPreference, +) => + preference === 'destination' + ? destinationValue || currentValue || '' + : currentValue || destinationValue || ''; + export const migrateNovel = async ( - { pluginId, fromNovel, toNovelPath }: MigrateNovelData, - setMeta: ( - transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, - ) => void, + { + pluginId, + fromNovel, + toNovelPath, + options = LEGACY_MIGRATION_OPTIONS, + }: MigrateNovelData, + setMeta: TaskProgressUpdater, + enqueue: BackgroundTaskEnqueuer, ) => { setMeta(meta => ({ ...meta, @@ -78,12 +94,36 @@ export const migrateNovel = async ( await tx .update(novelSchema) .set({ - cover: fromNovel.cover || toNovel!.cover || '', - summary: fromNovel.summary || toNovel!.summary || '', - author: fromNovel.author || toNovel!.author || '', - artist: fromNovel.artist || toNovel!.artist || '', - status: fromNovel.status || toNovel!.status || '', - genres: fromNovel.genres || toNovel!.genres || '', + cover: selectMigrationValue( + fromNovel.cover, + toNovel!.cover, + options.cover, + ), + summary: selectMigrationValue( + fromNovel.summary, + toNovel!.summary, + options.metadata, + ), + author: selectMigrationValue( + fromNovel.author, + toNovel!.author, + options.metadata, + ), + artist: selectMigrationValue( + fromNovel.artist, + toNovel!.artist, + options.metadata, + ), + status: selectMigrationValue( + fromNovel.status, + toNovel!.status, + options.metadata, + ), + genres: selectMigrationValue( + fromNovel.genres, + toNovel!.genres, + options.metadata, + ), inLibrary: true, }) .where(eq(novelSchema.id, toNovel!.id)); @@ -96,22 +136,21 @@ export const migrateNovel = async ( await tx.delete(novelSchema).where(eq(novelSchema.id, fromNovel.id)); }); - setMMKVObject( - `${NOVEL_SETTINGS_PREFIX}_${toNovel!.pluginId}_${toNovel!.path}`, - getMMKVObject( - `${NOVEL_SETTINGS_PREFIX}_${fromNovel.pluginId}_${fromNovel.path}`, - ), - ); + const fromPersistenceInput: NovelPersistenceInput = { + pluginId: fromNovel.pluginId, + novelPath: fromNovel.path, + }; + const toPersistenceInput: NovelPersistenceInput = { + pluginId: toNovel!.pluginId, + novelPath: toNovel!.path, + }; + + novelPersistence.copySettings(fromPersistenceInput, toPersistenceInput); - const lastRead = getMMKVObject( - `${LAST_READ_PREFIX}_${fromNovel.pluginId}_${fromNovel.path}`, - ); + const lastRead = novelPersistence.readLastRead(fromPersistenceInput); const setLastRead = (chapter: ChapterInfo) => { - setMMKVObject( - `${LAST_READ_PREFIX}_${toNovel!.pluginId}_${toNovel!.path}`, - chapter, - ); + novelPersistence.writeLastRead(toPersistenceInput, chapter); }; fromChapters = sortChaptersByNumber(fromNovel.name, fromChapters); @@ -119,6 +158,7 @@ export const migrateNovel = async ( let fromPointer = 0, toPointer = 0; + const chaptersToDownload: ChapterDownload[] = []; while (fromPointer < fromChapters.length && toPointer < toChapters.length) { const fromChapter = fromChapters[fromPointer]; const toChapter = toChapters[toPointer]; @@ -149,16 +189,11 @@ export const migrateNovel = async ( .where(eq(chapterSchema.id, toChapter.id)); }); - if (fromChapter.isDownloaded) { - ServiceManager.manager.addTask({ - name: 'DOWNLOAD_CHAPTER', - data: { - chapterId: toChapter.id, - novelName: toNovel.name, - chapterName: toChapter.name, - }, + if (options.redownloadChapters && fromChapter.isDownloaded) { + chaptersToDownload.push({ + chapterId: toChapter.id, + chapterName: toChapter.name, }); - await sleep(1000); } if (lastRead && fromChapter.id === lastRead.id) { @@ -169,6 +204,18 @@ export const migrateNovel = async ( ++toPointer; } + if (chaptersToDownload.length) { + enqueue({ + name: 'DOWNLOAD_CHAPTER', + data: { + novelName: toNovel.name, + novelId: toNovel.id, + pluginId: toNovel.pluginId, + chapters: chaptersToDownload, + }, + }); + } + setMeta(meta => ({ ...meta, isRunning: false, diff --git a/src/services/plugin/fetch.ts b/src/services/plugin/fetch.ts index 96b5dd589..01776bc02 100644 --- a/src/services/plugin/fetch.ts +++ b/src/services/plugin/fetch.ts @@ -1,5 +1,21 @@ import { getPlugin } from '@plugins/pluginManager'; import { isUrlAbsolute } from '@plugins/helpers/isAbsoluteUrl'; +import { ChapterItem } from '@plugins/types'; + +function formatChapters(chapters: T): T { + if (!chapters) { + return undefined as T; + } + return chapters.map(ch => { + if (Array.isArray(ch.scanlator)) { + return { + ...ch, + scanlator: ch.scanlator.join(', '), + }; + } + return ch; + }) as T; +} export const fetchNovel = async (pluginId: string, novelPath: string) => { const plugin = getPlugin(pluginId); @@ -7,6 +23,9 @@ export const fetchNovel = async (pluginId: string, novelPath: string) => { throw new Error(`Unknown plugin: ${pluginId}`); } const res = await plugin.parseNovel(novelPath); + if (res?.chapters) { + res.chapters = formatChapters(res.chapters); + } return res; }; @@ -25,7 +44,7 @@ export const fetchChapters = async (pluginId: string, novelPath: string) => { throw new Error(`Unknown plugin: ${pluginId}`); } const res = await plugin.parseNovel(novelPath); - return res?.chapters; + return formatChapters(res?.chapters); }; export const fetchPage = async ( @@ -43,6 +62,9 @@ export const fetchPage = async ( throw new Error(`Could not fetch chapters for page ${page}`); } const res = await plugin.parsePage(novelPath, page); + if (res?.chapters) { + res.chapters = formatChapters(res.chapters); + } return res; }; diff --git a/src/services/updates/LibraryUpdateQueries.ts b/src/services/updates/LibraryUpdateQueries.ts index ab841ab8c..6f1deba20 100644 --- a/src/services/updates/LibraryUpdateQueries.ts +++ b/src/services/updates/LibraryUpdateQueries.ts @@ -3,11 +3,12 @@ import { ChapterItem, SourceNovel } from '@plugins/types'; import { getPlugin, LOCAL_PLUGIN_ID } from '@plugins/pluginManager'; import { NOVEL_STORAGE } from '@utils/Storages'; import { downloadFile } from '@plugins/helpers/fetch'; -import ServiceManager from '@services/ServiceManager'; +import type { BackgroundTaskEnqueuer } from '@services/backgroundTasks/contracts'; import { dbManager } from '@database/db'; import { novelSchema, chapterSchema } from '@database/schema'; -import { eq, and, ne, or, sql } from 'drizzle-orm'; -import NativeFile from '@specs/NativeFile'; +import { eq, and, inArray } from 'drizzle-orm'; +import NativeFile from '@modules/native-file'; +import { insertChapters } from '@database/queries/ChapterQueries'; /** * Update novel metadata in the database including cover image. @@ -21,8 +22,8 @@ const updateNovelMetadata = async ( let cover = novel.cover; const novelDir = `${NOVEL_STORAGE}/${pluginId}/${novelId}`; - if (!NativeFile.exists(novelDir)) { - NativeFile.mkdir(novelDir); + if (!(await NativeFile.exists(novelDir))) { + await NativeFile.mkdir(novelDir); } if (cover) { @@ -42,7 +43,8 @@ const updateNovelMetadata = async ( } await dbManager.write(async tx => { - tx.update(novelSchema) + await tx + .update(novelSchema) .set({ name, cover: cover || null, @@ -63,7 +65,8 @@ const updateNovelMetadata = async ( */ const updateNovelTotalPages = async (novelId: number, totalPages: number) => { await dbManager.write(async tx => { - tx.update(novelSchema) + await tx + .update(novelSchema) .set({ totalPages }) .where(eq(novelSchema.id, novelId)) .run(); @@ -75,91 +78,90 @@ const updateNovelTotalPages = async (novelId: number, totalPages: number) => { * Distinguishes between new chapters (triggers download) and existing chapters (updates metadata). */ const updateNovelChapters = async ( + pluginId: string, novelName: string, novelId: number, chapters: ChapterItem[], downloadNewChapters?: boolean, page?: string, + enqueue?: BackgroundTaskEnqueuer, ) => { - await dbManager.write(async tx => { - for (let position = 0; position < chapters.length; position++) { - const chapter = chapters[position]; - const { - name, - path, - releaseTime, - page: customPage, - chapterNumber, - } = chapter; - const chapterPage = page || customPage || '1'; + if (!chapters.length) { + return; + } - // Check if chapter already exists - const existing = await tx - .select({ id: chapterSchema.id }) + const incomingPaths = Array.from( + new Set(chapters.map(chapter => chapter.path)), + ); + const existingChapters = incomingPaths.length + ? await dbManager + .select({ path: chapterSchema.path }) .from(chapterSchema) .where( - and(eq(chapterSchema.novelId, novelId), eq(chapterSchema.path, path)), + and( + eq(chapterSchema.novelId, novelId), + inArray(chapterSchema.path, incomingPaths), + ), ) - .get(); + .all() + : []; - if (!existing) { - // Insert new chapter - const newChapter = await tx - .insert(chapterSchema) - .values({ - path, - name, - releaseTime: releaseTime || null, - novelId, - updatedTime: sql`datetime('now','localtime')`, - chapterNumber: chapterNumber || null, - page: chapterPage, - position: position, - }) - .returning() - .get(); + const existingPathSet = new Set( + existingChapters.map(chapter => chapter.path), + ); + const newPaths = incomingPaths.filter(path => !existingPathSet.has(path)); - if (newChapter && downloadNewChapters) { - ServiceManager.manager.addTask({ - name: 'DOWNLOAD_CHAPTER', - data: { - chapterId: newChapter.id, - novelName: novelName, - chapterName: name, - }, - }); - } - } else { - // Update existing chapter if metadata changed - tx.update(chapterSchema) - .set({ - name, - releaseTime: releaseTime || null, - updatedTime: sql`datetime('now','localtime')`, - page: chapterPage, - position: position, - }) - .where( - and( - eq(chapterSchema.id, existing.id), - eq(chapterSchema.novelId, novelId), - or( - ne(chapterSchema.name, name), - ne(chapterSchema.releaseTime, releaseTime!), - ne(chapterSchema.page, chapterPage), - ne(chapterSchema.position, position), - ), - ), - ) - .run(); - } - } + await insertChapters(novelId, chapters, { + page, + touchUpdatedTime: true, }); + + if (downloadNewChapters && newPaths.length && enqueue) { + const insertedNewChapters = await dbManager + .select({ + id: chapterSchema.id, + path: chapterSchema.path, + name: chapterSchema.name, + }) + .from(chapterSchema) + .where( + and( + eq(chapterSchema.novelId, novelId), + inArray(chapterSchema.path, newPaths), + ), + ) + .all(); + + const chapterNameByPath = new Map( + chapters.map((chapter, index) => [ + chapter.path, + chapter.name || `Chapter ${index + 1}`, + ]), + ); + + if (insertedNewChapters.length) { + enqueue({ + name: 'DOWNLOAD_CHAPTER', + data: { + novelName, + novelId, + pluginId, + chapters: insertedNewChapters.map(insertedChapter => ({ + chapterId: insertedChapter.id, + chapterName: + chapterNameByPath.get(insertedChapter.path) || + insertedChapter.name, + })), + }, + }); + } + } }; export interface UpdateNovelOptions { downloadNewChapters?: boolean; refreshNovelMetadata?: boolean; + enqueue?: BackgroundTaskEnqueuer; } const getStoredTotalPages = async (novelId: number): Promise => { @@ -184,7 +186,7 @@ const updateNovel = async ( if (pluginId === LOCAL_PLUGIN_ID) { return; } - const { downloadNewChapters, refreshNovelMetadata } = options; + const { downloadNewChapters, refreshNovelMetadata, enqueue } = options; const oldTotalPages = await getStoredTotalPages(novelId); @@ -194,14 +196,15 @@ const updateNovel = async ( await updateNovelMetadata(pluginId, novelId, novel); } else if (novel.totalPages) { await updateNovelTotalPages(novelId, novel.totalPages); - await updateNovelTotalPages(novelId, novel.totalPages); } - await updateNovelChapters( + pluginId, novel.name, novelId, novel.chapters || [], downloadNewChapters, + undefined, + enqueue, ); // For paged novels: re-fetch the last known page and fetch any new pages @@ -217,11 +220,13 @@ const updateNovel = async ( String(oldTotalPages), ); await updateNovelChapters( + pluginId, novel.name, novelId, sourcePage.chapters || [], downloadNewChapters, String(oldTotalPages), + enqueue, ); } catch {} } @@ -231,11 +236,13 @@ const updateNovel = async ( try { const sourcePage = await fetchPage(pluginId, novelPath, String(page)); await updateNovelChapters( + pluginId, novel.name, novelId, sourcePage.chapters || [], downloadNewChapters, String(page), + enqueue, ); } catch {} } @@ -252,17 +259,19 @@ const updateNovelPage = async ( novelPath: string, novelId: number, page: string, - options: Pick, + options: Pick, ) => { const { downloadNewChapters } = options; const sourcePage = await fetchPage(pluginId, novelPath, page); await updateNovelChapters( + pluginId, novelName, novelId, sourcePage.chapters || [], downloadNewChapters, page, + options.enqueue, ); }; diff --git a/src/services/updates/__tests__/index.test.ts b/src/services/updates/__tests__/index.test.ts new file mode 100644 index 000000000..2286bb738 --- /dev/null +++ b/src/services/updates/__tests__/index.test.ts @@ -0,0 +1,158 @@ +import { + getLibraryNovelsForGlobalUpdate, + getLibraryWithCategory, +} from '@database/queries/LibraryQueries'; +import type { BackgroundTaskMetadata } from '@services/backgroundTasks/contracts'; +import { getMMKVObject } from '@utils/mmkv/mmkv'; +import { updateNovel } from '../LibraryUpdateQueries'; +import { updateLibrary } from '../index'; + +jest.mock('@database/queries/LibraryQueries', () => ({ + getLibraryNovelsForGlobalUpdate: jest.fn(), + getLibraryWithCategory: jest.fn(), +})); +jest.mock('../LibraryUpdateQueries', () => ({ + updateNovel: jest.fn(), +})); +jest.mock('@utils/sleep', () => ({ + sleep: jest.fn(() => Promise.resolve()), +})); +jest.mock('@utils/showToast', () => ({ + showToast: jest.fn(), +})); +jest.mock('@utils/mmkv/mmkv', () => ({ + MMKVStorage: { set: jest.fn() }, + getMMKVObject: jest.fn(() => ({})), +})); +jest.mock('@hooks/persisted/useUpdates', () => ({ + LAST_UPDATE_TIME: 'LAST_UPDATE_TIME', +})); +jest.mock('@hooks/persisted/useSettings', () => ({ + APP_SETTINGS: 'APP_SETTINGS', + getGlobalUpdateCategoryFilters: jest.fn(() => ({ + excludedCategoryIds: [], + includedCategoryIds: [], + })), +})); + +const mockedGetLibraryNovels = jest.mocked(getLibraryNovelsForGlobalUpdate); +const mockedGetLibraryWithCategory = jest.mocked(getLibraryWithCategory); +const mockedUpdateNovel = jest.mocked(updateNovel); +const mockedGetMMKVObject = jest.mocked(getMMKVObject); + +type LibraryNovel = Awaited< + ReturnType +>[number]; + +const novel = (id: number, pluginId: string, name: string): LibraryNovel => + ({ + id, + pluginId, + name, + path: `/${name}`, + } as LibraryNovel); + +const flushPromises = () => new Promise(resolve => setImmediate(resolve)); + +describe('updateLibrary', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockedGetMMKVObject.mockReturnValue({}); + }); + + it('updates at most three different sources while keeping each source sequential', async () => { + const novels = [ + novel(1, 'source-a', 'A One'), + novel(2, 'source-a', 'A Two'), + novel(3, 'source-b', 'B One'), + novel(4, 'source-c', 'C One'), + novel(5, 'source-d', 'D One'), + ]; + mockedGetLibraryNovels.mockResolvedValue(novels); + + const completions = new Map void>(); + mockedUpdateNovel.mockImplementation( + (_pluginId, _path, novelId) => + new Promise(resolve => completions.set(novelId, resolve)), + ); + + let metadata: BackgroundTaskMetadata = { + name: 'Update library', + isRunning: false, + progress: undefined, + progressText: undefined, + }; + const progressSnapshots: BackgroundTaskMetadata[] = []; + const setMeta = ( + transform: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, + ) => { + metadata = transform(metadata); + progressSnapshots.push(metadata); + }; + + const updatePromise = updateLibrary({}, setMeta, jest.fn()); + await flushPromises(); + + expect(mockedUpdateNovel.mock.calls.map(([pluginId]) => pluginId)).toEqual([ + 'source-a', + 'source-b', + 'source-c', + ]); + expect(progressSnapshots.at(-1)?.progressText).toBe( + ['A One', 'B One', 'C One'].join('\n'), + ); + + completions.get(3)?.(); + await flushPromises(); + expect(mockedUpdateNovel).toHaveBeenCalledTimes(4); + expect(mockedUpdateNovel.mock.calls[3][0]).toBe('source-d'); + + completions.get(1)?.(); + await flushPromises(); + expect(mockedUpdateNovel).toHaveBeenCalledTimes(5); + expect(mockedUpdateNovel.mock.calls[4][0]).toBe('source-a'); + + completions.get(2)?.(); + completions.get(4)?.(); + completions.get(5)?.(); + await updatePromise; + + expect(metadata).toMatchObject({ + isRunning: false, + progress: 1, + progressText: undefined, + }); + }); + + it('uses the category query when an update is category-scoped', async () => { + mockedGetLibraryWithCategory.mockResolvedValue([]); + + await updateLibrary({ categoryId: 4 }, jest.fn(), jest.fn()); + + expect(mockedGetLibraryWithCategory).toHaveBeenCalledWith(4, true); + expect(mockedGetLibraryNovels).not.toHaveBeenCalled(); + }); + + it('passes smart update preferences to the global update query', async () => { + mockedGetMMKVObject.mockReturnValue({ + smartUpdateSkipCompleted: true, + smartUpdateSkipUnstarted: true, + smartUpdateSkipWithUnread: true, + }); + mockedGetLibraryNovels.mockResolvedValue([]); + + await updateLibrary({}, jest.fn(), jest.fn()); + + expect(mockedGetLibraryNovels).toHaveBeenCalledWith( + { + excludedCategoryIds: [], + includedCategoryIds: [], + }, + { + skipCompleted: true, + skipUnstarted: true, + skipWithUnread: true, + }, + ); + }); +}); diff --git a/src/services/updates/index.ts b/src/services/updates/index.ts index fc61caaa2..37a338fb3 100644 --- a/src/services/updates/index.ts +++ b/src/services/updates/index.ts @@ -1,17 +1,41 @@ import { + getLibraryNovelsForGlobalUpdate, getLibraryWithCategory, - getLibraryNovelsFromDb, } from '../../database/queries/LibraryQueries'; import { showToast } from '../../utils/showToast'; -import { UpdateNovelOptions, updateNovel } from './LibraryUpdateQueries'; -import { DBNovelInfo } from '@database/types'; +import { updateNovel, type UpdateNovelOptions } from './LibraryUpdateQueries'; +import type { DBNovelInfo } from '@database/types'; import { sleep } from '@utils/sleep'; import { MMKVStorage, getMMKVObject } from '@utils/mmkv/mmkv'; import { LAST_UPDATE_TIME } from '@hooks/persisted/useUpdates'; import dayjs from 'dayjs'; -import { APP_SETTINGS, AppSettings } from '@hooks/persisted/useSettings'; -import { BackgroundTaskMetadata } from '@services/ServiceManager'; +import { + APP_SETTINGS, + AppSettings, + getGlobalUpdateCategoryFilters, +} from '@hooks/persisted/useSettings'; +import type { + BackgroundTaskEnqueuer, + TaskProgressUpdater, +} from '@services/backgroundTasks/contracts'; + +const UPDATE_SOURCE_CONCURRENCY = 3; + +const groupNovelsByPlugin = (novels: DBNovelInfo[]) => { + const groupedNovels = new Map(); + + for (const novel of novels) { + const pluginNovels = groupedNovels.get(novel.pluginId); + if (pluginNovels) { + pluginNovels.push(novel); + } else { + groupedNovels.set(novel.pluginId, [novel]); + } + } + + return [...groupedNovels.values()]; +}; const updateLibrary = async ( { @@ -19,9 +43,8 @@ const updateLibrary = async ( }: { categoryId?: number; }, - setMeta: ( - transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, - ) => void, + setMeta: TaskProgressUpdater, + enqueue: BackgroundTaskEnqueuer, ) => { setMeta(meta => ({ ...meta, @@ -29,52 +52,84 @@ const updateLibrary = async ( progress: 0, })); - const { downloadNewChapters, refreshNovelMetadata, onlyUpdateOngoingNovels } = - getMMKVObject(APP_SETTINGS) || {}; + const { + downloadNewChapters, + refreshNovelMetadata, + smartUpdateSkipCompleted, + smartUpdateSkipUnstarted, + smartUpdateSkipWithUnread, + } = getMMKVObject(APP_SETTINGS) || {}; + const smartUpdateFilters = { + skipCompleted: Boolean(smartUpdateSkipCompleted), + skipUnstarted: Boolean(smartUpdateSkipUnstarted), + skipWithUnread: Boolean(smartUpdateSkipWithUnread), + }; const options: UpdateNovelOptions = { downloadNewChapters: downloadNewChapters || false, refreshNovelMetadata: refreshNovelMetadata || false, + enqueue, }; let libraryNovels: DBNovelInfo[] = []; if (categoryId) { - libraryNovels = await getLibraryWithCategory( - categoryId, - onlyUpdateOngoingNovels, - true, - ); + libraryNovels = await getLibraryWithCategory(categoryId, true); } else { - libraryNovels = await getLibraryNovelsFromDb( - '', - onlyUpdateOngoingNovels ? "status = 'Ongoing'" : '', - '', - false, - true, + libraryNovels = await getLibraryNovelsForGlobalUpdate( + getGlobalUpdateCategoryFilters(), + smartUpdateFilters, ); } if (libraryNovels.length > 0) { MMKVStorage.set(LAST_UPDATE_TIME, dayjs().format('YYYY-MM-DD HH:mm:ss')); - for (let i = 0; i < libraryNovels.length; i++) { + + const sourceQueues = groupNovelsByPlugin(libraryNovels); + const activeNovels = new Map(); + let completedNovels = 0; + let nextSourceQueue = 0; + + const publishProgress = () => { setMeta(meta => ({ ...meta, - progressText: libraryNovels[i].name, - progress: i / libraryNovels.length, + progressText: [...activeNovels.values()].join('\n') || undefined, + progress: completedNovels / libraryNovels.length, })); + }; - try { - await updateNovel( - libraryNovels[i].pluginId, - libraryNovels[i].path, - libraryNovels[i].id, - options, - ); - await sleep(1000); - } catch (error: any) { - showToast(libraryNovels[i].name + ': ' + error.message); - continue; + const updateSourceQueue = async (sourceQueue: DBNovelInfo[]) => { + for (const novel of sourceQueue) { + activeNovels.set(novel.pluginId, novel.name); + publishProgress(); + + try { + await updateNovel(novel.pluginId, novel.path, novel.id, options); + await sleep(1000); + } catch (error: any) { + showToast(novel.name + ': ' + error.message); + } finally { + completedNovels += 1; + activeNovels.delete(novel.pluginId); + publishProgress(); + } } - } + }; + + const updateNextSource = async () => { + while (nextSourceQueue < sourceQueues.length) { + const sourceQueue = sourceQueues[nextSourceQueue]; + nextSourceQueue += 1; + await updateSourceQueue(sourceQueue); + } + }; + + await Promise.all( + Array.from( + { + length: Math.min(UPDATE_SOURCE_CONCURRENCY, sourceQueues.length), + }, + updateNextSource, + ), + ); } else { showToast("There's no novel to be updated"); } @@ -82,6 +137,7 @@ const updateLibrary = async ( setMeta(meta => ({ ...meta, progress: 1, + progressText: undefined, isRunning: false, })); }; diff --git a/src/theme/colors.ts b/src/theme/colors.ts index 59a636e3c..9f393204f 100644 --- a/src/theme/colors.ts +++ b/src/theme/colors.ts @@ -1,5 +1,3 @@ -export const coverPlaceholderColor = '#8888881F'; - export const filterColor: (isDark: boolean) => string = isDark => isDark ? '#FFC107' : '#FFC107'; diff --git a/src/theme/dynamic.ts b/src/theme/dynamic.ts new file mode 100644 index 000000000..5b35ceebe --- /dev/null +++ b/src/theme/dynamic.ts @@ -0,0 +1,26 @@ +import { + getMaterial3Theme, + isDynamicThemeSupported, + type Material3Theme, +} from '@pchmn/expo-material3-theme'; + +import { getString } from '@i18n/translations'; +import type { ThemeColors } from '@theme/types'; + +export const DYNAMIC_THEME_ID = -1; +const DYNAMIC_THEME_FALLBACK_COLOR = '#0057CE'; + +export const isDynamicThemeAvailable = isDynamicThemeSupported; + +export const getSystemDynamicTheme = (): Material3Theme => + getMaterial3Theme(DYNAMIC_THEME_FALLBACK_COLOR); + +export const toDynamicThemeColors = ( + dynamicTheme: Material3Theme, + isDark: boolean, +): ThemeColors => ({ + ...(isDark ? dynamicTheme.dark : dynamicTheme.light), + id: DYNAMIC_THEME_ID, + name: getString('appearanceScreen.dynamicColors'), + isDark, +}); diff --git a/src/theme/md3/catppuccin.ts b/src/theme/md3/catppuccin.ts index d7ae348c0..8d7a05d08 100644 --- a/src/theme/md3/catppuccin.ts +++ b/src/theme/md3/catppuccin.ts @@ -1,8 +1,7 @@ -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; export const catppuccinTheme = { light: { - id: 20, name: getString('appearanceScreen.theme.catppuccin'), isDark: false, primary: 'rgb(136, 57, 239)', @@ -39,7 +38,6 @@ export const catppuccinTheme = { backdrop: 'rgba(196, 200, 208, 0.4)', }, dark: { - id: 21, name: getString('appearanceScreen.theme.catppuccin'), isDark: true, primary: 'rgb(203, 166, 247)', diff --git a/src/theme/md3/defaultTheme.ts b/src/theme/md3/defaultTheme.ts index 5fc18f685..8b9cd898a 100644 --- a/src/theme/md3/defaultTheme.ts +++ b/src/theme/md3/defaultTheme.ts @@ -1,8 +1,7 @@ -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; export const defaultTheme = { light: { - id: 1, name: getString('appearanceScreen.theme.default'), isDark: false, primary: 'rgb(0, 87, 206)', @@ -39,7 +38,6 @@ export const defaultTheme = { backdrop: 'rgba(46, 48, 56, 0.4)', }, dark: { - id: 2, name: getString('appearanceScreen.theme.default'), isDark: true, primary: 'rgb(177, 197, 255)', diff --git a/src/theme/md3/index.ts b/src/theme/md3/index.ts index 044f09e09..b1378e34e 100644 --- a/src/theme/md3/index.ts +++ b/src/theme/md3/index.ts @@ -8,6 +8,13 @@ import { takoTheme } from './tako'; import { catppuccinTheme } from './catppuccin'; import { yinyangTheme } from './yinyang'; +/** + * Exports for MD3 theme system + * + * IMPORTANT: + * IDs are auto-assigned, so new themes + * need to be added at the end of the list. + */ export const lightThemes = [ defaultTheme.light, midnightDusk.light, @@ -18,7 +25,7 @@ export const lightThemes = [ takoTheme.light, catppuccinTheme.light, yinyangTheme.light, -]; +].map((theme, i) => ({ ...theme, id: 100 + i })); export const darkThemes = [ defaultTheme.dark, midnightDusk.dark, @@ -29,4 +36,4 @@ export const darkThemes = [ takoTheme.dark, catppuccinTheme.dark, yinyangTheme.dark, -]; +].map((theme, i) => ({ ...theme, id: 100 + i })); diff --git a/src/theme/md3/lavender.ts b/src/theme/md3/lavender.ts index d3515ad1c..ef1bd17a4 100644 --- a/src/theme/md3/lavender.ts +++ b/src/theme/md3/lavender.ts @@ -1,8 +1,7 @@ -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; export const lavenderTheme = { light: { - id: 14, name: getString('appearanceScreen.theme.lavender'), isDark: false, primary: 'rgb(121, 68, 173)', @@ -39,7 +38,6 @@ export const lavenderTheme = { backdrop: 'rgba(52, 47, 55, 0.4)', }, dark: { - id: 15, name: getString('appearanceScreen.theme.lavender'), isDark: true, primary: 'rgb(221, 184, 255)', diff --git a/src/theme/md3/mignightDusk.ts b/src/theme/md3/mignightDusk.ts index 8a47708c9..8dac3d1bd 100644 --- a/src/theme/md3/mignightDusk.ts +++ b/src/theme/md3/mignightDusk.ts @@ -1,8 +1,7 @@ -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; export const midnightDusk = { light: { - id: 10, name: getString('appearanceScreen.theme.daybreakBloom'), isDark: false, primary: 'rgb(240, 36, 117)', @@ -39,7 +38,6 @@ export const midnightDusk = { backdrop: 'rgba(58, 45, 47, 0.4)', }, dark: { - id: 11, name: getString('appearanceScreen.theme.midnightDusk'), isDark: true, primary: 'rgb(240, 36, 117)', diff --git a/src/theme/md3/strawberry.ts b/src/theme/md3/strawberry.ts index 612992179..ac00f4446 100644 --- a/src/theme/md3/strawberry.ts +++ b/src/theme/md3/strawberry.ts @@ -1,8 +1,7 @@ -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; export const strawberryDaiquiriTheme = { light: { - id: 16, name: getString('appearanceScreen.theme.strawberry'), isDark: false, primary: 'rgb(182, 30, 64)', @@ -39,7 +38,6 @@ export const strawberryDaiquiriTheme = { backdrop: 'rgba(59, 45, 46, 0.4)', }, dark: { - id: 17, name: getString('appearanceScreen.theme.strawberry'), isDark: true, primary: 'rgb(255, 178, 184)', diff --git a/src/theme/md3/tako.ts b/src/theme/md3/tako.ts index 2502c2f2c..295fa2d10 100644 --- a/src/theme/md3/tako.ts +++ b/src/theme/md3/tako.ts @@ -1,8 +1,7 @@ -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; export const takoTheme = { light: { - id: 18, name: getString('appearanceScreen.theme.tako'), isDark: false, primary: '#66577E', @@ -39,7 +38,6 @@ export const takoTheme = { backdrop: 'rgba(51, 47, 55, 0.4)', }, dark: { - id: 19, name: getString('appearanceScreen.theme.tako'), isDark: true, primary: '#F3B375', diff --git a/src/theme/md3/tealTurquoise.ts b/src/theme/md3/tealTurquoise.ts index f9bb458bf..2f2e008b0 100644 --- a/src/theme/md3/tealTurquoise.ts +++ b/src/theme/md3/tealTurquoise.ts @@ -1,8 +1,7 @@ -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; export const tealTurquoise = { light: { - id: 8, name: getString('appearanceScreen.theme.teal'), isDark: false, primary: 'rgb(0, 106, 106)', @@ -39,7 +38,6 @@ export const tealTurquoise = { backdrop: 'rgba(41, 50, 50, 0.4)', }, dark: { - id: 9, name: getString('appearanceScreen.theme.turquoise'), isDark: true, primary: 'rgb(76, 218, 218)', diff --git a/src/theme/md3/yinyang.ts b/src/theme/md3/yinyang.ts index 5f4b934d2..619199564 100644 --- a/src/theme/md3/yinyang.ts +++ b/src/theme/md3/yinyang.ts @@ -1,8 +1,7 @@ -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; export const yinyangTheme = { light: { - id: 9, name: getString('appearanceScreen.theme.yinyang'), isDark: false, primary: '#000000', @@ -39,7 +38,6 @@ export const yinyangTheme = { backdrop: 'rgba(0, 0, 0, 0.4)', }, dark: { - id: 10, name: getString('appearanceScreen.theme.yinyang'), isDark: true, primary: '#FFFFFF', diff --git a/src/theme/md3/yotsuba.ts b/src/theme/md3/yotsuba.ts index 0105636e9..d4fed52e6 100644 --- a/src/theme/md3/yotsuba.ts +++ b/src/theme/md3/yotsuba.ts @@ -1,8 +1,7 @@ -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; export const yotsubaTheme = { light: { - id: 12, name: getString('appearanceScreen.theme.yotsuba'), isDark: false, primary: 'rgb(174, 50, 0)', @@ -39,7 +38,6 @@ export const yotsubaTheme = { backdrop: 'rgba(59, 45, 41, 0.4)', }, dark: { - id: 13, name: getString('appearanceScreen.theme.yotsuba'), isDark: true, primary: 'rgb(255, 181, 158)', diff --git a/src/theme/types/index.ts b/src/theme/types/index.ts index eec0a863a..ddf6e6a02 100644 --- a/src/theme/types/index.ts +++ b/src/theme/types/index.ts @@ -38,6 +38,8 @@ export interface MD3ThemeType { export interface ThemeColors extends MD3ThemeType { rippleColor?: string; + surfaceContainerLow?: string; + surfaceContainerHigh?: string; surface2?: string; overlay3?: string; surfaceReader?: string; diff --git a/src/theme/utils/setBarColor.ts b/src/theme/utils/setBarColor.ts index 2d1451445..7256338bb 100644 --- a/src/theme/utils/setBarColor.ts +++ b/src/theme/utils/setBarColor.ts @@ -1,6 +1,5 @@ import { StatusBar } from 'react-native'; import { ThemeColors } from '@theme/types'; -import * as NavigationBar from 'expo-navigation-bar'; import Color, { ColorInstance } from 'color'; export const setStatusBarColor = (color: ThemeColors | ColorInstance) => { @@ -14,8 +13,3 @@ export const setStatusBarColor = (color: ThemeColors | ColorInstance) => { StatusBar.setBarStyle(color.isDark ? 'light-content' : 'dark-content'); } }; - -export const changeNavigationBarColor = (color: string, isDark = false) => { - NavigationBar.setBackgroundColorAsync(color); - NavigationBar.setButtonStyleAsync(isDark ? 'light' : 'dark'); -}; diff --git a/src/utils/Storages.ts b/src/utils/Storages.ts index 7c18ae4cd..79df902ac 100644 --- a/src/utils/Storages.ts +++ b/src/utils/Storages.ts @@ -1,5 +1,9 @@ -import NativeFile from '@specs/NativeFile'; +import NativeFile from '@modules/native-file'; -export const ROOT_STORAGE = NativeFile.getConstants().ExternalDirectoryPath; -export const PLUGIN_STORAGE = ROOT_STORAGE + '/Plugins'; +const documentStorage = + NativeFile.DocumentDirectoryPath || NativeFile.ExternalDirectoryPath; + +export const ROOT_STORAGE = NativeFile.ExternalDirectoryPath || documentStorage; +export const LEGACY_PLUGIN_STORAGE = ROOT_STORAGE + '/Plugins'; +export const PLUGIN_STORAGE = documentStorage + '/Plugins'; export const NOVEL_STORAGE = ROOT_STORAGE + '/Novels'; diff --git a/src/utils/__tests__/dateFormat.test.ts b/src/utils/__tests__/dateFormat.test.ts new file mode 100644 index 000000000..30558b7e0 --- /dev/null +++ b/src/utils/__tests__/dateFormat.test.ts @@ -0,0 +1,38 @@ +import { formatDate, getDateFormatLabel } from '../dateFormat'; + +describe('dateFormat', () => { + const now = new Date(2026, 6, 25, 12); + + it.each([ + ['MM/DD/YY', '07/25/26'], + ['DD/MM/YY', '25/07/26'], + ['YYYY-MM-DD', '2026-07-25'], + ['DD MMM YYYY', '25 Jul 2026'], + ['MMM DD, YYYY', 'Jul 25, 2026'], + ] as const)('formats %s dates', (dateFormat, expected) => { + expect(formatDate(now, dateFormat, false)).toBe(expected); + }); + + it('uses relative labels only when enabled', () => { + expect(formatDate(now, 'YYYY-MM-DD', true, now)).toBe('Today'); + expect(formatDate(new Date(2026, 6, 24, 12), 'YYYY-MM-DD', true, now)).toBe( + 'Yesterday', + ); + expect(formatDate(now, 'YYYY-MM-DD', false, now)).toBe('2026-07-25'); + }); + + it('keeps calendar labels for dates within a week', () => { + expect(formatDate(new Date(2026, 6, 20, 12), 'YYYY-MM-DD', true, now)).toBe( + 'Last Monday', + ); + }); + + it('includes a preview in each setting label', () => { + expect(getDateFormatLabel('DD/MM/YY', now)).toBe('dd/MM/yy (25/07/26)'); + expect(getDateFormatLabel('default', now)).toContain('Default ('); + }); + + it('preserves invalid string values', () => { + expect(formatDate('Unknown', 'YYYY-MM-DD')).toBe('Unknown'); + }); +}); diff --git a/src/utils/constants/languages.ts b/src/utils/constants/languages.ts index e7eda2ee8..232cc3129 100644 --- a/src/utils/constants/languages.ts +++ b/src/utils/constants/languages.ts @@ -2,7 +2,7 @@ // https://en.wikipedia.org/wiki/IETF_language_tag // https://en.wikipedia.org/wiki/List_of_language_names -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; export const languagesMapping: Record = { 'id': 'Bahasa Indonesia', diff --git a/src/utils/constants/readerConstants.ts b/src/utils/constants/readerConstants.ts index 7af0016e8..7b12bdb04 100644 --- a/src/utils/constants/readerConstants.ts +++ b/src/utils/constants/readerConstants.ts @@ -1,5 +1,4 @@ import { ReaderTheme } from '@hooks/persisted/useSettings'; -import { MaterialDesignIconName } from '@type/icon'; export const presetReaderThemes: ReaderTheme[] = [ { backgroundColor: '#f5f5fa', textColor: '#111111' }, @@ -12,18 +11,6 @@ export const presetReaderThemes: ReaderTheme[] = [ }, ]; -interface TextAlignments { - value: string; - icon: MaterialDesignIconName; -} - -export const textAlignments: TextAlignments[] = [ - { value: 'left', icon: 'format-align-left' }, - { value: 'center', icon: 'format-align-center' }, - { value: 'justify', icon: 'format-align-justify' }, - { value: 'right', icon: 'format-align-right' }, -]; - export interface Font { fontFamily: string; name: string; diff --git a/src/utils/dateFormat.ts b/src/utils/dateFormat.ts new file mode 100644 index 000000000..9c442f8ec --- /dev/null +++ b/src/utils/dateFormat.ts @@ -0,0 +1,79 @@ +import dayjs, { ConfigType } from 'dayjs'; +import calendar from 'dayjs/plugin/calendar'; +import localizedFormat from 'dayjs/plugin/localizedFormat'; + +import { getString, localization } from '@i18n/translations'; + +dayjs.extend(calendar); +dayjs.extend(localizedFormat); + +export type DateFormat = + | 'default' + | 'MM/DD/YY' + | 'DD/MM/YY' + | 'YYYY-MM-DD' + | 'DD MMM YYYY' + | 'MMM DD, YYYY'; + +export const DATE_FORMATS: DateFormat[] = [ + 'default', + 'MM/DD/YY', + 'DD/MM/YY', + 'YYYY-MM-DD', + 'DD MMM YYYY', + 'MMM DD, YYYY', +]; + +const DATE_FORMAT_NAMES: Record, string> = { + 'MM/DD/YY': 'MM/dd/yy', + 'DD/MM/YY': 'dd/MM/yy', + 'YYYY-MM-DD': 'yyyy-MM-dd', + 'DD MMM YYYY': 'dd MMM yyyy', + 'MMM DD, YYYY': 'MMM dd, yyyy', +}; + +const formatDefaultDate = (date: Date): string => { + try { + return new Intl.DateTimeFormat(localization, { + dateStyle: 'short', + }).format(date); + } catch { + return dayjs(date).format('L'); + } +}; + +export const formatDate = ( + value: ConfigType, + dateFormat: DateFormat = 'default', + relativeTimestamps = true, + now: ConfigType = dayjs(), +): string => { + const date = dayjs(value); + if (!date.isValid()) { + return typeof value === 'string' ? value : ''; + } + + if (relativeTimestamps) { + const referenceDate = dayjs(now); + const dayDifference = date + .startOf('day') + .diff(referenceDate.startOf('day'), 'day'); + if (dayDifference >= -6 && dayDifference < 7) { + return date.calendar(referenceDate); + } + } + + return dateFormat === 'default' + ? formatDefaultDate(date.toDate()) + : date.format(dateFormat); +}; + +export const getDateFormatLabel = ( + dateFormat: DateFormat, + sampleDate: ConfigType = dayjs(), +): string => { + const sample = formatDate(sampleDate, dateFormat, false); + return dateFormat === 'default' + ? getString('appearanceScreen.dateFormatDefault', { date: sample }) + : `${DATE_FORMAT_NAMES[dateFormat]} (${sample})`; +}; diff --git a/src/utils/mmkv/zustand-adapter.ts b/src/utils/mmkv/zustand-adapter.ts new file mode 100644 index 000000000..8b9219839 --- /dev/null +++ b/src/utils/mmkv/zustand-adapter.ts @@ -0,0 +1,59 @@ +import { MMKVStorage } from './mmkv'; + +/** + * Zustand persist storage adapter for MMKV. + * Implements the storage contract required by zustand's persist middleware. + * + * This adapter bridges zustand's storage interface (getItem, setItem, removeItem) + * with the MMKV native storage backend used in react-native-mmkv. + */ +export const mmkvZustandAdapter = { + /** + * Get a stored value from MMKV by key. + * Returns JSON string for zustand to parse, or null if not found. + */ + getItem: (key: string): string | null => { + try { + const value = MMKVStorage.getString(key); + return value ?? null; + } catch (error) { + // eslint-disable-next-line no-console + console.error( + `[mmkvZustandAdapter] Error getting item for key "${key}":`, + error, + ); + return null; + } + }, + + /** + * Set a value in MMKV storage. + * Zustand passes a JSON string; we store it directly. + */ + setItem: (key: string, value: string): void => { + try { + MMKVStorage.set(key, value); + } catch (error) { + // eslint-disable-next-line no-console + console.error( + `[mmkvZustandAdapter] Error setting item for key "${key}":`, + error, + ); + } + }, + + /** + * Remove a value from MMKV storage. + */ + removeItem: (key: string): void => { + try { + MMKVStorage.remove(key); + } catch (error) { + // eslint-disable-next-line no-console + console.error( + `[mmkvZustandAdapter] Error removing item for key "${key}":`, + error, + ); + } + }, +}; diff --git a/src/utils/runWhenIdle.ts b/src/utils/runWhenIdle.ts new file mode 100644 index 000000000..2992e9b31 --- /dev/null +++ b/src/utils/runWhenIdle.ts @@ -0,0 +1,20 @@ +/** + * Schedules `task` to run once the JS thread is idle, and after `timeout` at + * the latest. Useful for bookkeeping and prefetching that should never compete + * with work the user is waiting for. + * + * Returns a canceller so callers can drop the task when it becomes irrelevant + * (e.g. the screen unmounted before it ever ran). + * + * `requestIdleCallback` replaces the deprecated `InteractionManager`; it is + * unavailable in the test environment, where a macrotask is close enough. + */ +export const runWhenIdle = (task: () => void, timeout = 500): (() => void) => { + if (typeof requestIdleCallback !== 'function') { + const handle = setTimeout(task, 0); + return () => clearTimeout(handle); + } + + const handle = requestIdleCallback(task, { timeout }); + return () => cancelIdleCallback(handle); +}; diff --git a/src/utils/showToast.ts b/src/utils/showToast.ts index bd357a23c..e4bdbcbb8 100644 --- a/src/utils/showToast.ts +++ b/src/utils/showToast.ts @@ -1,7 +1,7 @@ import { ToastAndroid } from 'react-native'; -export const showToast = (message: string) => { - ToastAndroid.show(message, ToastAndroid.SHORT); +export const showToast = (...message: string[]) => { + ToastAndroid.show(message.join(' '), ToastAndroid.SHORT); if (__DEV__) { // eslint-disable-next-line no-console console.trace('Toast: ', message); diff --git a/src/utils/translateEnum.ts b/src/utils/translateEnum.ts index e73bab958..e1056c6b9 100644 --- a/src/utils/translateEnum.ts +++ b/src/utils/translateEnum.ts @@ -1,5 +1,5 @@ import { NovelStatus } from '@plugins/types'; -import { getString } from '@strings/translations'; +import { getString } from '@i18n/translations'; export const translateNovelStatus = (status?: NovelStatus | string) => { switch (status) { @@ -17,6 +17,10 @@ export const translateNovelStatus = (status?: NovelStatus | string) => { return getString('novelScreen.status.licensed'); case NovelStatus.PublishingFinished: return getString('novelScreen.status.publishingFinished'); + case NovelStatus.STUB: + return getString('novelScreen.status.stub'); + case NovelStatus.Inactive: + return getString('novelScreen.status.inactive'); default: return status ?? ''; } diff --git a/src/utils/ttsNotification.ts b/src/utils/ttsNotification.ts deleted file mode 100644 index e5758d9cd..000000000 --- a/src/utils/ttsNotification.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { NativeEventEmitter } from 'react-native'; -import NativeTTSMediaControl from '@specs/NativeTTSMediaControl'; - -export const ttsMediaEmitter = new NativeEventEmitter(NativeTTSMediaControl); - -export interface TTSNotificationData { - novelName: string; - chapterName: string; - coverUri: string; - isPlaying: boolean; -} - -export const showTTSNotification = (data: TTSNotificationData) => { - NativeTTSMediaControl.showMediaNotification( - data.novelName, - data.chapterName, - data.coverUri, - data.isPlaying, - ); -}; - -export const updateTTSNotification = (data: TTSNotificationData) => { - NativeTTSMediaControl.showMediaNotification( - data.novelName, - data.chapterName, - data.coverUri, - data.isPlaying, - ); -}; - -export const updateTTSPlaybackState = (isPlaying: boolean) => { - NativeTTSMediaControl.updatePlaybackState(isPlaying); -}; - -export const updateTTSProgress = (current: number, total: number) => { - NativeTTSMediaControl.updateProgress(current, total); -}; - -export const dismissTTSNotification = () => { - NativeTTSMediaControl.dismiss(); -}; diff --git a/src/utils/useLoadingColors.ts b/src/utils/useLoadingColors.ts index b40e37807..0c68c7014 100644 --- a/src/utils/useLoadingColors.ts +++ b/src/utils/useLoadingColors.ts @@ -1,34 +1,40 @@ import { ThemeColors } from '@theme/types'; import color from 'color'; import { useAppSettings } from '@hooks/persisted'; -import { interpolateColor } from 'react-native-reanimated'; +import { useMemo } from 'react'; -const useLoadingColors = (theme: ThemeColors) => { - const highlightColor = color(theme.primary).alpha(0.08).string(); - const backgroundColor = color(theme.surface); +const BASE_STRENGTH = 0.08; +const STATIC_BASE_STRENGTH = 0.12; +const HIGHLIGHT_STRENGTH = 0.14; - let adjustedBackgroundColor; +export const getLoadingColors = ( + theme: ThemeColors, + disableLoadingAnimations = false, +) => { + const surfaceColor = color(theme.surface); + const foregroundColor = color(theme.onSurface); + const backgroundStrength = disableLoadingAnimations + ? STATIC_BASE_STRENGTH + : BASE_STRENGTH; - if (backgroundColor.isDark()) { - adjustedBackgroundColor = - backgroundColor.luminosity() !== 0 - ? backgroundColor.lighten(0.1).toString() - : backgroundColor.negate().darken(0.98).toString(); - } else { - adjustedBackgroundColor = backgroundColor.darken(0.04).toString(); - } + const backgroundColor = surfaceColor + .mix(foregroundColor, backgroundStrength) + .hex(); + const highlightColor = surfaceColor + .mix(foregroundColor, HIGHLIGHT_STRENGTH) + .hex(); - const { disableLoadingAnimations } = useAppSettings(); + return [highlightColor, backgroundColor] as const; +}; - if (disableLoadingAnimations) { - //If loading animations is disabled highlight color is never shown so make background color more visible to compensate - adjustedBackgroundColor = interpolateColor( - 0.01, //I have no idea why the interpolation amount has to be so small, I think its cus of the massive difference in alpha - [0, 1], - [adjustedBackgroundColor, highlightColor], - ); - } +const useLoadingColors = (theme: ThemeColors) => { + const { disableLoadingAnimations } = useAppSettings(); + const colors = useMemo( + () => getLoadingColors(theme, disableLoadingAnimations), + [disableLoadingAnimations, theme], + ); - return [highlightColor, adjustedBackgroundColor]; + return [...colors, disableLoadingAnimations] as const; }; + export default useLoadingColors; diff --git a/strings/languages/hi_IN/strings.json b/strings/languages/hi_IN/strings.json deleted file mode 100644 index 1896da6be..000000000 --- a/strings/languages/hi_IN/strings.json +++ /dev/null @@ -1,574 +0,0 @@ -{ - "aboutScreen": { - "website": "Website", - "discord": "Discord", - "github": "Github", - "helpTranslate": "Help translate", - "plugins": "Plugins", - "version": "Version", - "whatsNew": "What's new" - }, - "advancedSettings": "Advanced", - "advancedSettingsScreen": { - "cachedNovelsDeletedToast": "Cached novels deleted", - "chapterInsertFailed": "chapter insert failed", - "clearCachedNovels": "Clear cached novels", - "clearCachedNovelsDesc": "Delete cached novels which not in your library", - "clearDatabaseWarning": "Read and Downloaded chapters and progress of non-library novels will be lost.", - "clearUpdatesMessage": "Updates cleared.", - "clearUpdatesTab": "Clear updates tab", - "clearUpdatesWarning": "Updates tab will be cleared.", - "clearupdatesTabDesc": "Clears chapter entries in updates tab", - "dataManagement": "Data Management", - "deleteReadChapters": "Delete read chapters", - "deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.", - "importEpub": "Import Epub", - "importNovel": "Import Novel", - "importStaticFiles": "Import Static Files", - "novelInsertFailed": "novel insert failed", - "useFAB": "Use FAB instead of button", - "userAgent": "User Agent", - "recreateDBIndexes": "Recreate DB indexes", - "recreateDBIndexesToast": "Recreated DB indexes", - "recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.", - "recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices." - }, - "appearance": "Appearance", - "appearanceScreen": { - "accentColor": "Accent Color", - "alwaysShowNavLabels": "Always show nav labels", - "appLanguage": "App language", - "languagePickerModal": { - "title": "Select Language", - "restartNote": "You will need to restart the app for the language change to take full effect." - }, - "appLanguageDefault": "Default", - "appTheme": "App theme", - "darkTheme": "Dark Theme", - "hideBackdrop": "Hide backdrop", - "lightTheme": "Light Theme", - "navbar": "Navbar", - "novelInfo": "Novel info", - "pureBlackDarkMode": "Pure black dark mode", - "showHistoryInTheNav": "Show history in the nav", - "showUpdatesInTheNav": "Show updates in the nav", - "themeMode": "Theme mode", - "themeModeLight": "Light", - "themeModeDark": "Dark", - "themeModeSystem": "System", - "theme": { - "default": "Default", - "lavender": "Lavender", - "midnightDusk": "Midnight Dusk", - "daybreakBloom": "Daybreak Bloom", - "strawberry": "Strawberry Daiquiri", - "tako": "Tako", - "teal": "Teal", - "turquoise": "Turquoise", - "yotsuba": "Yotsuba", - "catppuccin": "Catppuccin", - "yinyang": "Yin & Yang" - } - }, - "backupScreen": { - "backupName": "Backup name", - "backupCreated": "Backup created successfully", - "backupRestored": "Backup restored successfully", - "savingBackup": "Saving Backup", - "categoriesRestored": "Restored %{count} categories", - "categoriesRestoredWithErrors": "Restored %{count} categories (%{failedCount} failed)", - "categoryRestoreFailed": "Failed to restore category: %{categoryName} - %{error}", - "categoryFileNotFound": "Category file not found in backup", - "categoryFileReadFailed": "Failed to read category file: %{error}", - "categoryFileWriteFailed": "Failed to write category file: %{error}", - "createBackup": "Create backup", - "createBackupDesc": "Can be used to restore current library", - "createBackupWarning": "Create backup may not work on devices with Android 9 or lower.", - "downloadingData": "Downloading Data", - "downloadingDownloadedFiles": "Downloading Downloaded Files", - "failed": "Backup failed", - "novelsRestored": "Restored %{count} novels", - "novelsRestoredWithErrors": "Restored %{count} novels (%{failedCount} failed)", - "novelBackupFailed": "Failed to backup novel: %{novelName} - %{error}", - "novelRestoreFailed": "Failed to restore novel: %{novelName} - %{error}", - "novelDirectoryNotFound": "Novel directory not found in backup", - "novelDirectoryReadFailed": "Failed to read novel directory: %{error}", - "restoringCategories": "Restoring Categories", - "restoringNovels": "Restoring Novels", - "restoringSettings": "Restoring Settings", - "settingsRestored": "Settings restored", - "settingsFileNotFound": "Settings file not found in backup", - "settingsRestoreFailed": "Failed to restore settings: %{error}", - "settingsFileWriteFailed": "Failed to write settings file: %{error}", - "versionFileWriteFailed": "Failed to write version file: %{error}", - "drive": { - "backup": "Drive Backup", - "backupInterruped": "Drive Backup Interrupted", - "googleDriveBackup": "Google Drive Backup", - "restore": "Drive Restore", - "restoreInterruped": "Drive Restore Interrupted" - }, - "googeDrive": "Googe Drive", - "googeDriveDesc": "Backup to your Google Drive", - "invalidBackupFolder": "Invalid backup folder", - "localBackup": "Local Backup", - "noBackupFound": "No backup found", - "preparingData": "Preparing Data", - "remote": { - "backup": "Self Host Backup", - "host": "Host", - "unknownHost": "Unknown host" - }, - "remoteBackup": "Remote Backup", - "restoreBackup": "Restore backup", - "restoreBackupDesc": "Restore library from backup file", - "restoreLargeBackupsWarning": "Restoring large backups may freeze the app until restoring is finished", - "restorinBackup": "Restoring backup", - "restoringData": "Restoring Data", - "selfHost": "Self Host", - "selfHostDesc": "Backup to your server", - "uploadingData": "Uploading Data", - "uploadingDownloadedFiles": "Uploading Downloaded Files" - }, - "browse": "Browse", - "browseScreen": { - "addedToLibrary": "Added to library", - "available": "Available", - "deletePluginMessage": "Are you sure you want to uninstall %{name}?", - "discover": "Discover", - "globalSearch": "Global search", - "installFailed": "Installation failed: %{name}", - "installed": "Installed", - "installedPlugin": "Installed %{name}", - "installedPlugins": "Installed plugins", - "lastUsed": "Last used", - "latest": "Latest", - "listEmpty": "Enable languages from settings", - "migration": { - "dialogMessage": "Migrate %{url}?", - "novelAlreadyInLibrary": "Novel already in library", - "selectSource": "Select Source", - "selectSourceDesc": "Select a Source To Migrate From" - }, - "noSource": "Your library does not have any novels from this source", - "pinnedPlugin": "Pinned %{name}", - "pinnedPlugins": "Pinned plugins", - "removeFromLibrary": "Removed from library", - "searchbar": "Search sources", - "searchResults": "Search results", - "selectNovel": "Select Novel", - "uninstalledPlugin": "Uninstalled %{name}", - "unpinnedPlugin": "Unpinned %{name}", - "updateFailed": "Update failed", - "updatedTo": "Updated to %{version}", - "settings": { - "title": "Plugin Settings", - "description": "Fill in the plugin settings. Restart app to apply the settings." - } - }, - "browseSettings": "Browse Settings", - "browseSettingsScreen": { - "concurrentSearches": "Concurrent Source Searches", - "multi": "Multi", - "languages": "Languages" - }, - "categories": { - "addCategories": "Add category", - "cantDeleteDefault": "You cant delete default category", - "default": "Default", - "defaultCategory": "Default category", - "deleteModal": { - "desc": "Do you wish to delete category", - "header": "Delete category" - }, - "duplicateError": "A category with this name already exists!", - "editCategories": "Rename category", - "emptyMsg": "You have no categories. Tap the plus button to create one for organizing your library", - "header": "Edit categories", - "local": "Local", - "setCategories": "Set categories", - "setModalEmptyMsg": "You have no categories. Tap the Edit button to create one for organizing your library" - }, - "repositories": { - "emptyMsg": "You have no repositories. Add your first plugin repository to get started." - }, - "common": { - "about": "About", - "add": "Add", - "all": "All", - "backup": "Backup", - "cancel": "Cancel", - "categories": "Categories", - "chapters": "Chapters", - "clear": "Clear", - "copiedToClipboard": "Copied to clipboard: %{name}", - "delete": "Delete", - "deleted": "Deleted %{name}", - "deprecated": "Deprecated", - "display": "Display", - "done": "Done", - "downloads": "Downloads", - "edit": "Edit", - "example": "Example", - "filter": "Filter", - "globally": "globally", - "install": "Install", - "logout": "Logout", - "name": "Name", - "newUpdateAvailable": "New update available", - "ok": "Ok", - "pause": "Pause", - "preparing": "Preparing", - "remove": "Remove", - "reset": "Reset", - "restore": "Restore", - "resume": "Resume", - "retry": "Retry", - "save": "Save", - "search": "Search", - "searchFor": "Search for", - "searchResults": "Search results", - "settings": "Settings", - "show": "Show", - "signIn": "Sign in", - "signOut": "Sign out", - "sort": "Sort", - "submit": "Submit", - "loading": "Loading ...", - "warning": "Warning" - }, - "webview": { - "refresh": "Refresh", - "share": "Share", - "openInBrowser": "Open in browser", - "clearCookies": "Clear cookies", - "cookiesCleared": "Cookies cleared", - "clearData": "Clear WebView data", - "dataDeleted": "WebView data cleared" - }, - "date": { - "calendar": { - "lastDay": "[Yesterday]", - "lastWeek": "[Last] dddd", - "nextDay": "[Tomorrow]", - "sameDay": "[Today]" - } - }, - "downloadScreen": { - "cancelDownloads": "Cancel downloads", - "cancelled": "Downloads cancelled.", - "chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it", - "chapterName": "Chapter: %{name}", - "completed": "Download completed", - "dbInfo": "Downloads are saved in a SQLite Database.", - "downloading": "Downloading", - "downloadingNovel": "Downloading: %{name}", - "downloadsLower": "downloads", - "noDownloads": "No downloads", - "pluginNotFound": "Plugin not found!", - "removeDownloadsWarning": "Are you sure? All downloaded chapters will be deleted." - }, - "generalSettings": "General", - "generalSettingsScreen": { - "asc": "(Ascending)", - "autoDownload": "Auto-download", - "bySource": "By source", - "chapterSort": "Default chapter sort", - "desc": "(Descending)", - "disableLoadingAnimations": "Disable loading animations", - "disableLoadingAnimationsDesc": "May improve performance on slower devices", - "disableHapticFeedback": "Disable haptic feedback", - "disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.", - "displayMode": "Display Mode", - "downloadNewChapters": "Download new chapters", - "epub": "EPUB", - "epubLocation": "EPUB Location", - "epubLocationDescription": "The place where you open and export your EPUB files.", - "globalUpdate": "Global update", - "gridSize": "Grid size", - "gridSizeDesc": "%{num} per row", - "itemsPerRow": "Items per row", - "itemsPerRowLibrary": "Items per row in library", - "jumpToLastReadChapter": "Jump to last read chapter in list", - "novel": "Novel", - "novelBadges": "Novel Badges", - "novelSort": "Novel Sort", - "refreshMetadata": "Automatically refresh metadata", - "refreshMetadataDescription": "Check for new cover and details when updating library", - "sortOrder": "Sort Order", - "updateLibrary": "Update library on launch", - "updateLibraryDesc": "Not recommended for low devices", - "updateOngoing": "Only update ongoing novels", - "updateTime": "Show last update time", - "useFAB": "Use FAB in Library" - }, - "globalSearch": { - "allSources": "all sources", - "searchIn": "Search a novel in" - }, - "history": "History", - "historyScreen": { - "chapter": "Chapter", - "clearHistorWarning": "Are you sure? All history will be lost.", - "deleted": "History deleted.", - "nothingReadRecently": "Nothing read recently", - "searchbar": "Search history" - }, - "library": "Library", - "libraryScreen": { - "bottomSheet": { - "display": { - "badges": "Badges", - "comfortable": "Comfortable grid", - "compact": "Compact grid", - "displayMode": "Display mode", - "download": "Download", - "downloadBadges": "Download badges", - "list": "List", - "noTitle": "Cover only grid", - "numberOfItems": "Number of Items", - "showNoOfItems": "Show number of items", - "unread": "Unread", - "unreadBadges": "Unread badges" - }, - "filters": { - "completed": "Completed", - "downloaded": "Downloaded", - "started": "Started", - "unread": "Unread" - }, - "sortOrders": { - "alphabetically": "Alphabetically", - "dateAdded": "Date added", - "download": "Downloaded", - "lastRead": "Last read", - "lastUpdated": "Last updated", - "totalChapters": "Total chapters", - "unread": "Unread" - } - }, - "empty": "Your library is empty. Add series to your library from Browse.", - "extraMenu": { - "importEpub": "Import Epub", - "openRandom": "Open Random Entry", - "updateCategory": "Update Category", - "updateLibrary": "Update Library" - }, - "searchbar": "Search library" - }, - "more": "More", - "moreScreen": { - "downloadOnly": "Downloaded only", - "downloadOnlyDesc": "Filters all novels in your library", - "downloadQueue": "Download queue", - "incognitoMode": "Incognito mode", - "incognitoModeDesc": "Pauses reading history" - }, - "novelScreen": { - "addToLibaray": "Add to library", - "bottomSheet": { - "displays": { - "chapterNumber": "Chapter number", - "sourceTitle": "Source title" - }, - "filters": { - "bookmarked": "Bookmarked", - "downloaded": "Downloaded", - "unread": "Unread" - }, - "order": { - "byChapterName": "By chapter name", - "bySource": "By source" - } - }, - "chapterChapnum": "Chapter %{num}", - "chapters": "chapters", - "continueReading": "Continue reading", - "exportEpubModal": { - "applyReaderTheme": "Apply reader theme to EPUB", - "customJSWarning": "Custom JS may not be supported by all EPUB readers", - "downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file", - "endChapter": "End Chapter", - "exportAll": "Export All Chapters", - "includeCustomCSS": "Include Custom CSS", - "includeCustomJS": "Include Custom JS", - "invalidRange": "Please enter valid chapter numbers", - "selectFolder": "Select destination folder for EPUB file", - "startChapter": "Start Chapter", - "startGreaterThanEnd": "Start chapter must be less than or equal to end chapter", - "title": "Export Novel as EPUB" - }, - "epub": { - "exportFailed": "Failed to export EPUB: %{error}", - "exportSuccess": "Successfully exported %{chapters} chapters as EPUB", - "noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.", - "noNovelSelected": "No novel selected for export" - }, - "coverSaved": "Cover saved", - "coverNotSaved": "Cover not saved", - "deleteChapterError": "Cant delete chapter chapter folder", - "deleteMessage": "Delete downloaded chapters?", - "deletedAllDownloads": "Deleted all Downloads", - "download": { - "custom": "Custom", - "customAmount": "Download custom amount", - "delete": "Delete downloads", - "next": "Next chapter", - "next10": "Next 10 chapter", - "next5": "Next 5 chapter", - "unread": "Unread" - }, - "edit": { - "addTag": "Add Tag", - "author": "Author: %{author}", - "cover": "Edit cover", - "info": "Edit info", - "status": "Status:", - "summary": "Description: %{summary}...", - "title": "Title: %{title}" - }, - "inLibaray": "In library", - "jumpToChapterModal": { - "chapterName": "Chapter Name", - "chapterNumber": "Chapter Number", - "error": { - "validChapterName": "Enter a valid chapter name", - "validChapterNumber": "Enter a valid chapter number" - }, - "jumpToChapter": "Jump to Chapter", - "openChapter": "Open Chapter" - }, - "migrate": "Migrate", - "noSummary": "No summary", - "noCoverFound": "No cover found", - "progress": "Progress %{progress} %", - "readChaptersDeleted": "Read chapters deleted", - "startReadingChapters": "Start reading %{name}", - "status": { - "cancelled": "Cancelled", - "completed": "Completed", - "licensed": "Licensed", - "onHiatus": "On Hiatus", - "ongoing": "Ongoing", - "publishingFinished": "Publishing Finished", - "unknown": "Unknown" - }, - "tracked": "Tracked", - "tracking": "Tracking", - "unknownStatus": "Unknown status", - "updatedToast": "Updated %{name}" - }, - "readerScreen": { - "bottomSheet": { - "allowTextSelection": "Text selection", - "autoscroll": "Auto-scroll", - "bionicReading": "Bionic reading", - "tapToScroll": "Tap to scroll", - "color": "Color", - "fontStyle": "Font style", - "fullscreen": "Fullscreen", - "lineHeight": "Line height", - "padding": "Padding", - "pageReader": "Paged reading (Experimental)", - "removeExtraSpacing": "Remove extra spacing", - "scrollAmount": "Scroll amount (screen height by default)", - "showBatteryAndTime": "Battery & time", - "showProgressPercentage": "Reading progress", - "swipeGestures": "Swipe between chapters", - "textAlign": "Text alignment", - "textSize": "Text size", - "useChapterDrawerSwipeNavigation": "Swipe to open drawer", - "verticalSeekbar": "Vertical seekbar", - "keepScreenOn": "Keep screen on", - "volumeButtonsScroll": "Volume button scrolling" - }, - "drawer": { - "scrollToBottom": "Scroll to bottom", - "scrollToCurrentChapter": "Scroll to current chapter", - "scrollToTop": "Scroll to top" - }, - "emptyChapterMessage": "

    Chapter is empty.

    Report on GitHub if it's available in WebView.

    Plugin: %{pluginId}

    Novel: %{novelName}

    Chapter: %{chapterName}

    ", - "finished": "Finished", - "nextChapter": "Next: %{name}", - "noNextChapter": "There's no next chapter", - "noPreviousChapter": "There's no previous chapter" - }, - "readerSettings": { - "autoScrollInterval": "Scroll interval (seconds)", - "autoScrollOffset": "Scroll offset (screen heights)", - "backgroundColor": "Background color", - "backgroundColorModal": "Background color", - "clearCustomCSS": "Reset your custom CSS?", - "clearCustomJS": "Reset your custom JS?", - "cssHint": "Target specific sources using #sourceId-[SOURCEID] in your selectors", - "customCSS": "Custom CSS", - "customJS": "Custom JS", - "deleteCustomTheme": "Delete theme", - "jsHint": "Available variables: html, novelName, chapterName, sourceId, chapterId, novelId", - "navigationControls": "Navigation Controls", - "notSaved": "Not saved", - "openCSSFile": "Import CSS file", - "openJSFile": "Import JS file", - "preset": "Preset", - "readingMode": "Reading Mode", - "readerTheme": "Theme", - "saveCustomTheme": "Save theme", - "textColor": "Text color", - "textColorModal": "Text color", - "title": "Reader", - "verticalSeekbarDesc": "Use vertical seekbar" - }, - "sourceScreen": { - "noResultsFound": "No results found" - }, - "statsScreen": { - "downloadedChapters": "Downloaded chapters", - "genreDistribution": "Genre distribution", - "readChapters": "Read chapters", - "sources": "Sources", - "statusDistribution": "Status distribution", - "title": "Statistics", - "titlesInLibrary": "Titles in library", - "totalChapters": "Total chapters", - "unreadChapters": "Unread chapters" - }, - "tracking": "Tracking", - "trackingScreen": { - "logOutMessage": "Log out from %{name}?", - "revalidate": "Revalidate", - "services": "Services" - }, - "updates": "Updates", - "updatesScreen": { - "deletedChapters": "Deleted %{num} chapters", - "emptyView": "No recent updates", - "lastUpdatedAt": "Library last updated:", - "libraryUpdated": "Library Updated", - "newChapters": "new Chapters", - "novelsUpdated": "%{num} novels updated", - "searchbar": "Search updates", - "unableToGetNovel": "Unable to get novel", - "updatesLower": "updates", - "updatingLibrary": "Updating library" - }, - "onboardingScreen": { - "welcome": "Welcome", - "pickATheme": "Pick a theme", - "light": "Light", - "dark": "Dark", - "system": "System", - "complete": "Complete" - }, - "notifications": { - "IMPORT_EPUB": "Importing EPUB", - "UPDATE_LIBRARY": "Updating Library", - "DRIVE_BACKUP": "Google Drive Backup", - "DRIVE_RESTORE": "Google Drive Restore", - "SELF_HOST_BACKUP": "Self-Host Backup", - "SELF_HOST_RESTORE": "Self-Host Restore", - "LOCAL_BACKUP": "Local Backup", - "LOCAL_RESTORE": "Local Restore", - "MIGRATE_NOVEL": "Migrating Novel", - "DOWNLOAD_CHAPTER": "Downloading Chapter" - } -} diff --git a/strings/languages/id_ID/strings.json b/strings/languages/id_ID/strings.json deleted file mode 100644 index 27c966577..000000000 --- a/strings/languages/id_ID/strings.json +++ /dev/null @@ -1,574 +0,0 @@ -{ - "aboutScreen": { - "website": "indonesia", - "discord": "\nindonesia", - "github": "Indonesia", - "helpTranslate": "INDONESIA", - "plugins": "Indonesia", - "version": "", - "whatsNew": "" - }, - "advancedSettings": "", - "advancedSettingsScreen": { - "cachedNovelsDeletedToast": "", - "chapterInsertFailed": "indonesian\n", - "clearCachedNovels": "", - "clearCachedNovelsDesc": "bahasa Indonesia", - "clearDatabaseWarning": ".", - "clearUpdatesMessage": "Updates cleared.", - "clearUpdatesTab": "Clear updates tab", - "clearUpdatesWarning": ".", - "clearupdatesTabDesc": "", - "dataManagement": "", - "deleteReadChapters": "Hapus bab yang telah dibaca\n", - "deleteReadChaptersDialogTitle": "Indonesia ", - "importEpub": "", - "importNovel": "", - "importStaticFiles": "", - "novelInsertFailed": "", - "useFAB": "", - "userAgent": "", - "recreateDBIndexes": "Recreate DB indexes", - "recreateDBIndexesToast": "", - "recreateDBIndexesDialogTitle": ".", - "recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices." - }, - "appearance": "", - "appearanceScreen": { - "accentColor": "Save anyway", - "alwaysShowNavLabels": "", - "appLanguage": "", - "languagePickerModal": { - "title": "", - "restartNote": "." - }, - "appLanguageDefault": "Indonesian", - "appTheme": "Indonesian\n", - "darkTheme": "", - "hideBackdrop": "", - "lightTheme": "", - "navbar": "", - "novelInfo": "", - "pureBlackDarkMode": "", - "showHistoryInTheNav": "", - "showUpdatesInTheNav": "", - "themeMode": "Theme mode", - "themeModeLight": "", - "themeModeDark": "", - "themeModeSystem": "Jawa", - "theme": { - "default": "", - "lavender": "", - "midnightDusk": "", - "daybreakBloom": "", - "strawberry": "", - "tako": "", - "teal": "", - "turquoise": "", - "yotsuba": "", - "catppuccin": "Catppuccin", - "yinyang": "" - } - }, - "backupScreen": { - "backupName": "", - "backupCreated": "", - "backupRestored": "", - "savingBackup": "Indonesia", - "categoriesRestored": "%{count}Indonesia", - "categoriesRestoredWithErrors": "%{failedCount}(indonesia) ", - "categoryRestoreFailed": "%{categoryName}(indonesia) %{error}", - "categoryFileNotFound": "Indonesia", - "categoryFileReadFailed": "%{error}Indonesia", - "categoryFileWriteFailed": "", - "createBackup": "", - "createBackupDesc": "", - "createBackupWarning": "", - "downloadingData": "", - "downloadingDownloadedFiles": "Downloading Downloaded Files", - "failed": "", - "novelsRestored": "Restored %{count} novels", - "novelsRestoredWithErrors": "Restored %{count} novels (%{failedCount} failed)", - "novelBackupFailed": "Failed to backup novel: %{novelName} - %{error}", - "novelRestoreFailed": "Failed to restore novel: %{novelName} - %{error}", - "novelDirectoryNotFound": "Novel directory not found in backup", - "novelDirectoryReadFailed": "Failed to read novel directory: %{error}", - "restoringCategories": "Restoring Categories", - "restoringNovels": "Restoring Novels", - "restoringSettings": "Restoring Settings", - "settingsRestored": "Settings restored", - "settingsFileNotFound": "Settings file not found in backup", - "settingsRestoreFailed": "Failed to restore settings: %{error}", - "settingsFileWriteFailed": "Failed to write settings file: %{error}", - "versionFileWriteFailed": "Failed to write version file: %{error}", - "drive": { - "backup": "", - "backupInterruped": "Drive Backup Interrupted", - "googleDriveBackup": "", - "restore": "", - "restoreInterruped": "Drive Restore Interrupted" - }, - "googeDrive": "", - "googeDriveDesc": "", - "invalidBackupFolder": "", - "localBackup": "Local Backup", - "noBackupFound": "", - "preparingData": "", - "remote": { - "backup": "", - "host": "", - "unknownHost": "" - }, - "remoteBackup": "😹", - "restoreBackup": "", - "restoreBackupDesc": "", - "restoreLargeBackupsWarning": "", - "restorinBackup": "", - "restoringData": "", - "selfHost": "", - "selfHostDesc": "", - "uploadingData": "", - "uploadingDownloadedFiles": "Uploading Downloaded Files" - }, - "browse": "Telusur", - "browseScreen": { - "addedToLibrary": "Telah ditambahkan ke koleksi", - "available": "", - "deletePluginMessage": "Are you sure you want to uninstall %{name}?", - "discover": "Jelajahi", - "globalSearch": "Pencarian global\n", - "installFailed": "Indonesia ", - "installed": "Indonesia", - "installedPlugin": "Installed %{name}", - "installedPlugins": "Installed plugins", - "lastUsed": "Terakhir digunakan\n", - "latest": "Terbaru\n", - "listEmpty": "Menggunakan bahasa dari pengaturan\n", - "migration": { - "dialogMessage": "Migrate %{url}?", - "novelAlreadyInLibrary": "Novel already in library", - "selectSource": "Select Source", - "selectSourceDesc": "Select a Source To Migrate From" - }, - "noSource": "Your library does not have any novels from this source", - "pinnedPlugin": "Pinned %{name}", - "pinnedPlugins": "Pinned plugins", - "removeFromLibrary": "Dihapus dari koleksi\n", - "searchbar": "Cari sumber\n", - "searchResults": "Search results", - "selectNovel": "Select Novel", - "uninstalledPlugin": "Uninstalled %{name}", - "unpinnedPlugin": "Unpinned %{name}", - "updateFailed": "Update failed", - "updatedTo": "Updated to %{version}", - "settings": { - "title": "Plugin Settings", - "description": "Fill in the plugin settings. Restart app to apply the settings." - } - }, - "browseSettings": "Pengaturan Setelan\n", - "browseSettingsScreen": { - "concurrentSearches": "Concurrent Source Searches", - "multi": "Multi", - "languages": "Bahasa\n" - }, - "categories": { - "addCategories": "Tambah kategori\n", - "cantDeleteDefault": "You cant delete default category", - "default": "Indonesia", - "defaultCategory": "Kategori default\n", - "deleteModal": { - "desc": "Apakah Kamu ingin untuk menghapus kategori ini\n", - "header": "Hapus kategori\n" - }, - "duplicateError": "Kategori dengan nama ini sudah ada!\n", - "editCategories": "Ganti nama kategori\n", - "emptyMsg": "Kamu tidak memiliki kategori. Tekan tombol tambah untuk membuat satu untuk merapikan koleksimu\n", - "header": "Ubah kategori\n", - "local": "Local", - "setCategories": "Mengatur kategori\n", - "setModalEmptyMsg": "Kamu tidak memiliki kategori. Tekan tombol Ganti untuk membuat satu untuk merapikan koleksi kamu\n" - }, - "repositories": { - "emptyMsg": "You have no repositories. Add your first plugin repository to get started." - }, - "common": { - "about": "Indonesia", - "add": "Tambahkan", - "all": "Semuanya", - "backup": "Indonesia", - "cancel": "Batal", - "categories": "Kategori", - "chapters": "Bab", - "clear": "Kosongkan", - "copiedToClipboard": "Copied to clipboard: %{name}", - "delete": "Delete", - "deleted": "Deleted %{name}", - "deprecated": "Deprecated", - "display": "Tampilan\n", - "done": "Done", - "downloads": "Downloads", - "edit": "Ubah", - "example": "Example", - "filter": "Filter", - "globally": "menyeluruh", - "install": "Pasang", - "logout": "Logout", - "name": "Nama", - "newUpdateAvailable": "Pembaruan terbaru tersedia", - "ok": "YA", - "pause": "Pause", - "preparing": "Preparing", - "remove": "Remove", - "reset": "Atur ulang", - "restore": "Indonesia", - "resume": "Resume", - "retry": "Coba lagi", - "save": "Simpan", - "search": "Cari\n", - "searchFor": "Pencarian untuk", - "searchResults": "Hasil pencarian", - "settings": "Pengaturan", - "show": "Show", - "signIn": "Sign in", - "signOut": "Sign out", - "sort": "Menyortir\n", - "submit": "Megirim", - "loading": "Loading ...", - "warning": "Warning" - }, - "webview": { - "refresh": "Refresh", - "share": "Share", - "openInBrowser": "Indonesia", - "clearCookies": "Clear cookies", - "cookiesCleared": "", - "clearData": "", - "dataDeleted": "Indonesia" - }, - "date": { - "calendar": { - "lastDay": "[Kemarin]\n", - "lastWeek": "dddd [Kemarin]\n", - "nextDay": "[Besok]\n", - "sameDay": "[Hari ini]\n" - } - }, - "downloadScreen": { - "cancelDownloads": "Indonesia", - "cancelled": "Indonesia", - "chapterEmptyOrScrapeError": "Indonesia", - "chapterName": "%{name}(indonesia", - "completed": "Indonesia", - "dbInfo": "Downloads are saved in a SQLite Database.", - "downloading": "Downloading", - "downloadingNovel": "Downloading: %{name}", - "downloadsLower": "light novel is super long so we’ve decided to split chapter 1 into 7 parts. Why 7 you ask? Well, the chapter itself was already kind of split into 7 parts already.\n\nStarted by alyschu and strengthened with OverTheRanbow’s awesome slime thoughts.\n\nOverTheRanbow, I dub thee, king of Japanese sound effects!\n\n\nWhere’s this? Say, what exactly is going on here?\n\nThe last thing I remembered was something useless about a sage or great sage….\n\nAnd now, I woke up.\n\nMy name’s Minami Satoru, a good man of 37 years old.\n\nIn order to save a Kohai from a criminal, I got stabbed from behind.\n\nGreat, I still remember it. No problem, there’s no need to panic.\n\nBesides, I’m a dashing person. The only time I’ve panicked was when I pooped my pants in elementary school.\n\nLooking around, I finally discovered, I couldn’t open my eyes.\n\nThis is pretty headache inducing. I tried to rub my head…… I didn’t get any response from my hand either. Before this, where exactly is my head?\n\nOi oi, give me a minute.\n\nGive me some time, I need to calm down. I think this is a good time to count prime numbers?\n\nOne, two, three, ah ——!!\n\nWait no, not like this! Actually, one is not even a prime number, right?\n\nNo no, this kind of thing doesn’t matter.\n\nRight now’s not the time to think about these useless things, my current condition should be anything other than reassuring, right?\n\nEh? W-what exactly is going on here?\n\nDon’t tell me…… I’ve already sank into a state of confusion and now I’m wasting time?\n\nI hurriedly confirmed if anywhere on my body was hurting.\n\nNothing hurts at all, it’s better to say that it’s actually pretty comfortable.\n\nNot even hot or cold, this is really a refreshing space.\n\nThis made me loosen up a little.\n\nAnd now to confirm my hands and feet… Let’s not talk about finger tips, neither my hands or feet had any responses at all.\n\nWhat exactly is going on?\n\nI was clearly only stabbed once, it shouldn’t have chopped away my hands and feet right?\n\nAlso, I can’t even open my eyes.\n\nI can’t see anything, this is pitch-black darkness.\n\nIn my heart, an anxiety that I’ve never felt before flooded out.\n\nIs this….. the legendary unconscious state?\n\nOr is that, although I have my consciousness, my nerves are all broken and I can’t move?\n\nOi oi oi, give me a break!\n\nI have to carefully think about it.\n\nA human will go mad shortly after if confined into a closed space. It could be said that the current me is in that state. Also, I can’t die even if I wanted to.\n\nWaiting to go mad just like this, how could that be possible!\n\nAt this time, a feeling of being touched by something traveled through my body.\n\nEh? What’s this….?\n\nI concentrate my entire consciousness on that sensation.\n\nAbdomen? An area that feels like the abdomen was touched by some kind of plant.\n\nEverything I gathered my consciousness onto a spot, I could then roughly understand the area of that spot. Occasionally, I could feel a slightly prickling sensation from something that seems to be a leaf-like plant.\n\nI feel a bit happier now.\n\nEven though I’m still inside the pitch-black darkness. But at least the sense of touch from the five senses is still working.\n\nI enthusiastically tried to move toward that blade of grass ——\n\nAs if crawling, my body indeed moved.\n\nI actually…. moved!?\n\nNow I’ve finally understood that I’m not on the hospital bed. Why, you say? Because the solid feeling of something like rocks came from under my abdomen.\n\nSo that’s what it is…. Even though I don’t really understand it, in short, I’m not in the hospital.\n\nNot only that, neither my eyes or ears worked.\n\nAlthough I don’t know where my head is, I still tried to move toward the direction of the grass. My consciousness was completely concentrated onto the part that touched.\n\nI couldn’t smell anything at all. I’m afraid that, it seems like my sense of smell has also failed?\n\nSpeaking of which, what kind of shape am I right now?\n\nAlthough I don’t want to admit, but from this streamlined contour and elasticity, I could only think of [that].\n\nA possibility flashed across my head.\n\nNo no…… how is that possible. No matter how you say it, it shouldn’t be….\n\nAll in all, let’s put the anxiety aside.\n\nI, started to try the last function, of a human’s five senses.\n\nHowever, since I don’t even know where my mouth is at, how exactly am I supposed to try it?\n\n[Activate Unique skill [Predator]? (Yes/No)]\n\nSuddenly, a voice came from my brain.\n\nHa? What did you say? It’s actually Unique skill [Predator]……!?\n\nSpeaking of which, what exactly is this voice?\n\nIt seems like that I had heard this voice when I passed on my last will to Tamura, was it not a hallucination?\n\nIs someone there? No, it seems a little different. Rather than to say someone’s here…… It’s more like the words floated out from my consciousness.\n\nThere is no sense of being human, it seemed inorganic, like a computer generated voice.\n\nAnyways, I chose No!\n\nNo response. Although I tried and waited for a long time, no more sound came out.\n\nIt looks like it wouldn’t ask a second time. Did I perchance choose the wrong option? Is this the type of game that would get stuck if you didn’t choose the Yes option?\n\nI thought it’d be like a Rpg, and if you don’t choose Yes it would keep on asking. Seems like I was wrong.\n\nIt clearly was the one that asked first, and then ignored me after. What a impolite fellow.\n\nAfter finally hearing a voice, I was actually a little happy about it.\n\nI immersed myself in regret.\n\nWell, there is no other choice. Anyways, I’ll try to test out the sense of taste.\n\nI concentrated my awareness onto the pile of grass, and moved my body.\n\nAfter confirming the part that was touched by the grass, I pressed my whole body onto it. This tactile impression, as expected, is undoubtedly grass.\n\nAs I was feeling the tactile impression of the grass, the part of the grass that was in contact with my body started to melt. At first I had thought my body was melting and was really startled, but it seems that only the grass had melted.\n\nAnd my body was absorbing the components of the grass.\n\nIt seems, my body didn’t have a mouth, and the part that touched things had replaced the function of the mouth instead. Just as a side note, I couldn’t taste anything at all.\n\nThat being said, it’s just like that.\n\nIt seems, the fact that I’m no longer being a human, there’s almost no mistakes about it.\n\nThen that means, sure enough, I was stabbed to death back then?\n\nRather than asking, I’m already almost convinced. Then, the fact that I’m not in a hospital, but in on a stone ground that grew weeds can be accepted.\n\nWhat happened with Tamura in the end?\n\nWhat about Sawatari?\n\nMy computer should be taken care of, right?\n\nI’m full of questions. But, there’s no point in worrying right now. It’s better to think about what to do from now on.\n\nHowever, the way I look right now ——\n\nAnd that feeling a moment ago….\n\nI directed my consciousness to my body again.\n\nPuni, Puni\n\nA body that moved with a rhythm.\n\nIn this total darkness, I spent a little time confirming the whole structure of my body.\n\nHow could this be possible!?\n\nI was clearly that kind of dashing and handsome man before, but now I’ve turned into a streamlined and bouncy slime!!\n\nWhat kind of joke was this! Who would even agree to this kind of thing!!\n\nBut this body’s contour, no matter how I think about it, it can only be that thing!!\n\nNo no, but, hm.\n\nI don’t really dislike it either? Umu, that thing really is kind of cute.\n\nAh but, if one were to be asked if they want to turn into something like that? I’d say over 90 percent of people would probably choose no.\n\nWell, I’ll have to accept it I guess…..\n\nIt seems, my [Soul] after I had died, turned into a monster in another world.\n\nThis originally should be impossible. Even if it’s possible the chances should only be near astronomical.\n\nIn conclusion, I reincarnated into a slime.\n\nMugu mugu.\n\nRight now, I’m eating grass.\n\nWhy, you ask…… do I even need to answer!\n\nBecause I am ex. tre. me. ly. bor. ed!\n\nAfter accepting that I’ve been turned into a slime, many days have passed. I don’t really know the exact amount of time….. Since there’s no concept of time at all in this pitch-black darkness.\n\n\n\nDuring this period of time, I discovered that the body of a slime is actually pretty convenient. I can’t get hungry, nor become sleepy. Which means, I don’t need to worry about eating and sleeping at all.\n\nI’ve also ascertained another thing.\n\nAlthough I don’t know where this is, but there seems to be no other living beings. And thanks to that, there is completely no need to worry about my life being in danger…… Just that it’s kind of hard to stand doing nothing every single day.\n\nAfter that time, the mysterious voice never appeared again. If it’s now, it’d be fine to chat with you a little, ya know.\n\nAnyways, I helplessly started to eat grass.\n\nIt’s not like I’ve got anything else to do, this is only a way to pass time.\n\nRight now, the grass I’ve absorbed is disintegrating in my body. I can feel the components slowly accumulate after being dismantled.\n\nTo ask what meaning does this have, I’d say it has no meaning at all.\n\nIf I didn’t do anything, I feel like I would really go mad. I’m just afraid of that, that’s all.\n\nAbsorb, Dismantle, Storage. Recently, I had become totally proficient in these set of actions.\n\nThere is something very incredible here.\n\nUp until now, I seemed to not have any excreting behavior.\n\nAfter all, I’m a slime, there is indeed a high possibility that this is unnecessary. But, where did the stuff that was stored go?\n\nJust based on feelings, I felt no change of my contour.\n\nWhat exactly is going on here?\n\n[Answer. The items are stored inside the Unique Skill [Predator]’s stomach sack. Also, the current usage of the space does not exceed 1 percent]\n\nWhat? It actually answered ——!\n\nBut, how exactly did I use the skill? I should have answered No! earlier that time.\n\n[Answer. Did not use the Unique skill [Predator]. From the settings, absorbed materials will automatically be stored inside the stomach sack. It can be changed at will]\n\nWhat? The answer this time felt really fluent! Wait no, putting that aside for now……\n\nThen, what would happen if I used the skill?\n\n[Answer. Unique Skill [Predator]’s main effects are ——–\n\nAnalyze: Analyze and examine the stored target. Can create items that can be produced. Under the conditions with sufficient materials, replication is possible. After successfully analyzing skills or magic, it is possible to learn the target’s skills/magic.\n\nStomach sack: Store targets that have been predated. Also, it can conserve objects made from Analyze. The objects stored inside the Stomach sack will go into a state of stasis.\n\nMimicry: Mimic targets that have been absorbed, can use the same level of abilities of the target. However, it’s only limited to objects that have been successfully analyzed.\n\nIsolate: Storage of impossible to analyze or harmful effects. After passing through the nullification process, restores magic power.\n\n—– The five abilities above]\n\nEh……Eh?\n\nA nostalgic feeling of being surprised. It feels like, this skill is kind of cheating…… This isn’t an ability that a mere slime should have, right?\n\nWait a minute, before that, who’s voice was it that answered me?\n\n[Answer. Unique Skill [Great Sage]’s effect. Since the ability had finished cementing, the speed of response has increased]\n\nGreat Sage huh…… I thought it was only mocking me. I didn’t think that it’d be this reliable. Take care of me from now on.\n\nSpeaking of which, it’s not the time to be stubborn anymore…..\n\nIf I could heal this endless loneliness, it’d be fine even if this [voice] was only a hallucination.\n\nI have finally experienced the feeling of true relaxation after such a long time.\n\nPrevious Chapter | Main Page | Next Chapter", - "noDownloads": "Light novel is super long so we’ve decided to split chapter 1 into 7 parts. Why 7 you ask? Well, the chapter itself was already kind of split into 7 parts already.\n\nStarted by alyschu and strengthened with OverTheRanbow’s awesome slime thoughts.\n\nOverTheRanbow, I dub thee, king of Japanese sound effects!\n\n\nWhere’s this? Say, what exactly is going on here?\n\nThe last thing I remembered was something useless about a sage or great sage….\n\nAnd now, I woke up.\n\nMy name’s Minami Satoru, a good man of 37 years old.\n\nIn order to save a Kohai from a criminal, I got stabbed from behind.\n\nGreat, I still remember it. No problem, there’s no need to panic.\n\nBesides, I’m a dashing person. The only time I’ve panicked was when I pooped my pants in elementary school.\n\nLooking around, I finally discovered, I couldn’t open my eyes.\n\nThis is pretty headache inducing. I tried to rub my head…… I didn’t get any response from my hand either. Before this, where exactly is my head?\n\nOi oi, give me a minute.\n\nGive me some time, I need to calm down. I think this is a good time to count prime numbers?\n\nOne, two, three, ah ——!!\n\nWait no, not like this! Actually, one is not even a prime number, right?\n\nNo no, this kind of thing doesn’t matter.\n\nRight now’s not the time to think about these useless things, my current condition should be anything other than reassuring, right?\n\nEh? W-what exactly is going on here?\n\nDon’t tell me…… I’ve already sank into a state of confusion and now I’m wasting time?\n\nI hurriedly confirmed if anywhere on my body was hurting.\n\nNothing hurts at all, it’s better to say that it’s actually pretty comfortable.\n\nNot even hot or cold, this is really a refreshing space.\n\nThis made me loosen up a little.\n\nAnd now to confirm my hands and feet… Let’s not talk about finger tips, neither my hands or feet had any responses at all.\n\nWhat exactly is going on?\n\nI was clearly only stabbed once, it shouldn’t have chopped away my hands and feet right?\n\nAlso, I can’t even open my eyes.\n\nI can’t see anything, this is pitch-black darkness.\n\nIn my heart, an anxiety that I’ve never felt before flooded out.\n\nIs this….. the legendary unconscious state?\n\nOr is that, although I have my consciousness, my nerves are all broken and I can’t move?\n\nOi oi oi, give me a break!\n\nI have to carefully think about it.\n\nA human will go mad shortly after if confined into a closed space. It could be said that the current me is in that state. Also, I can’t die even if I wanted to.\n\nWaiting to go mad just like this, how could that be possible!\n\nAt this time, a feeling of being touched by something traveled through my body.\n\nEh? What’s this….?\n\nI concentrate my entire consciousness on that sensation.\n\nAbdomen? An area that feels like the abdomen was touched by some kind of plant.\n\nEverything I gathered my consciousness onto a spot, I could then roughly understand the area of that spot. Occasionally, I could feel a slightly prickling sensation from something that seems to be a leaf-like plant.\n\nI feel a bit happier now.\n\nEven though I’m still inside the pitch-black darkness. But at least the sense of touch from the five senses is still working.\n\nI enthusiastically tried to move toward that blade of grass ——\n\nAs if crawling, my body indeed moved.\n\nI actually…. moved!?\n\nNow I’ve finally understood that I’m not on the hospital bed. Why, you say? Because the solid feeling of something like rocks came from under my abdomen.\n\nSo that’s what it is…. Even though I don’t really understand it, in short, I’m not in the hospital.\n\nNot only that, neither my eyes or ears worked.\n\nAlthough I don’t know where my head is, I still tried to move toward the direction of the grass. My consciousness was completely concentrated onto the part that touched.\n\nI couldn’t smell anything at all. I’m afraid that, it seems like my sense of smell has also failed?\n\nSpeaking of which, what kind of shape am I right now?\n\nAlthough I don’t want to admit, but from this streamlined contour and elasticity, I could only think of [that].\n\nA possibility flashed across my head.\n\nNo no…… how is that possible. No matter how you say it, it shouldn’t be….\n\nAll in all, let’s put the anxiety aside.\n\nI, started to try the last function, of a human’s five senses.\n\nHowever, since I don’t even know where my mouth is at, how exactly am I supposed to try it?\n\n[Activate Unique skill [Predator]? (YES/NO)]\n\nSuddenly, a voice came from my brain.\n\nHa? What did you say? It’s actually Unique skill [Predator]……!?\n\nSpeaking of which, what exactly is this voice?\n\nIt seems like that I had heard this voice when I passed on my last will to Tamura, was it not a hallucination?\n\nIs someone there? No, it seems a little different. Rather than to say someone’s here…… It’s more like the words floated out from my consciousness.\n\nThere is no sense of being human, it seemed inorganic, like a computer generated voice.\n\nAnyways, I chose NO!\n\nNo response. Although I tried and waited for a long time, no more sound came out.\n\nIt looks like it wouldn’t ask a second time. Did I perchance choose the wrong option? Is this the type of game that would get stuck if you didn’t choose the YES option?\n\nI thought it’d be like a RPG, and if you don’t choose YES it would keep on asking. Seems like I was wrong.\n\nIt clearly was the one that asked first, and then ignored me after. What a impolite fellow.\n\nAfter finally hearing a voice, I was actually a little happy about it.\n\nI immersed myself in regret.\n\nWell, there is no other choice. Anyways, I’ll try to test out the sense of taste.\n\nI concentrated my awareness onto the pile of grass, and moved my body.\n\nAfter confirming the part that was touched by the grass, I pressed my whole body onto it. This tactile impression, as expected, is undoubtedly grass.\n\nAs I was feeling the tactile impression of the grass, the part of the grass that was in contact with my body started to melt. At first I had thought my body was melting and was really startled, but it seems that only the grass had melted.\n\nAnd my body was absorbing the components of the grass.\n\nIt seems, my body didn’t have a mouth, and the part that touched things had replaced the function of the mouth instead. Just as a side note, I couldn’t taste anything at all.\n\nThat being said, it’s just like that.\n\nIt seems, the fact that I’m no longer being a human, there’s almost no mistakes about it.\n\nThen that means, sure enough, I was stabbed to death back then?\n\nRather than asking, I’m already almost convinced. Then, the fact that I’m not in a hospital, but in on a stone ground that grew weeds can be accepted.\n\nWhat happened with Tamura in the end?\n\nWhat about Sawatari?\n\nMy computer should be taken care of, right?\n\nI’m full of questions. But, there’s no point in worrying right now. It’s better to think about what to do from now on.\n\nHowever, the way I look right now ——\n\nAnd that feeling a moment ago….\n\nI directed my consciousness to my body again.\n\nPuni, Puni\n\nA body that moved with a rhythm.\n\nIn this total darkness, I spent a little time confirming the whole structure of my body.\n\nHow could this be possible!?\n\nI was clearly that kind of dashing and handsome man before, but now I’ve turned into a streamlined and bouncy slime!!\n\nWhat kind of joke was this! Who would even agree to this kind of thing!!\n\nBut this body’s contour, no matter how I think about it, it can only be that thing!!\n\nNo no, but, hm.\n\nI don’t really dislike it either? Umu, that thing really is kind of cute.\n\nAh but, if one were to be asked if they want to turn into something like that? I’d say over 90 percent of people would probably choose no.\n\nWell, I’ll have to accept it I guess…..\n\nIt seems, my [Soul] after I had died, turned into a monster in another world.\n\nThis originally should be impossible. Even if it’s possible the chances should only be near astronomical.\n\nIn conclusion, I reincarnated into a slime.\n\nMugu mugu.\n\nRight now, I’m eating grass.\n\nWhy, you ask…… do I even need to answer!\n\nBecause I am ex.tre.me.ly. bor.ed!\n\nAfter accepting that I’ve been turned into a slime, many days have passed. I don’t really know the exact amount of time….. Since there’s no concept of time at all in this pitch-black darkness.\n\n\n\nDuring this period of time, I discovered that the body of a slime is actually pretty convenient. I can’t get hungry, nor become sleepy. Which means, I don’t need to worry about eating and sleeping at all.\n\nI’ve also ascertained another thing.\n\nAlthough I don’t know where this is, but there seems to be no other living beings. And thanks to that, there is completely no need to worry about my life being in danger…… Just that it’s kind of hard to stand doing nothing every single day.\n\nAfter that time, the mysterious voice never appeared again. If it’s now, it’d be fine to chat with you a little, ya know.\n\nAnyways, I helplessly started to eat grass.\n\nIt’s not like I’ve got anything else to do, this is only a way to pass time.\n\nRight now, the grass I’ve absorbed is disintegrating in my body. I can feel the components slowly accumulate after being dismantled.\n\nTo ask what meaning does this have, I’d say it has no meaning at all.\n\nIf I didn’t do anything, I feel like I would really go mad. I’m just afraid of that, that’s all.\n\nAbsorb, Dismantle, Storage. Recently, I had become totally proficient in these set of actions.\n\nThere is something very incredible here.\n\nUp until now, I seemed to not have any excreting behavior.\n\nAfter all, I’m a slime, there is indeed a high possibility that this is unnecessary. But, where did the stuff that was stored go?\n\nJust based on feelings, I felt no change of my contour.\n\nWhat exactly is going on here?\n\n[Answer. The items are stored inside the Unique Skill [Predator]’s stomach sack. Also, the current usage of the space does not exceed 1 percent]\n\nWhat? It actually answered ——!\n\nBut, how exactly did I use the skill? I should have answered NO! earlier that time.\n\n[Answer. Did not use the Unique skill [Predator]. From the settings, absorbed materials will automatically be stored inside the stomach sack. It can be changed at will]\n\nWhat? The answer this time felt really fluent! Wait no, putting that aside for now……\n\nThen, what would happen if I used the skill?\n\n[Answer. Unique Skill [Predator]’s main effects are ——–\n\nAnalyze: Analyze and examine the stored target. Can create items that can be produced. Under the conditions with sufficient materials, replication is possible. After successfully analyzing skills or magic, it is possible to learn the target’s skills/magic.\n\nStomach sack: Store targets that have been predated. Also, it can conserve objects made from Analyze. The objects stored inside the Stomach sack will go into a state of stasis.\n\nMimicry: Mimic targets that have been absorbed, can use the same level of abilities of the target. However, it’s only limited to objects that have been successfully analyzed.\n\nIsolate: Storage of impossible to analyze or harmful effects. After passing through the nullification process, restores magic power.\n\n—– The five abilities above]\n\nEh……Eh?\n\nA nostalgic feeling of being surprised. It feels like, this skill is kind of cheating…… This isn’t an ability that a mere slime should have, right?\n\nWait a minute, before that, who’s voice was it that answered me?\n\n[Answer. Unique Skill [Great Sage]’s effect. Since the ability had finished cementing, the speed of response has increased]\n\nGreat Sage huh…… I thought it was only mocking me. I didn’t think that it’d be this reliable. Take care of me from now on.\n\nSpeaking of which, it’s not the time to be stubborn anymore…..\n\nIf I could heal this endless loneliness, it’d be fine even if this [voice] was only a hallucination.\n\nI have finally experienced the feeling of true relaxation after such a long time.\n\nPrevious Chapter | Main Page | Next Chapter\n", - "pluginNotFound": "Plugin not found!", - "removeDownloadsWarning": "Are you sure? All downloaded chapters will be deleted." - }, - "generalSettings": "Umum\n", - "generalSettingsScreen": { - "asc": "(Naik)\n", - "autoDownload": "Unduh otomatis\n", - "bySource": "Berdasarkan sumber\n", - "chapterSort": "Pengurutan Bab bawaan\n", - "desc": "(Menurun)\n", - "disableLoadingAnimations": "Disable loading animations", - "disableLoadingAnimationsDesc": "May improve performance on slower devices", - "disableHapticFeedback": "Nonaktifkan umpan balik haptic\n", - "disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.", - "displayMode": "Mode tampilan\n", - "downloadNewChapters": "Unduh bab terbaru\n", - "epub": "EPUB\n", - "epubLocation": "Lokasi EPUB\n", - "epubLocationDescription": "Lokasi dimana anda membuka dan mengekspor file EPUB anda.\n", - "globalUpdate": "Pembaruan global\n", - "gridSize": "Grid size", - "gridSizeDesc": "%{num} per row", - "itemsPerRow": "Barang-barang per baris\n", - "itemsPerRowLibrary": "Barang-barang per baris di perpustakaan\n", - "jumpToLastReadChapter": "Jump to last read chapter in list", - "novel": "Novel\n", - "novelBadges": "Lencana Novel", - "novelSort": "Urutkan Novel", - "refreshMetadata": "Memperbarui metadata secara otomatis\n", - "refreshMetadataDescription": "Periksa sampul dan detail baru saat memperbarui perpustakaan\n", - "sortOrder": "Sort Order", - "updateLibrary": "Perbarui perpustakaan saat diluncurkan\n", - "updateLibraryDesc": "Not recommended for low devices", - "updateOngoing": "Hanya memperbarui novel yang sedang berlangsung\n", - "updateTime": "Perlihatkan waktu pembaruan terakhir\n", - "useFAB": "Gunakan FAB di perpustakaan\n" - }, - "globalSearch": { - "allSources": "semua sumber\n", - "searchIn": "Cari novel di\n" - }, - "history": "Sejarah", - "historyScreen": { - "chapter": "Indonesia", - "clearHistorWarning": "Apa anda yakin? Semua riwayat akan hilang.\n", - "deleted": "History deleted.", - "nothingReadRecently": "Nothing read recently", - "searchbar": "Pencarian riwayat" - }, - "library": "Koleksi", - "libraryScreen": { - "bottomSheet": { - "display": { - "badges": "Lencana\n", - "comfortable": "Baris nyaman\n", - "compact": "Baris tersusun rapat\n", - "displayMode": "Mode Tampilan", - "download": "Indonesia", - "downloadBadges": "Lencana unduh\n", - "list": "Daftar\n", - "noTitle": "Hanya menutupi baris\n", - "numberOfItems": "Number of Items", - "showNoOfItems": "Tampilkan banyaknya barang\n", - "unread": "Unread", - "unreadBadges": "Lencana belum dibaca\n" - }, - "filters": { - "completed": "Sudah selesai", - "downloaded": "Diunduh", - "started": "Sudah dimulai", - "unread": "Belum dibaca" - }, - "sortOrders": { - "alphabetically": "Menurut Abjad", - "dateAdded": "Tanggal Ditambahkan", - "download": "Diunduh", - "lastRead": "Terakhir Dibaca", - "lastUpdated": "Terakhir kali diperbarui", - "totalChapters": "Jimlah bab", - "unread": "belum dibaca" - } - }, - "empty": "Pustaka kamu kosong. Tambahkan serial ke pustaka kamu dari Jelajah.\n", - "extraMenu": { - "importEpub": "Import Epub", - "openRandom": "", - "updateCategory": "Update Category", - "updateLibrary": "Indonesia" - }, - "searchbar": "Cari di pustaka" - }, - "more": "Lainnya\n", - "moreScreen": { - "downloadOnly": "Terunduh saja\n", - "downloadOnlyDesc": "Filters all novels in your library", - "downloadQueue": "Download queue", - "incognitoMode": "Mode penyamaran\n", - "incognitoModeDesc": "Pauses reading history" - }, - "novelScreen": { - "addToLibaray": "Tambahkan ke koleksi\n", - "bottomSheet": { - "displays": { - "chapterNumber": "Chapter number", - "sourceTitle": "Source title" - }, - "filters": { - "bookmarked": "Bookmarked", - "downloaded": "Downloaded", - "unread": "Unread" - }, - "order": { - "byChapterName": "By chapter name", - "bySource": "By source" - } - }, - "chapterChapnum": "Chapter %{num}", - "chapters": "bab\n", - "continueReading": "Lanjutkan membaca\n", - "exportEpubModal": { - "applyReaderTheme": "Apply reader theme to EPUB", - "customJSWarning": "Custom JS may not be supported by all EPUB readers", - "downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file", - "endChapter": "Indonesia ", - "exportAll": "Export All Chapters", - "includeCustomCSS": "Include Custom CSS", - "includeCustomJS": "Include Custom JS", - "invalidRange": "Please enter valid chapter numbers", - "selectFolder": "Select destination folder for EPUB file", - "startChapter": "Start Chapter", - "startGreaterThanEnd": "Start chapter must be less than or equal to end chapter", - "title": "Export Novel as EPUB" - }, - "epub": { - "exportFailed": "Failed to export EPUB: %{error}", - "exportSuccess": "Successfully exported %{chapters} chapters as EPUB", - "noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.", - "noNovelSelected": "No novel selected for export" - }, - "coverSaved": "Cover saved", - "coverNotSaved": "Cover not saved", - "deleteChapterError": "Cant delete chapter chapter folder", - "deleteMessage": "Delete downloaded chapters?", - "deletedAllDownloads": "Deleted all Downloads", - "download": { - "custom": "Custom", - "customAmount": "Download custom amount", - "delete": "Delete downloads", - "next": "Next chapter", - "next10": "Next 10 chapter", - "next5": "Next 5 chapter", - "unread": "Unread" - }, - "edit": { - "addTag": "Add Tag", - "author": "Author: %{author}", - "cover": "Edit cover", - "info": "Edit info", - "status": "Status:", - "summary": "Description: %{summary}...", - "title": "" - }, - "inLibaray": "Di koleksi\n", - "jumpToChapterModal": { - "chapterName": "Nama Bab\n", - "chapterNumber": "Nomor Bab\n", - "error": { - "validChapterName": "Masukan nama bab yang benar\n", - "validChapterNumber": "Masukan nomor bab yang benar\n" - }, - "jumpToChapter": "Melompat ke Bab\n", - "openChapter": "Buka Bab\n" - }, - "migrate": "Pindah\n", - "noSummary": "Tidak ada rangkuman\n", - "noCoverFound": "No cover found", - "progress": "Progress %{progress} %", - "readChaptersDeleted": "Read chapters deleted", - "startReadingChapters": "Start reading %{name}", - "status": { - "cancelled": "Cancelled", - "completed": "Completed", - "licensed": "Licensed", - "onHiatus": "On Hiatus", - "ongoing": "Ongoing", - "publishingFinished": "Publishing Finished", - "unknown": "Unknown" - }, - "tracked": "Tracked", - "tracking": "Tracking", - "unknownStatus": "Unknown status", - "updatedToast": "Updated %{name}" - }, - "readerScreen": { - "bottomSheet": { - "allowTextSelection": "Text selection", - "autoscroll": "Auto-scroll", - "bionicReading": "Bionic reading", - "tapToScroll": "Tap to scroll", - "color": "Warna\n", - "fontStyle": "Gaya huruf\n", - "fullscreen": "Layar Penuh", - "lineHeight": "Line height", - "padding": "Padding", - "pageReader": "Paged reading (Experimental)", - "removeExtraSpacing": "Remove extra spacing", - "scrollAmount": "Banyak gulung (tinggi layar secara bawaan)\n", - "showBatteryAndTime": "Battery & time", - "showProgressPercentage": "Reading progress", - "swipeGestures": "Swipe between chapters", - "textAlign": "Text alignment", - "textSize": "Ukuran teks\n", - "useChapterDrawerSwipeNavigation": "Swipe to open drawer", - "verticalSeekbar": "Vertical seekbar", - "keepScreenOn": "Keep screen on", - "volumeButtonsScroll": "Volume button scrolling" - }, - "drawer": { - "scrollToBottom": "Gulung ke bawah\n", - "scrollToCurrentChapter": "Gulir ke bab saat ini", - "scrollToTop": "Gulung ke atas\n" - }, - "emptyChapterMessage": "

    Bab kosong.

    Laporkan di GitHub jika tersedia di WebView.

    Plugin: %{pluginId}

    Novel: %{novelName}

    Bab: %{chapterName}

    ", - "finished": "Selesai", - "nextChapter": "Berikutnya: %{name}", - "noNextChapter": "Tidak ada bab berikutnya", - "noPreviousChapter": "Tidak ada bab sebelumnya" - }, - "readerSettings": { - "autoScrollInterval": "Scroll interval (seconds)", - "autoScrollOffset": "Scroll offset (screen heights)", - "backgroundColor": "Warna latar belakang\n", - "backgroundColorModal": "Background color", - "clearCustomCSS": "Reset your custom CSS?", - "clearCustomJS": "Reset your custom JS?", - "cssHint": "Target specific sources using #sourceId-[SOURCEID] in your selectors", - "customCSS": "Kostumisasi CSS\n", - "customJS": "JS Khusus\n", - "deleteCustomTheme": "Delete theme", - "jsHint": "Available variables: html, novelName, chapterName, sourceId, chapterId, novelId", - "navigationControls": "Navigation Controls", - "notSaved": "Tidak tersimpan\n", - "openCSSFile": "Import CSS file", - "openJSFile": "Import JS file", - "preset": "Prasetel\n", - "readingMode": "Reading Mode", - "readerTheme": "Theme", - "saveCustomTheme": "Save theme", - "textColor": "Warna teks\n", - "textColorModal": "Text color", - "title": "Pembaca\n", - "verticalSeekbarDesc": "Use vertical seekbar" - }, - "sourceScreen": { - "noResultsFound": "Tidak ada hasil ditemukan\n" - }, - "statsScreen": { - "downloadedChapters": "Downloaded chapters\n", - "genreDistribution": "Genre distribution\n", - "readChapters": "Baca Bab\n", - "sources": "Sources", - "statusDistribution": "Status distribusi", - "title": "Statistics\n", - "titlesInLibrary": "Titles in library\n", - "totalChapters": "Total chapters\n", - "unreadChapters": "Unread chapters\n" - }, - "tracking": "Tracking", - "trackingScreen": { - "logOutMessage": "Log out from %{name}?", - "revalidate": "Revalidate", - "services": "Services" - }, - "updates": "Pembaruan\n", - "updatesScreen": { - "deletedChapters": "Deleted %{num} chapters", - "emptyView": "Tidak ada pembaruan terkini\n", - "lastUpdatedAt": "Pustaka terakhir diperbarui:\n", - "libraryUpdated": "Library Updated", - "newChapters": "Bab baru\n", - "novelsUpdated": "%{num} novels updated", - "searchbar": "Cari di pembaruan\n", - "unableToGetNovel": "Unable to get novel", - "updatesLower": "Pembaruan\n", - "updatingLibrary": "Updating library" - }, - "onboardingScreen": { - "welcome": "Welcome", - "pickATheme": "Pick a theme", - "light": "Light", - "dark": "Dark", - "system": "System", - "complete": "Complete" - }, - "notifications": { - "IMPORT_EPUB": "Importing EPUB", - "UPDATE_LIBRARY": "Updating Library", - "DRIVE_BACKUP": "Google Drive Backup", - "DRIVE_RESTORE": "Google Drive Restore", - "SELF_HOST_BACKUP": "Self-Host Backup", - "SELF_HOST_RESTORE": "Self-Host Restore", - "LOCAL_BACKUP": "Local Backup", - "LOCAL_RESTORE": "Local Restore", - "MIGRATE_NOVEL": "Migrating Novel", - "DOWNLOAD_CHAPTER": "Downloading Chapter" - } -} diff --git a/strings/languages/ko_KR/strings.json b/strings/languages/ko_KR/strings.json deleted file mode 100644 index 447217f13..000000000 --- a/strings/languages/ko_KR/strings.json +++ /dev/null @@ -1,574 +0,0 @@ -{ - "aboutScreen": { - "website": "", - "discord": "Discord", - "github": "Github", - "helpTranslate": "Help translate", - "plugins": "Plugins", - "version": "Version", - "whatsNew": "What's new" - }, - "advancedSettings": "Advanced", - "advancedSettingsScreen": { - "cachedNovelsDeletedToast": "Cached novels deleted", - "chapterInsertFailed": "chapter insert failed", - "clearCachedNovels": "Clear cached novels", - "clearCachedNovelsDesc": "Delete cached novels which not in your library", - "clearDatabaseWarning": "Read and Downloaded chapters and progress of non-library novels will be lost.", - "clearUpdatesMessage": "Updates cleared.", - "clearUpdatesTab": "Clear updates tab", - "clearUpdatesWarning": "Updates tab will be cleared.", - "clearupdatesTabDesc": "Clears chapter entries in updates tab", - "dataManagement": "Data Management", - "deleteReadChapters": "Delete read chapters", - "deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.", - "importEpub": "Import Epub", - "importNovel": "Import Novel", - "importStaticFiles": "Import Static Files", - "novelInsertFailed": "novel insert failed", - "useFAB": "Use FAB instead of button", - "userAgent": "User Agent", - "recreateDBIndexes": "Recreate DB indexes", - "recreateDBIndexesToast": "Recreated DB indexes", - "recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.", - "recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices." - }, - "appearance": "Appearance", - "appearanceScreen": { - "accentColor": "Accent Color", - "alwaysShowNavLabels": "Always show nav labels", - "appLanguage": "App language", - "languagePickerModal": { - "title": "Select Language", - "restartNote": "You will need to restart the app for the language change to take full effect." - }, - "appLanguageDefault": "Default", - "appTheme": "App theme", - "darkTheme": "Dark Theme", - "hideBackdrop": "Hide backdrop", - "lightTheme": "Light Theme", - "navbar": "Navbar", - "novelInfo": "Novel info", - "pureBlackDarkMode": "Pure black dark mode", - "showHistoryInTheNav": "Show history in the nav", - "showUpdatesInTheNav": "Show updates in the nav", - "themeMode": "Theme mode", - "themeModeLight": "Light", - "themeModeDark": "Dark", - "themeModeSystem": "System", - "theme": { - "default": "Default", - "lavender": "Lavender", - "midnightDusk": "Midnight Dusk", - "daybreakBloom": "Daybreak Bloom", - "strawberry": "Strawberry Daiquiri", - "tako": "Tako", - "teal": "Teal", - "turquoise": "Turquoise", - "yotsuba": "Yotsuba", - "catppuccin": "Catppuccin", - "yinyang": "Yin & Yang" - } - }, - "backupScreen": { - "backupName": "Backup name", - "backupCreated": "Backup created successfully", - "backupRestored": "Backup restored successfully", - "savingBackup": "Saving Backup", - "categoriesRestored": "Restored %{count} categories", - "categoriesRestoredWithErrors": "Restored %{count} categories (%{failedCount} failed)", - "categoryRestoreFailed": "Failed to restore category: %{categoryName} - %{error}", - "categoryFileNotFound": "Category file not found in backup", - "categoryFileReadFailed": "Failed to read category file: %{error}", - "categoryFileWriteFailed": "Failed to write category file: %{error}", - "createBackup": "Create backup", - "createBackupDesc": "Can be used to restore current library", - "createBackupWarning": "Create backup may not work on devices with Android 9 or lower.", - "downloadingData": "Downloading Data", - "downloadingDownloadedFiles": "Downloading Downloaded Files", - "failed": "Backup failed", - "novelsRestored": "Restored %{count} novels", - "novelsRestoredWithErrors": "Restored %{count} novels (%{failedCount} failed)", - "novelBackupFailed": "Failed to backup novel: %{novelName} - %{error}", - "novelRestoreFailed": "Failed to restore novel: %{novelName} - %{error}", - "novelDirectoryNotFound": "Novel directory not found in backup", - "novelDirectoryReadFailed": "Failed to read novel directory: %{error}", - "restoringCategories": "Restoring Categories", - "restoringNovels": "Restoring Novels", - "restoringSettings": "Restoring Settings", - "settingsRestored": "Settings restored", - "settingsFileNotFound": "Settings file not found in backup", - "settingsRestoreFailed": "Failed to restore settings: %{error}", - "settingsFileWriteFailed": "Failed to write settings file: %{error}", - "versionFileWriteFailed": "Failed to write version file: %{error}", - "drive": { - "backup": "Drive Backup", - "backupInterruped": "Drive Backup Interrupted", - "googleDriveBackup": "Google Drive Backup", - "restore": "Drive Restore", - "restoreInterruped": "Drive Restore Interrupted" - }, - "googeDrive": "Googe Drive", - "googeDriveDesc": "Backup to your Google Drive", - "invalidBackupFolder": "Invalid backup folder", - "localBackup": "Local Backup", - "noBackupFound": "No backup found", - "preparingData": "Preparing Data", - "remote": { - "backup": "Self Host Backup", - "host": "Host", - "unknownHost": "Unknown host" - }, - "remoteBackup": "Remote Backup", - "restoreBackup": "Restore backup", - "restoreBackupDesc": "Restore library from backup file", - "restoreLargeBackupsWarning": "Restoring large backups may freeze the app until restoring is finished", - "restorinBackup": "Restoring backup", - "restoringData": "Restoring Data", - "selfHost": "Self Host", - "selfHostDesc": "Backup to your server", - "uploadingData": "Uploading Data", - "uploadingDownloadedFiles": "Uploading Downloaded Files" - }, - "browse": "Browse", - "browseScreen": { - "addedToLibrary": "Added to library", - "available": "Available", - "deletePluginMessage": "Are you sure you want to uninstall %{name}?", - "discover": "Discover", - "globalSearch": "Global search", - "installFailed": "Installation failed: %{name}", - "installed": "Installed", - "installedPlugin": "Installed %{name}", - "installedPlugins": "Installed plugins", - "lastUsed": "Last used", - "latest": "Latest", - "listEmpty": "Enable languages from settings", - "migration": { - "dialogMessage": "Migrate %{url}?", - "novelAlreadyInLibrary": "Novel already in library", - "selectSource": "Select Source", - "selectSourceDesc": "Select a Source To Migrate From" - }, - "noSource": "Your library does not have any novels from this source", - "pinnedPlugin": "Pinned %{name}", - "pinnedPlugins": "Pinned plugins", - "removeFromLibrary": "Removed from library", - "searchbar": "Search sources", - "searchResults": "Search results", - "selectNovel": "Select Novel", - "uninstalledPlugin": "Uninstalled %{name}", - "unpinnedPlugin": "Unpinned %{name}", - "updateFailed": "Update failed", - "updatedTo": "Updated to %{version}", - "settings": { - "title": "Plugin Settings", - "description": "Fill in the plugin settings. Restart app to apply the settings." - } - }, - "browseSettings": "Browse Settings", - "browseSettingsScreen": { - "concurrentSearches": "Concurrent Source Searches", - "multi": "Multi", - "languages": "Languages" - }, - "categories": { - "addCategories": "Add category", - "cantDeleteDefault": "You cant delete default category", - "default": "Default", - "defaultCategory": "Default category", - "deleteModal": { - "desc": "Do you wish to delete category", - "header": "Delete category" - }, - "duplicateError": "A category with this name already exists!", - "editCategories": "Rename category", - "emptyMsg": "You have no categories. Tap the plus button to create one for organizing your library", - "header": "Edit categories", - "local": "Local", - "setCategories": "Set categories", - "setModalEmptyMsg": "You have no categories. Tap the Edit button to create one for organizing your library" - }, - "repositories": { - "emptyMsg": "You have no repositories. Add your first plugin repository to get started." - }, - "common": { - "about": "About", - "add": "Add", - "all": "All", - "backup": "Backup", - "cancel": "Cancel", - "categories": "Categories", - "chapters": "Chapters", - "clear": "Clear", - "copiedToClipboard": "Copied to clipboard: %{name}", - "delete": "Delete", - "deleted": "Deleted %{name}", - "deprecated": "Deprecated", - "display": "Display", - "done": "Done", - "downloads": "Downloads", - "edit": "Edit", - "example": "Example", - "filter": "Filter", - "globally": "globally", - "install": "Install", - "logout": "Logout", - "name": "Name", - "newUpdateAvailable": "New update available", - "ok": "Ok", - "pause": "Pause", - "preparing": "Preparing", - "remove": "Remove", - "reset": "Reset", - "restore": "Restore", - "resume": "Resume", - "retry": "Retry", - "save": "Save", - "search": "Search", - "searchFor": "Search for", - "searchResults": "Search results", - "settings": "Settings", - "show": "Show", - "signIn": "Sign in", - "signOut": "Sign out", - "sort": "Sort", - "submit": "Submit", - "loading": "Loading ...", - "warning": "Warning" - }, - "webview": { - "refresh": "Refresh", - "share": "Share", - "openInBrowser": "Open in browser", - "clearCookies": "Clear cookies", - "cookiesCleared": "Cookies cleared", - "clearData": "Clear WebView data", - "dataDeleted": "WebView data cleared" - }, - "date": { - "calendar": { - "lastDay": "[Yesterday]", - "lastWeek": "[Last] dddd", - "nextDay": "[Tomorrow]", - "sameDay": "[Today]" - } - }, - "downloadScreen": { - "cancelDownloads": "Cancel downloads", - "cancelled": "Downloads cancelled.", - "chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it", - "chapterName": "Chapter: %{name}", - "completed": "Download completed", - "dbInfo": "Downloads are saved in a SQLite Database.", - "downloading": "Downloading", - "downloadingNovel": "Downloading: %{name}", - "downloadsLower": "downloads", - "noDownloads": "No downloads", - "pluginNotFound": "Plugin not found!", - "removeDownloadsWarning": "Are you sure? All downloaded chapters will be deleted." - }, - "generalSettings": "General", - "generalSettingsScreen": { - "asc": "(Ascending)", - "autoDownload": "Auto-download", - "bySource": "By source", - "chapterSort": "Default chapter sort", - "desc": "(Descending)", - "disableLoadingAnimations": "Disable loading animations", - "disableLoadingAnimationsDesc": "May improve performance on slower devices", - "disableHapticFeedback": "Disable haptic feedback", - "disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.", - "displayMode": "Display Mode", - "downloadNewChapters": "Download new chapters", - "epub": "EPUB", - "epubLocation": "EPUB Location", - "epubLocationDescription": "The place where you open and export your EPUB files.", - "globalUpdate": "Global update", - "gridSize": "Grid size", - "gridSizeDesc": "%{num} per row", - "itemsPerRow": "Items per row", - "itemsPerRowLibrary": "Items per row in library", - "jumpToLastReadChapter": "Jump to last read chapter in list", - "novel": "Novel", - "novelBadges": "Novel Badges", - "novelSort": "Novel Sort", - "refreshMetadata": "Automatically refresh metadata", - "refreshMetadataDescription": "Check for new cover and details when updating library", - "sortOrder": "Sort Order", - "updateLibrary": "Update library on launch", - "updateLibraryDesc": "Not recommended for low devices", - "updateOngoing": "Only update ongoing novels", - "updateTime": "Show last update time", - "useFAB": "Use FAB in Library" - }, - "globalSearch": { - "allSources": "all sources", - "searchIn": "Search a novel in" - }, - "history": "History", - "historyScreen": { - "chapter": "Chapter", - "clearHistorWarning": "Are you sure? All history will be lost.", - "deleted": "History deleted.", - "nothingReadRecently": "Nothing read recently", - "searchbar": "Search history" - }, - "library": "Library", - "libraryScreen": { - "bottomSheet": { - "display": { - "badges": "Badges", - "comfortable": "Comfortable grid", - "compact": "Compact grid", - "displayMode": "Display mode", - "download": "Download", - "downloadBadges": "Download badges", - "list": "List", - "noTitle": "Cover only grid", - "numberOfItems": "Number of Items", - "showNoOfItems": "Show number of items", - "unread": "Unread", - "unreadBadges": "Unread badges" - }, - "filters": { - "completed": "Completed", - "downloaded": "Downloaded", - "started": "Started", - "unread": "Unread" - }, - "sortOrders": { - "alphabetically": "Alphabetically", - "dateAdded": "Date added", - "download": "Downloaded", - "lastRead": "Last read", - "lastUpdated": "Last updated", - "totalChapters": "Total chapters", - "unread": "Unread" - } - }, - "empty": "Your library is empty. Add series to your library from Browse.", - "extraMenu": { - "importEpub": "Import Epub", - "openRandom": "Open Random Entry", - "updateCategory": "Update Category", - "updateLibrary": "Update Library" - }, - "searchbar": "Search library" - }, - "more": "More", - "moreScreen": { - "downloadOnly": "Downloaded only", - "downloadOnlyDesc": "Filters all novels in your library", - "downloadQueue": "Download queue", - "incognitoMode": "Incognito mode", - "incognitoModeDesc": "Pauses reading history" - }, - "novelScreen": { - "addToLibaray": "Add to library", - "bottomSheet": { - "displays": { - "chapterNumber": "Chapter number", - "sourceTitle": "Source title" - }, - "filters": { - "bookmarked": "Bookmarked", - "downloaded": "Downloaded", - "unread": "Unread" - }, - "order": { - "byChapterName": "By chapter name", - "bySource": "By source" - } - }, - "chapterChapnum": "Chapter %{num}", - "chapters": "chapters", - "continueReading": "Continue reading", - "exportEpubModal": { - "applyReaderTheme": "Apply reader theme to EPUB", - "customJSWarning": "Custom JS may not be supported by all EPUB readers", - "downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file", - "endChapter": "End Chapter", - "exportAll": "Export All Chapters", - "includeCustomCSS": "Include Custom CSS", - "includeCustomJS": "Include Custom JS", - "invalidRange": "Please enter valid chapter numbers", - "selectFolder": "Select destination folder for EPUB file", - "startChapter": "Start Chapter", - "startGreaterThanEnd": "Start chapter must be less than or equal to end chapter", - "title": "Export Novel as EPUB" - }, - "epub": { - "exportFailed": "Failed to export EPUB: %{error}", - "exportSuccess": "Successfully exported %{chapters} chapters as EPUB", - "noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.", - "noNovelSelected": "No novel selected for export" - }, - "coverSaved": "Cover saved", - "coverNotSaved": "Cover not saved", - "deleteChapterError": "Cant delete chapter chapter folder", - "deleteMessage": "Delete downloaded chapters?", - "deletedAllDownloads": "Deleted all Downloads", - "download": { - "custom": "Custom", - "customAmount": "Download custom amount", - "delete": "Delete downloads", - "next": "Next chapter", - "next10": "Next 10 chapter", - "next5": "Next 5 chapter", - "unread": "Unread" - }, - "edit": { - "addTag": "Add Tag", - "author": "Author: %{author}", - "cover": "Edit cover", - "info": "Edit info", - "status": "Status:", - "summary": "Description: %{summary}...", - "title": "Title: %{title}" - }, - "inLibaray": "In library", - "jumpToChapterModal": { - "chapterName": "Chapter Name", - "chapterNumber": "Chapter Number", - "error": { - "validChapterName": "Enter a valid chapter name", - "validChapterNumber": "Enter a valid chapter number" - }, - "jumpToChapter": "Jump to Chapter", - "openChapter": "Open Chapter" - }, - "migrate": "Migrate", - "noSummary": "No summary", - "noCoverFound": "No cover found", - "progress": "Progress %{progress} %", - "readChaptersDeleted": "Read chapters deleted", - "startReadingChapters": "Start reading %{name}", - "status": { - "cancelled": "Cancelled", - "completed": "Completed", - "licensed": "Licensed", - "onHiatus": "On Hiatus", - "ongoing": "Ongoing", - "publishingFinished": "Publishing Finished", - "unknown": "Unknown" - }, - "tracked": "Tracked", - "tracking": "Tracking", - "unknownStatus": "Unknown status", - "updatedToast": "Updated %{name}" - }, - "readerScreen": { - "bottomSheet": { - "allowTextSelection": "Text selection", - "autoscroll": "Auto-scroll", - "bionicReading": "Bionic reading", - "tapToScroll": "Tap to scroll", - "color": "Color", - "fontStyle": "Font style", - "fullscreen": "Fullscreen", - "lineHeight": "Line height", - "padding": "Padding", - "pageReader": "Paged reading (Experimental)", - "removeExtraSpacing": "Remove extra spacing", - "scrollAmount": "Scroll amount (screen height by default)", - "showBatteryAndTime": "Battery & time", - "showProgressPercentage": "Reading progress", - "swipeGestures": "Swipe between chapters", - "textAlign": "Text alignment", - "textSize": "Text size", - "useChapterDrawerSwipeNavigation": "Swipe to open drawer", - "verticalSeekbar": "Vertical seekbar", - "keepScreenOn": "Keep screen on", - "volumeButtonsScroll": "Volume button scrolling" - }, - "drawer": { - "scrollToBottom": "Scroll to bottom", - "scrollToCurrentChapter": "Scroll to current chapter", - "scrollToTop": "Scroll to top" - }, - "emptyChapterMessage": "

    Chapter is empty.

    Report on GitHub if it's available in WebView.

    Plugin: %{pluginId}

    Novel: %{novelName}

    Chapter: %{chapterName}

    ", - "finished": "Finished", - "nextChapter": "Next: %{name}", - "noNextChapter": "There's no next chapter", - "noPreviousChapter": "There's no previous chapter" - }, - "readerSettings": { - "autoScrollInterval": "Scroll interval (seconds)", - "autoScrollOffset": "Scroll offset (screen heights)", - "backgroundColor": "Background color", - "backgroundColorModal": "Background color", - "clearCustomCSS": "Reset your custom CSS?", - "clearCustomJS": "Reset your custom JS?", - "cssHint": "Target specific sources using #sourceId-[SOURCEID] in your selectors", - "customCSS": "Custom CSS", - "customJS": "Custom JS", - "deleteCustomTheme": "Delete theme", - "jsHint": "Available variables: html, novelName, chapterName, sourceId, chapterId, novelId", - "navigationControls": "Navigation Controls", - "notSaved": "Not saved", - "openCSSFile": "Import CSS file", - "openJSFile": "Import JS file", - "preset": "Preset", - "readingMode": "Reading Mode", - "readerTheme": "Theme", - "saveCustomTheme": "Save theme", - "textColor": "Text color", - "textColorModal": "Text color", - "title": "Reader", - "verticalSeekbarDesc": "Use vertical seekbar" - }, - "sourceScreen": { - "noResultsFound": "No results found" - }, - "statsScreen": { - "downloadedChapters": "Downloaded chapters", - "genreDistribution": "Genre distribution", - "readChapters": "Read chapters", - "sources": "Sources", - "statusDistribution": "Status distribution", - "title": "Statistics", - "titlesInLibrary": "Titles in library", - "totalChapters": "Total chapters", - "unreadChapters": "Unread chapters" - }, - "tracking": "Tracking", - "trackingScreen": { - "logOutMessage": "Log out from %{name}?", - "revalidate": "Revalidate", - "services": "Services" - }, - "updates": "Updates", - "updatesScreen": { - "deletedChapters": "Deleted %{num} chapters", - "emptyView": "No recent updates", - "lastUpdatedAt": "Library last updated:", - "libraryUpdated": "Library Updated", - "newChapters": "new Chapters", - "novelsUpdated": "%{num} novels updated", - "searchbar": "Search updates", - "unableToGetNovel": "Unable to get novel", - "updatesLower": "updates", - "updatingLibrary": "Updating library" - }, - "onboardingScreen": { - "welcome": "Welcome", - "pickATheme": "Pick a theme", - "light": "Light", - "dark": "Dark", - "system": "System", - "complete": "Complete" - }, - "notifications": { - "IMPORT_EPUB": "Importing EPUB", - "UPDATE_LIBRARY": "Updating Library", - "DRIVE_BACKUP": "Google Drive Backup", - "DRIVE_RESTORE": "Google Drive Restore", - "SELF_HOST_BACKUP": "Self-Host Backup", - "SELF_HOST_RESTORE": "Self-Host Restore", - "LOCAL_BACKUP": "Local Backup", - "LOCAL_RESTORE": "Local Restore", - "MIGRATE_NOVEL": "Migrating Novel", - "DOWNLOAD_CHAPTER": "Downloading Chapter" - } -} diff --git a/strings/languages/no_NO/strings.json b/strings/languages/no_NO/strings.json deleted file mode 100644 index 1896da6be..000000000 --- a/strings/languages/no_NO/strings.json +++ /dev/null @@ -1,574 +0,0 @@ -{ - "aboutScreen": { - "website": "Website", - "discord": "Discord", - "github": "Github", - "helpTranslate": "Help translate", - "plugins": "Plugins", - "version": "Version", - "whatsNew": "What's new" - }, - "advancedSettings": "Advanced", - "advancedSettingsScreen": { - "cachedNovelsDeletedToast": "Cached novels deleted", - "chapterInsertFailed": "chapter insert failed", - "clearCachedNovels": "Clear cached novels", - "clearCachedNovelsDesc": "Delete cached novels which not in your library", - "clearDatabaseWarning": "Read and Downloaded chapters and progress of non-library novels will be lost.", - "clearUpdatesMessage": "Updates cleared.", - "clearUpdatesTab": "Clear updates tab", - "clearUpdatesWarning": "Updates tab will be cleared.", - "clearupdatesTabDesc": "Clears chapter entries in updates tab", - "dataManagement": "Data Management", - "deleteReadChapters": "Delete read chapters", - "deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.", - "importEpub": "Import Epub", - "importNovel": "Import Novel", - "importStaticFiles": "Import Static Files", - "novelInsertFailed": "novel insert failed", - "useFAB": "Use FAB instead of button", - "userAgent": "User Agent", - "recreateDBIndexes": "Recreate DB indexes", - "recreateDBIndexesToast": "Recreated DB indexes", - "recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.", - "recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices." - }, - "appearance": "Appearance", - "appearanceScreen": { - "accentColor": "Accent Color", - "alwaysShowNavLabels": "Always show nav labels", - "appLanguage": "App language", - "languagePickerModal": { - "title": "Select Language", - "restartNote": "You will need to restart the app for the language change to take full effect." - }, - "appLanguageDefault": "Default", - "appTheme": "App theme", - "darkTheme": "Dark Theme", - "hideBackdrop": "Hide backdrop", - "lightTheme": "Light Theme", - "navbar": "Navbar", - "novelInfo": "Novel info", - "pureBlackDarkMode": "Pure black dark mode", - "showHistoryInTheNav": "Show history in the nav", - "showUpdatesInTheNav": "Show updates in the nav", - "themeMode": "Theme mode", - "themeModeLight": "Light", - "themeModeDark": "Dark", - "themeModeSystem": "System", - "theme": { - "default": "Default", - "lavender": "Lavender", - "midnightDusk": "Midnight Dusk", - "daybreakBloom": "Daybreak Bloom", - "strawberry": "Strawberry Daiquiri", - "tako": "Tako", - "teal": "Teal", - "turquoise": "Turquoise", - "yotsuba": "Yotsuba", - "catppuccin": "Catppuccin", - "yinyang": "Yin & Yang" - } - }, - "backupScreen": { - "backupName": "Backup name", - "backupCreated": "Backup created successfully", - "backupRestored": "Backup restored successfully", - "savingBackup": "Saving Backup", - "categoriesRestored": "Restored %{count} categories", - "categoriesRestoredWithErrors": "Restored %{count} categories (%{failedCount} failed)", - "categoryRestoreFailed": "Failed to restore category: %{categoryName} - %{error}", - "categoryFileNotFound": "Category file not found in backup", - "categoryFileReadFailed": "Failed to read category file: %{error}", - "categoryFileWriteFailed": "Failed to write category file: %{error}", - "createBackup": "Create backup", - "createBackupDesc": "Can be used to restore current library", - "createBackupWarning": "Create backup may not work on devices with Android 9 or lower.", - "downloadingData": "Downloading Data", - "downloadingDownloadedFiles": "Downloading Downloaded Files", - "failed": "Backup failed", - "novelsRestored": "Restored %{count} novels", - "novelsRestoredWithErrors": "Restored %{count} novels (%{failedCount} failed)", - "novelBackupFailed": "Failed to backup novel: %{novelName} - %{error}", - "novelRestoreFailed": "Failed to restore novel: %{novelName} - %{error}", - "novelDirectoryNotFound": "Novel directory not found in backup", - "novelDirectoryReadFailed": "Failed to read novel directory: %{error}", - "restoringCategories": "Restoring Categories", - "restoringNovels": "Restoring Novels", - "restoringSettings": "Restoring Settings", - "settingsRestored": "Settings restored", - "settingsFileNotFound": "Settings file not found in backup", - "settingsRestoreFailed": "Failed to restore settings: %{error}", - "settingsFileWriteFailed": "Failed to write settings file: %{error}", - "versionFileWriteFailed": "Failed to write version file: %{error}", - "drive": { - "backup": "Drive Backup", - "backupInterruped": "Drive Backup Interrupted", - "googleDriveBackup": "Google Drive Backup", - "restore": "Drive Restore", - "restoreInterruped": "Drive Restore Interrupted" - }, - "googeDrive": "Googe Drive", - "googeDriveDesc": "Backup to your Google Drive", - "invalidBackupFolder": "Invalid backup folder", - "localBackup": "Local Backup", - "noBackupFound": "No backup found", - "preparingData": "Preparing Data", - "remote": { - "backup": "Self Host Backup", - "host": "Host", - "unknownHost": "Unknown host" - }, - "remoteBackup": "Remote Backup", - "restoreBackup": "Restore backup", - "restoreBackupDesc": "Restore library from backup file", - "restoreLargeBackupsWarning": "Restoring large backups may freeze the app until restoring is finished", - "restorinBackup": "Restoring backup", - "restoringData": "Restoring Data", - "selfHost": "Self Host", - "selfHostDesc": "Backup to your server", - "uploadingData": "Uploading Data", - "uploadingDownloadedFiles": "Uploading Downloaded Files" - }, - "browse": "Browse", - "browseScreen": { - "addedToLibrary": "Added to library", - "available": "Available", - "deletePluginMessage": "Are you sure you want to uninstall %{name}?", - "discover": "Discover", - "globalSearch": "Global search", - "installFailed": "Installation failed: %{name}", - "installed": "Installed", - "installedPlugin": "Installed %{name}", - "installedPlugins": "Installed plugins", - "lastUsed": "Last used", - "latest": "Latest", - "listEmpty": "Enable languages from settings", - "migration": { - "dialogMessage": "Migrate %{url}?", - "novelAlreadyInLibrary": "Novel already in library", - "selectSource": "Select Source", - "selectSourceDesc": "Select a Source To Migrate From" - }, - "noSource": "Your library does not have any novels from this source", - "pinnedPlugin": "Pinned %{name}", - "pinnedPlugins": "Pinned plugins", - "removeFromLibrary": "Removed from library", - "searchbar": "Search sources", - "searchResults": "Search results", - "selectNovel": "Select Novel", - "uninstalledPlugin": "Uninstalled %{name}", - "unpinnedPlugin": "Unpinned %{name}", - "updateFailed": "Update failed", - "updatedTo": "Updated to %{version}", - "settings": { - "title": "Plugin Settings", - "description": "Fill in the plugin settings. Restart app to apply the settings." - } - }, - "browseSettings": "Browse Settings", - "browseSettingsScreen": { - "concurrentSearches": "Concurrent Source Searches", - "multi": "Multi", - "languages": "Languages" - }, - "categories": { - "addCategories": "Add category", - "cantDeleteDefault": "You cant delete default category", - "default": "Default", - "defaultCategory": "Default category", - "deleteModal": { - "desc": "Do you wish to delete category", - "header": "Delete category" - }, - "duplicateError": "A category with this name already exists!", - "editCategories": "Rename category", - "emptyMsg": "You have no categories. Tap the plus button to create one for organizing your library", - "header": "Edit categories", - "local": "Local", - "setCategories": "Set categories", - "setModalEmptyMsg": "You have no categories. Tap the Edit button to create one for organizing your library" - }, - "repositories": { - "emptyMsg": "You have no repositories. Add your first plugin repository to get started." - }, - "common": { - "about": "About", - "add": "Add", - "all": "All", - "backup": "Backup", - "cancel": "Cancel", - "categories": "Categories", - "chapters": "Chapters", - "clear": "Clear", - "copiedToClipboard": "Copied to clipboard: %{name}", - "delete": "Delete", - "deleted": "Deleted %{name}", - "deprecated": "Deprecated", - "display": "Display", - "done": "Done", - "downloads": "Downloads", - "edit": "Edit", - "example": "Example", - "filter": "Filter", - "globally": "globally", - "install": "Install", - "logout": "Logout", - "name": "Name", - "newUpdateAvailable": "New update available", - "ok": "Ok", - "pause": "Pause", - "preparing": "Preparing", - "remove": "Remove", - "reset": "Reset", - "restore": "Restore", - "resume": "Resume", - "retry": "Retry", - "save": "Save", - "search": "Search", - "searchFor": "Search for", - "searchResults": "Search results", - "settings": "Settings", - "show": "Show", - "signIn": "Sign in", - "signOut": "Sign out", - "sort": "Sort", - "submit": "Submit", - "loading": "Loading ...", - "warning": "Warning" - }, - "webview": { - "refresh": "Refresh", - "share": "Share", - "openInBrowser": "Open in browser", - "clearCookies": "Clear cookies", - "cookiesCleared": "Cookies cleared", - "clearData": "Clear WebView data", - "dataDeleted": "WebView data cleared" - }, - "date": { - "calendar": { - "lastDay": "[Yesterday]", - "lastWeek": "[Last] dddd", - "nextDay": "[Tomorrow]", - "sameDay": "[Today]" - } - }, - "downloadScreen": { - "cancelDownloads": "Cancel downloads", - "cancelled": "Downloads cancelled.", - "chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it", - "chapterName": "Chapter: %{name}", - "completed": "Download completed", - "dbInfo": "Downloads are saved in a SQLite Database.", - "downloading": "Downloading", - "downloadingNovel": "Downloading: %{name}", - "downloadsLower": "downloads", - "noDownloads": "No downloads", - "pluginNotFound": "Plugin not found!", - "removeDownloadsWarning": "Are you sure? All downloaded chapters will be deleted." - }, - "generalSettings": "General", - "generalSettingsScreen": { - "asc": "(Ascending)", - "autoDownload": "Auto-download", - "bySource": "By source", - "chapterSort": "Default chapter sort", - "desc": "(Descending)", - "disableLoadingAnimations": "Disable loading animations", - "disableLoadingAnimationsDesc": "May improve performance on slower devices", - "disableHapticFeedback": "Disable haptic feedback", - "disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.", - "displayMode": "Display Mode", - "downloadNewChapters": "Download new chapters", - "epub": "EPUB", - "epubLocation": "EPUB Location", - "epubLocationDescription": "The place where you open and export your EPUB files.", - "globalUpdate": "Global update", - "gridSize": "Grid size", - "gridSizeDesc": "%{num} per row", - "itemsPerRow": "Items per row", - "itemsPerRowLibrary": "Items per row in library", - "jumpToLastReadChapter": "Jump to last read chapter in list", - "novel": "Novel", - "novelBadges": "Novel Badges", - "novelSort": "Novel Sort", - "refreshMetadata": "Automatically refresh metadata", - "refreshMetadataDescription": "Check for new cover and details when updating library", - "sortOrder": "Sort Order", - "updateLibrary": "Update library on launch", - "updateLibraryDesc": "Not recommended for low devices", - "updateOngoing": "Only update ongoing novels", - "updateTime": "Show last update time", - "useFAB": "Use FAB in Library" - }, - "globalSearch": { - "allSources": "all sources", - "searchIn": "Search a novel in" - }, - "history": "History", - "historyScreen": { - "chapter": "Chapter", - "clearHistorWarning": "Are you sure? All history will be lost.", - "deleted": "History deleted.", - "nothingReadRecently": "Nothing read recently", - "searchbar": "Search history" - }, - "library": "Library", - "libraryScreen": { - "bottomSheet": { - "display": { - "badges": "Badges", - "comfortable": "Comfortable grid", - "compact": "Compact grid", - "displayMode": "Display mode", - "download": "Download", - "downloadBadges": "Download badges", - "list": "List", - "noTitle": "Cover only grid", - "numberOfItems": "Number of Items", - "showNoOfItems": "Show number of items", - "unread": "Unread", - "unreadBadges": "Unread badges" - }, - "filters": { - "completed": "Completed", - "downloaded": "Downloaded", - "started": "Started", - "unread": "Unread" - }, - "sortOrders": { - "alphabetically": "Alphabetically", - "dateAdded": "Date added", - "download": "Downloaded", - "lastRead": "Last read", - "lastUpdated": "Last updated", - "totalChapters": "Total chapters", - "unread": "Unread" - } - }, - "empty": "Your library is empty. Add series to your library from Browse.", - "extraMenu": { - "importEpub": "Import Epub", - "openRandom": "Open Random Entry", - "updateCategory": "Update Category", - "updateLibrary": "Update Library" - }, - "searchbar": "Search library" - }, - "more": "More", - "moreScreen": { - "downloadOnly": "Downloaded only", - "downloadOnlyDesc": "Filters all novels in your library", - "downloadQueue": "Download queue", - "incognitoMode": "Incognito mode", - "incognitoModeDesc": "Pauses reading history" - }, - "novelScreen": { - "addToLibaray": "Add to library", - "bottomSheet": { - "displays": { - "chapterNumber": "Chapter number", - "sourceTitle": "Source title" - }, - "filters": { - "bookmarked": "Bookmarked", - "downloaded": "Downloaded", - "unread": "Unread" - }, - "order": { - "byChapterName": "By chapter name", - "bySource": "By source" - } - }, - "chapterChapnum": "Chapter %{num}", - "chapters": "chapters", - "continueReading": "Continue reading", - "exportEpubModal": { - "applyReaderTheme": "Apply reader theme to EPUB", - "customJSWarning": "Custom JS may not be supported by all EPUB readers", - "downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file", - "endChapter": "End Chapter", - "exportAll": "Export All Chapters", - "includeCustomCSS": "Include Custom CSS", - "includeCustomJS": "Include Custom JS", - "invalidRange": "Please enter valid chapter numbers", - "selectFolder": "Select destination folder for EPUB file", - "startChapter": "Start Chapter", - "startGreaterThanEnd": "Start chapter must be less than or equal to end chapter", - "title": "Export Novel as EPUB" - }, - "epub": { - "exportFailed": "Failed to export EPUB: %{error}", - "exportSuccess": "Successfully exported %{chapters} chapters as EPUB", - "noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.", - "noNovelSelected": "No novel selected for export" - }, - "coverSaved": "Cover saved", - "coverNotSaved": "Cover not saved", - "deleteChapterError": "Cant delete chapter chapter folder", - "deleteMessage": "Delete downloaded chapters?", - "deletedAllDownloads": "Deleted all Downloads", - "download": { - "custom": "Custom", - "customAmount": "Download custom amount", - "delete": "Delete downloads", - "next": "Next chapter", - "next10": "Next 10 chapter", - "next5": "Next 5 chapter", - "unread": "Unread" - }, - "edit": { - "addTag": "Add Tag", - "author": "Author: %{author}", - "cover": "Edit cover", - "info": "Edit info", - "status": "Status:", - "summary": "Description: %{summary}...", - "title": "Title: %{title}" - }, - "inLibaray": "In library", - "jumpToChapterModal": { - "chapterName": "Chapter Name", - "chapterNumber": "Chapter Number", - "error": { - "validChapterName": "Enter a valid chapter name", - "validChapterNumber": "Enter a valid chapter number" - }, - "jumpToChapter": "Jump to Chapter", - "openChapter": "Open Chapter" - }, - "migrate": "Migrate", - "noSummary": "No summary", - "noCoverFound": "No cover found", - "progress": "Progress %{progress} %", - "readChaptersDeleted": "Read chapters deleted", - "startReadingChapters": "Start reading %{name}", - "status": { - "cancelled": "Cancelled", - "completed": "Completed", - "licensed": "Licensed", - "onHiatus": "On Hiatus", - "ongoing": "Ongoing", - "publishingFinished": "Publishing Finished", - "unknown": "Unknown" - }, - "tracked": "Tracked", - "tracking": "Tracking", - "unknownStatus": "Unknown status", - "updatedToast": "Updated %{name}" - }, - "readerScreen": { - "bottomSheet": { - "allowTextSelection": "Text selection", - "autoscroll": "Auto-scroll", - "bionicReading": "Bionic reading", - "tapToScroll": "Tap to scroll", - "color": "Color", - "fontStyle": "Font style", - "fullscreen": "Fullscreen", - "lineHeight": "Line height", - "padding": "Padding", - "pageReader": "Paged reading (Experimental)", - "removeExtraSpacing": "Remove extra spacing", - "scrollAmount": "Scroll amount (screen height by default)", - "showBatteryAndTime": "Battery & time", - "showProgressPercentage": "Reading progress", - "swipeGestures": "Swipe between chapters", - "textAlign": "Text alignment", - "textSize": "Text size", - "useChapterDrawerSwipeNavigation": "Swipe to open drawer", - "verticalSeekbar": "Vertical seekbar", - "keepScreenOn": "Keep screen on", - "volumeButtonsScroll": "Volume button scrolling" - }, - "drawer": { - "scrollToBottom": "Scroll to bottom", - "scrollToCurrentChapter": "Scroll to current chapter", - "scrollToTop": "Scroll to top" - }, - "emptyChapterMessage": "

    Chapter is empty.

    Report on GitHub if it's available in WebView.

    Plugin: %{pluginId}

    Novel: %{novelName}

    Chapter: %{chapterName}

    ", - "finished": "Finished", - "nextChapter": "Next: %{name}", - "noNextChapter": "There's no next chapter", - "noPreviousChapter": "There's no previous chapter" - }, - "readerSettings": { - "autoScrollInterval": "Scroll interval (seconds)", - "autoScrollOffset": "Scroll offset (screen heights)", - "backgroundColor": "Background color", - "backgroundColorModal": "Background color", - "clearCustomCSS": "Reset your custom CSS?", - "clearCustomJS": "Reset your custom JS?", - "cssHint": "Target specific sources using #sourceId-[SOURCEID] in your selectors", - "customCSS": "Custom CSS", - "customJS": "Custom JS", - "deleteCustomTheme": "Delete theme", - "jsHint": "Available variables: html, novelName, chapterName, sourceId, chapterId, novelId", - "navigationControls": "Navigation Controls", - "notSaved": "Not saved", - "openCSSFile": "Import CSS file", - "openJSFile": "Import JS file", - "preset": "Preset", - "readingMode": "Reading Mode", - "readerTheme": "Theme", - "saveCustomTheme": "Save theme", - "textColor": "Text color", - "textColorModal": "Text color", - "title": "Reader", - "verticalSeekbarDesc": "Use vertical seekbar" - }, - "sourceScreen": { - "noResultsFound": "No results found" - }, - "statsScreen": { - "downloadedChapters": "Downloaded chapters", - "genreDistribution": "Genre distribution", - "readChapters": "Read chapters", - "sources": "Sources", - "statusDistribution": "Status distribution", - "title": "Statistics", - "titlesInLibrary": "Titles in library", - "totalChapters": "Total chapters", - "unreadChapters": "Unread chapters" - }, - "tracking": "Tracking", - "trackingScreen": { - "logOutMessage": "Log out from %{name}?", - "revalidate": "Revalidate", - "services": "Services" - }, - "updates": "Updates", - "updatesScreen": { - "deletedChapters": "Deleted %{num} chapters", - "emptyView": "No recent updates", - "lastUpdatedAt": "Library last updated:", - "libraryUpdated": "Library Updated", - "newChapters": "new Chapters", - "novelsUpdated": "%{num} novels updated", - "searchbar": "Search updates", - "unableToGetNovel": "Unable to get novel", - "updatesLower": "updates", - "updatingLibrary": "Updating library" - }, - "onboardingScreen": { - "welcome": "Welcome", - "pickATheme": "Pick a theme", - "light": "Light", - "dark": "Dark", - "system": "System", - "complete": "Complete" - }, - "notifications": { - "IMPORT_EPUB": "Importing EPUB", - "UPDATE_LIBRARY": "Updating Library", - "DRIVE_BACKUP": "Google Drive Backup", - "DRIVE_RESTORE": "Google Drive Restore", - "SELF_HOST_BACKUP": "Self-Host Backup", - "SELF_HOST_RESTORE": "Self-Host Restore", - "LOCAL_BACKUP": "Local Backup", - "LOCAL_RESTORE": "Local Restore", - "MIGRATE_NOVEL": "Migrating Novel", - "DOWNLOAD_CHAPTER": "Downloading Chapter" - } -} diff --git a/strings/languages/pt_PT/strings.json b/strings/languages/pt_PT/strings.json deleted file mode 100644 index 1896da6be..000000000 --- a/strings/languages/pt_PT/strings.json +++ /dev/null @@ -1,574 +0,0 @@ -{ - "aboutScreen": { - "website": "Website", - "discord": "Discord", - "github": "Github", - "helpTranslate": "Help translate", - "plugins": "Plugins", - "version": "Version", - "whatsNew": "What's new" - }, - "advancedSettings": "Advanced", - "advancedSettingsScreen": { - "cachedNovelsDeletedToast": "Cached novels deleted", - "chapterInsertFailed": "chapter insert failed", - "clearCachedNovels": "Clear cached novels", - "clearCachedNovelsDesc": "Delete cached novels which not in your library", - "clearDatabaseWarning": "Read and Downloaded chapters and progress of non-library novels will be lost.", - "clearUpdatesMessage": "Updates cleared.", - "clearUpdatesTab": "Clear updates tab", - "clearUpdatesWarning": "Updates tab will be cleared.", - "clearupdatesTabDesc": "Clears chapter entries in updates tab", - "dataManagement": "Data Management", - "deleteReadChapters": "Delete read chapters", - "deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.", - "importEpub": "Import Epub", - "importNovel": "Import Novel", - "importStaticFiles": "Import Static Files", - "novelInsertFailed": "novel insert failed", - "useFAB": "Use FAB instead of button", - "userAgent": "User Agent", - "recreateDBIndexes": "Recreate DB indexes", - "recreateDBIndexesToast": "Recreated DB indexes", - "recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.", - "recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices." - }, - "appearance": "Appearance", - "appearanceScreen": { - "accentColor": "Accent Color", - "alwaysShowNavLabels": "Always show nav labels", - "appLanguage": "App language", - "languagePickerModal": { - "title": "Select Language", - "restartNote": "You will need to restart the app for the language change to take full effect." - }, - "appLanguageDefault": "Default", - "appTheme": "App theme", - "darkTheme": "Dark Theme", - "hideBackdrop": "Hide backdrop", - "lightTheme": "Light Theme", - "navbar": "Navbar", - "novelInfo": "Novel info", - "pureBlackDarkMode": "Pure black dark mode", - "showHistoryInTheNav": "Show history in the nav", - "showUpdatesInTheNav": "Show updates in the nav", - "themeMode": "Theme mode", - "themeModeLight": "Light", - "themeModeDark": "Dark", - "themeModeSystem": "System", - "theme": { - "default": "Default", - "lavender": "Lavender", - "midnightDusk": "Midnight Dusk", - "daybreakBloom": "Daybreak Bloom", - "strawberry": "Strawberry Daiquiri", - "tako": "Tako", - "teal": "Teal", - "turquoise": "Turquoise", - "yotsuba": "Yotsuba", - "catppuccin": "Catppuccin", - "yinyang": "Yin & Yang" - } - }, - "backupScreen": { - "backupName": "Backup name", - "backupCreated": "Backup created successfully", - "backupRestored": "Backup restored successfully", - "savingBackup": "Saving Backup", - "categoriesRestored": "Restored %{count} categories", - "categoriesRestoredWithErrors": "Restored %{count} categories (%{failedCount} failed)", - "categoryRestoreFailed": "Failed to restore category: %{categoryName} - %{error}", - "categoryFileNotFound": "Category file not found in backup", - "categoryFileReadFailed": "Failed to read category file: %{error}", - "categoryFileWriteFailed": "Failed to write category file: %{error}", - "createBackup": "Create backup", - "createBackupDesc": "Can be used to restore current library", - "createBackupWarning": "Create backup may not work on devices with Android 9 or lower.", - "downloadingData": "Downloading Data", - "downloadingDownloadedFiles": "Downloading Downloaded Files", - "failed": "Backup failed", - "novelsRestored": "Restored %{count} novels", - "novelsRestoredWithErrors": "Restored %{count} novels (%{failedCount} failed)", - "novelBackupFailed": "Failed to backup novel: %{novelName} - %{error}", - "novelRestoreFailed": "Failed to restore novel: %{novelName} - %{error}", - "novelDirectoryNotFound": "Novel directory not found in backup", - "novelDirectoryReadFailed": "Failed to read novel directory: %{error}", - "restoringCategories": "Restoring Categories", - "restoringNovels": "Restoring Novels", - "restoringSettings": "Restoring Settings", - "settingsRestored": "Settings restored", - "settingsFileNotFound": "Settings file not found in backup", - "settingsRestoreFailed": "Failed to restore settings: %{error}", - "settingsFileWriteFailed": "Failed to write settings file: %{error}", - "versionFileWriteFailed": "Failed to write version file: %{error}", - "drive": { - "backup": "Drive Backup", - "backupInterruped": "Drive Backup Interrupted", - "googleDriveBackup": "Google Drive Backup", - "restore": "Drive Restore", - "restoreInterruped": "Drive Restore Interrupted" - }, - "googeDrive": "Googe Drive", - "googeDriveDesc": "Backup to your Google Drive", - "invalidBackupFolder": "Invalid backup folder", - "localBackup": "Local Backup", - "noBackupFound": "No backup found", - "preparingData": "Preparing Data", - "remote": { - "backup": "Self Host Backup", - "host": "Host", - "unknownHost": "Unknown host" - }, - "remoteBackup": "Remote Backup", - "restoreBackup": "Restore backup", - "restoreBackupDesc": "Restore library from backup file", - "restoreLargeBackupsWarning": "Restoring large backups may freeze the app until restoring is finished", - "restorinBackup": "Restoring backup", - "restoringData": "Restoring Data", - "selfHost": "Self Host", - "selfHostDesc": "Backup to your server", - "uploadingData": "Uploading Data", - "uploadingDownloadedFiles": "Uploading Downloaded Files" - }, - "browse": "Browse", - "browseScreen": { - "addedToLibrary": "Added to library", - "available": "Available", - "deletePluginMessage": "Are you sure you want to uninstall %{name}?", - "discover": "Discover", - "globalSearch": "Global search", - "installFailed": "Installation failed: %{name}", - "installed": "Installed", - "installedPlugin": "Installed %{name}", - "installedPlugins": "Installed plugins", - "lastUsed": "Last used", - "latest": "Latest", - "listEmpty": "Enable languages from settings", - "migration": { - "dialogMessage": "Migrate %{url}?", - "novelAlreadyInLibrary": "Novel already in library", - "selectSource": "Select Source", - "selectSourceDesc": "Select a Source To Migrate From" - }, - "noSource": "Your library does not have any novels from this source", - "pinnedPlugin": "Pinned %{name}", - "pinnedPlugins": "Pinned plugins", - "removeFromLibrary": "Removed from library", - "searchbar": "Search sources", - "searchResults": "Search results", - "selectNovel": "Select Novel", - "uninstalledPlugin": "Uninstalled %{name}", - "unpinnedPlugin": "Unpinned %{name}", - "updateFailed": "Update failed", - "updatedTo": "Updated to %{version}", - "settings": { - "title": "Plugin Settings", - "description": "Fill in the plugin settings. Restart app to apply the settings." - } - }, - "browseSettings": "Browse Settings", - "browseSettingsScreen": { - "concurrentSearches": "Concurrent Source Searches", - "multi": "Multi", - "languages": "Languages" - }, - "categories": { - "addCategories": "Add category", - "cantDeleteDefault": "You cant delete default category", - "default": "Default", - "defaultCategory": "Default category", - "deleteModal": { - "desc": "Do you wish to delete category", - "header": "Delete category" - }, - "duplicateError": "A category with this name already exists!", - "editCategories": "Rename category", - "emptyMsg": "You have no categories. Tap the plus button to create one for organizing your library", - "header": "Edit categories", - "local": "Local", - "setCategories": "Set categories", - "setModalEmptyMsg": "You have no categories. Tap the Edit button to create one for organizing your library" - }, - "repositories": { - "emptyMsg": "You have no repositories. Add your first plugin repository to get started." - }, - "common": { - "about": "About", - "add": "Add", - "all": "All", - "backup": "Backup", - "cancel": "Cancel", - "categories": "Categories", - "chapters": "Chapters", - "clear": "Clear", - "copiedToClipboard": "Copied to clipboard: %{name}", - "delete": "Delete", - "deleted": "Deleted %{name}", - "deprecated": "Deprecated", - "display": "Display", - "done": "Done", - "downloads": "Downloads", - "edit": "Edit", - "example": "Example", - "filter": "Filter", - "globally": "globally", - "install": "Install", - "logout": "Logout", - "name": "Name", - "newUpdateAvailable": "New update available", - "ok": "Ok", - "pause": "Pause", - "preparing": "Preparing", - "remove": "Remove", - "reset": "Reset", - "restore": "Restore", - "resume": "Resume", - "retry": "Retry", - "save": "Save", - "search": "Search", - "searchFor": "Search for", - "searchResults": "Search results", - "settings": "Settings", - "show": "Show", - "signIn": "Sign in", - "signOut": "Sign out", - "sort": "Sort", - "submit": "Submit", - "loading": "Loading ...", - "warning": "Warning" - }, - "webview": { - "refresh": "Refresh", - "share": "Share", - "openInBrowser": "Open in browser", - "clearCookies": "Clear cookies", - "cookiesCleared": "Cookies cleared", - "clearData": "Clear WebView data", - "dataDeleted": "WebView data cleared" - }, - "date": { - "calendar": { - "lastDay": "[Yesterday]", - "lastWeek": "[Last] dddd", - "nextDay": "[Tomorrow]", - "sameDay": "[Today]" - } - }, - "downloadScreen": { - "cancelDownloads": "Cancel downloads", - "cancelled": "Downloads cancelled.", - "chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it", - "chapterName": "Chapter: %{name}", - "completed": "Download completed", - "dbInfo": "Downloads are saved in a SQLite Database.", - "downloading": "Downloading", - "downloadingNovel": "Downloading: %{name}", - "downloadsLower": "downloads", - "noDownloads": "No downloads", - "pluginNotFound": "Plugin not found!", - "removeDownloadsWarning": "Are you sure? All downloaded chapters will be deleted." - }, - "generalSettings": "General", - "generalSettingsScreen": { - "asc": "(Ascending)", - "autoDownload": "Auto-download", - "bySource": "By source", - "chapterSort": "Default chapter sort", - "desc": "(Descending)", - "disableLoadingAnimations": "Disable loading animations", - "disableLoadingAnimationsDesc": "May improve performance on slower devices", - "disableHapticFeedback": "Disable haptic feedback", - "disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.", - "displayMode": "Display Mode", - "downloadNewChapters": "Download new chapters", - "epub": "EPUB", - "epubLocation": "EPUB Location", - "epubLocationDescription": "The place where you open and export your EPUB files.", - "globalUpdate": "Global update", - "gridSize": "Grid size", - "gridSizeDesc": "%{num} per row", - "itemsPerRow": "Items per row", - "itemsPerRowLibrary": "Items per row in library", - "jumpToLastReadChapter": "Jump to last read chapter in list", - "novel": "Novel", - "novelBadges": "Novel Badges", - "novelSort": "Novel Sort", - "refreshMetadata": "Automatically refresh metadata", - "refreshMetadataDescription": "Check for new cover and details when updating library", - "sortOrder": "Sort Order", - "updateLibrary": "Update library on launch", - "updateLibraryDesc": "Not recommended for low devices", - "updateOngoing": "Only update ongoing novels", - "updateTime": "Show last update time", - "useFAB": "Use FAB in Library" - }, - "globalSearch": { - "allSources": "all sources", - "searchIn": "Search a novel in" - }, - "history": "History", - "historyScreen": { - "chapter": "Chapter", - "clearHistorWarning": "Are you sure? All history will be lost.", - "deleted": "History deleted.", - "nothingReadRecently": "Nothing read recently", - "searchbar": "Search history" - }, - "library": "Library", - "libraryScreen": { - "bottomSheet": { - "display": { - "badges": "Badges", - "comfortable": "Comfortable grid", - "compact": "Compact grid", - "displayMode": "Display mode", - "download": "Download", - "downloadBadges": "Download badges", - "list": "List", - "noTitle": "Cover only grid", - "numberOfItems": "Number of Items", - "showNoOfItems": "Show number of items", - "unread": "Unread", - "unreadBadges": "Unread badges" - }, - "filters": { - "completed": "Completed", - "downloaded": "Downloaded", - "started": "Started", - "unread": "Unread" - }, - "sortOrders": { - "alphabetically": "Alphabetically", - "dateAdded": "Date added", - "download": "Downloaded", - "lastRead": "Last read", - "lastUpdated": "Last updated", - "totalChapters": "Total chapters", - "unread": "Unread" - } - }, - "empty": "Your library is empty. Add series to your library from Browse.", - "extraMenu": { - "importEpub": "Import Epub", - "openRandom": "Open Random Entry", - "updateCategory": "Update Category", - "updateLibrary": "Update Library" - }, - "searchbar": "Search library" - }, - "more": "More", - "moreScreen": { - "downloadOnly": "Downloaded only", - "downloadOnlyDesc": "Filters all novels in your library", - "downloadQueue": "Download queue", - "incognitoMode": "Incognito mode", - "incognitoModeDesc": "Pauses reading history" - }, - "novelScreen": { - "addToLibaray": "Add to library", - "bottomSheet": { - "displays": { - "chapterNumber": "Chapter number", - "sourceTitle": "Source title" - }, - "filters": { - "bookmarked": "Bookmarked", - "downloaded": "Downloaded", - "unread": "Unread" - }, - "order": { - "byChapterName": "By chapter name", - "bySource": "By source" - } - }, - "chapterChapnum": "Chapter %{num}", - "chapters": "chapters", - "continueReading": "Continue reading", - "exportEpubModal": { - "applyReaderTheme": "Apply reader theme to EPUB", - "customJSWarning": "Custom JS may not be supported by all EPUB readers", - "downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file", - "endChapter": "End Chapter", - "exportAll": "Export All Chapters", - "includeCustomCSS": "Include Custom CSS", - "includeCustomJS": "Include Custom JS", - "invalidRange": "Please enter valid chapter numbers", - "selectFolder": "Select destination folder for EPUB file", - "startChapter": "Start Chapter", - "startGreaterThanEnd": "Start chapter must be less than or equal to end chapter", - "title": "Export Novel as EPUB" - }, - "epub": { - "exportFailed": "Failed to export EPUB: %{error}", - "exportSuccess": "Successfully exported %{chapters} chapters as EPUB", - "noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.", - "noNovelSelected": "No novel selected for export" - }, - "coverSaved": "Cover saved", - "coverNotSaved": "Cover not saved", - "deleteChapterError": "Cant delete chapter chapter folder", - "deleteMessage": "Delete downloaded chapters?", - "deletedAllDownloads": "Deleted all Downloads", - "download": { - "custom": "Custom", - "customAmount": "Download custom amount", - "delete": "Delete downloads", - "next": "Next chapter", - "next10": "Next 10 chapter", - "next5": "Next 5 chapter", - "unread": "Unread" - }, - "edit": { - "addTag": "Add Tag", - "author": "Author: %{author}", - "cover": "Edit cover", - "info": "Edit info", - "status": "Status:", - "summary": "Description: %{summary}...", - "title": "Title: %{title}" - }, - "inLibaray": "In library", - "jumpToChapterModal": { - "chapterName": "Chapter Name", - "chapterNumber": "Chapter Number", - "error": { - "validChapterName": "Enter a valid chapter name", - "validChapterNumber": "Enter a valid chapter number" - }, - "jumpToChapter": "Jump to Chapter", - "openChapter": "Open Chapter" - }, - "migrate": "Migrate", - "noSummary": "No summary", - "noCoverFound": "No cover found", - "progress": "Progress %{progress} %", - "readChaptersDeleted": "Read chapters deleted", - "startReadingChapters": "Start reading %{name}", - "status": { - "cancelled": "Cancelled", - "completed": "Completed", - "licensed": "Licensed", - "onHiatus": "On Hiatus", - "ongoing": "Ongoing", - "publishingFinished": "Publishing Finished", - "unknown": "Unknown" - }, - "tracked": "Tracked", - "tracking": "Tracking", - "unknownStatus": "Unknown status", - "updatedToast": "Updated %{name}" - }, - "readerScreen": { - "bottomSheet": { - "allowTextSelection": "Text selection", - "autoscroll": "Auto-scroll", - "bionicReading": "Bionic reading", - "tapToScroll": "Tap to scroll", - "color": "Color", - "fontStyle": "Font style", - "fullscreen": "Fullscreen", - "lineHeight": "Line height", - "padding": "Padding", - "pageReader": "Paged reading (Experimental)", - "removeExtraSpacing": "Remove extra spacing", - "scrollAmount": "Scroll amount (screen height by default)", - "showBatteryAndTime": "Battery & time", - "showProgressPercentage": "Reading progress", - "swipeGestures": "Swipe between chapters", - "textAlign": "Text alignment", - "textSize": "Text size", - "useChapterDrawerSwipeNavigation": "Swipe to open drawer", - "verticalSeekbar": "Vertical seekbar", - "keepScreenOn": "Keep screen on", - "volumeButtonsScroll": "Volume button scrolling" - }, - "drawer": { - "scrollToBottom": "Scroll to bottom", - "scrollToCurrentChapter": "Scroll to current chapter", - "scrollToTop": "Scroll to top" - }, - "emptyChapterMessage": "

    Chapter is empty.

    Report on GitHub if it's available in WebView.

    Plugin: %{pluginId}

    Novel: %{novelName}

    Chapter: %{chapterName}

    ", - "finished": "Finished", - "nextChapter": "Next: %{name}", - "noNextChapter": "There's no next chapter", - "noPreviousChapter": "There's no previous chapter" - }, - "readerSettings": { - "autoScrollInterval": "Scroll interval (seconds)", - "autoScrollOffset": "Scroll offset (screen heights)", - "backgroundColor": "Background color", - "backgroundColorModal": "Background color", - "clearCustomCSS": "Reset your custom CSS?", - "clearCustomJS": "Reset your custom JS?", - "cssHint": "Target specific sources using #sourceId-[SOURCEID] in your selectors", - "customCSS": "Custom CSS", - "customJS": "Custom JS", - "deleteCustomTheme": "Delete theme", - "jsHint": "Available variables: html, novelName, chapterName, sourceId, chapterId, novelId", - "navigationControls": "Navigation Controls", - "notSaved": "Not saved", - "openCSSFile": "Import CSS file", - "openJSFile": "Import JS file", - "preset": "Preset", - "readingMode": "Reading Mode", - "readerTheme": "Theme", - "saveCustomTheme": "Save theme", - "textColor": "Text color", - "textColorModal": "Text color", - "title": "Reader", - "verticalSeekbarDesc": "Use vertical seekbar" - }, - "sourceScreen": { - "noResultsFound": "No results found" - }, - "statsScreen": { - "downloadedChapters": "Downloaded chapters", - "genreDistribution": "Genre distribution", - "readChapters": "Read chapters", - "sources": "Sources", - "statusDistribution": "Status distribution", - "title": "Statistics", - "titlesInLibrary": "Titles in library", - "totalChapters": "Total chapters", - "unreadChapters": "Unread chapters" - }, - "tracking": "Tracking", - "trackingScreen": { - "logOutMessage": "Log out from %{name}?", - "revalidate": "Revalidate", - "services": "Services" - }, - "updates": "Updates", - "updatesScreen": { - "deletedChapters": "Deleted %{num} chapters", - "emptyView": "No recent updates", - "lastUpdatedAt": "Library last updated:", - "libraryUpdated": "Library Updated", - "newChapters": "new Chapters", - "novelsUpdated": "%{num} novels updated", - "searchbar": "Search updates", - "unableToGetNovel": "Unable to get novel", - "updatesLower": "updates", - "updatingLibrary": "Updating library" - }, - "onboardingScreen": { - "welcome": "Welcome", - "pickATheme": "Pick a theme", - "light": "Light", - "dark": "Dark", - "system": "System", - "complete": "Complete" - }, - "notifications": { - "IMPORT_EPUB": "Importing EPUB", - "UPDATE_LIBRARY": "Updating Library", - "DRIVE_BACKUP": "Google Drive Backup", - "DRIVE_RESTORE": "Google Drive Restore", - "SELF_HOST_BACKUP": "Self-Host Backup", - "SELF_HOST_RESTORE": "Self-Host Restore", - "LOCAL_BACKUP": "Local Backup", - "LOCAL_RESTORE": "Local Restore", - "MIGRATE_NOVEL": "Migrating Novel", - "DOWNLOAD_CHAPTER": "Downloading Chapter" - } -} diff --git a/strings/languages/ro_RO/strings.json b/strings/languages/ro_RO/strings.json deleted file mode 100644 index 1896da6be..000000000 --- a/strings/languages/ro_RO/strings.json +++ /dev/null @@ -1,574 +0,0 @@ -{ - "aboutScreen": { - "website": "Website", - "discord": "Discord", - "github": "Github", - "helpTranslate": "Help translate", - "plugins": "Plugins", - "version": "Version", - "whatsNew": "What's new" - }, - "advancedSettings": "Advanced", - "advancedSettingsScreen": { - "cachedNovelsDeletedToast": "Cached novels deleted", - "chapterInsertFailed": "chapter insert failed", - "clearCachedNovels": "Clear cached novels", - "clearCachedNovelsDesc": "Delete cached novels which not in your library", - "clearDatabaseWarning": "Read and Downloaded chapters and progress of non-library novels will be lost.", - "clearUpdatesMessage": "Updates cleared.", - "clearUpdatesTab": "Clear updates tab", - "clearUpdatesWarning": "Updates tab will be cleared.", - "clearupdatesTabDesc": "Clears chapter entries in updates tab", - "dataManagement": "Data Management", - "deleteReadChapters": "Delete read chapters", - "deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.", - "importEpub": "Import Epub", - "importNovel": "Import Novel", - "importStaticFiles": "Import Static Files", - "novelInsertFailed": "novel insert failed", - "useFAB": "Use FAB instead of button", - "userAgent": "User Agent", - "recreateDBIndexes": "Recreate DB indexes", - "recreateDBIndexesToast": "Recreated DB indexes", - "recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.", - "recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices." - }, - "appearance": "Appearance", - "appearanceScreen": { - "accentColor": "Accent Color", - "alwaysShowNavLabels": "Always show nav labels", - "appLanguage": "App language", - "languagePickerModal": { - "title": "Select Language", - "restartNote": "You will need to restart the app for the language change to take full effect." - }, - "appLanguageDefault": "Default", - "appTheme": "App theme", - "darkTheme": "Dark Theme", - "hideBackdrop": "Hide backdrop", - "lightTheme": "Light Theme", - "navbar": "Navbar", - "novelInfo": "Novel info", - "pureBlackDarkMode": "Pure black dark mode", - "showHistoryInTheNav": "Show history in the nav", - "showUpdatesInTheNav": "Show updates in the nav", - "themeMode": "Theme mode", - "themeModeLight": "Light", - "themeModeDark": "Dark", - "themeModeSystem": "System", - "theme": { - "default": "Default", - "lavender": "Lavender", - "midnightDusk": "Midnight Dusk", - "daybreakBloom": "Daybreak Bloom", - "strawberry": "Strawberry Daiquiri", - "tako": "Tako", - "teal": "Teal", - "turquoise": "Turquoise", - "yotsuba": "Yotsuba", - "catppuccin": "Catppuccin", - "yinyang": "Yin & Yang" - } - }, - "backupScreen": { - "backupName": "Backup name", - "backupCreated": "Backup created successfully", - "backupRestored": "Backup restored successfully", - "savingBackup": "Saving Backup", - "categoriesRestored": "Restored %{count} categories", - "categoriesRestoredWithErrors": "Restored %{count} categories (%{failedCount} failed)", - "categoryRestoreFailed": "Failed to restore category: %{categoryName} - %{error}", - "categoryFileNotFound": "Category file not found in backup", - "categoryFileReadFailed": "Failed to read category file: %{error}", - "categoryFileWriteFailed": "Failed to write category file: %{error}", - "createBackup": "Create backup", - "createBackupDesc": "Can be used to restore current library", - "createBackupWarning": "Create backup may not work on devices with Android 9 or lower.", - "downloadingData": "Downloading Data", - "downloadingDownloadedFiles": "Downloading Downloaded Files", - "failed": "Backup failed", - "novelsRestored": "Restored %{count} novels", - "novelsRestoredWithErrors": "Restored %{count} novels (%{failedCount} failed)", - "novelBackupFailed": "Failed to backup novel: %{novelName} - %{error}", - "novelRestoreFailed": "Failed to restore novel: %{novelName} - %{error}", - "novelDirectoryNotFound": "Novel directory not found in backup", - "novelDirectoryReadFailed": "Failed to read novel directory: %{error}", - "restoringCategories": "Restoring Categories", - "restoringNovels": "Restoring Novels", - "restoringSettings": "Restoring Settings", - "settingsRestored": "Settings restored", - "settingsFileNotFound": "Settings file not found in backup", - "settingsRestoreFailed": "Failed to restore settings: %{error}", - "settingsFileWriteFailed": "Failed to write settings file: %{error}", - "versionFileWriteFailed": "Failed to write version file: %{error}", - "drive": { - "backup": "Drive Backup", - "backupInterruped": "Drive Backup Interrupted", - "googleDriveBackup": "Google Drive Backup", - "restore": "Drive Restore", - "restoreInterruped": "Drive Restore Interrupted" - }, - "googeDrive": "Googe Drive", - "googeDriveDesc": "Backup to your Google Drive", - "invalidBackupFolder": "Invalid backup folder", - "localBackup": "Local Backup", - "noBackupFound": "No backup found", - "preparingData": "Preparing Data", - "remote": { - "backup": "Self Host Backup", - "host": "Host", - "unknownHost": "Unknown host" - }, - "remoteBackup": "Remote Backup", - "restoreBackup": "Restore backup", - "restoreBackupDesc": "Restore library from backup file", - "restoreLargeBackupsWarning": "Restoring large backups may freeze the app until restoring is finished", - "restorinBackup": "Restoring backup", - "restoringData": "Restoring Data", - "selfHost": "Self Host", - "selfHostDesc": "Backup to your server", - "uploadingData": "Uploading Data", - "uploadingDownloadedFiles": "Uploading Downloaded Files" - }, - "browse": "Browse", - "browseScreen": { - "addedToLibrary": "Added to library", - "available": "Available", - "deletePluginMessage": "Are you sure you want to uninstall %{name}?", - "discover": "Discover", - "globalSearch": "Global search", - "installFailed": "Installation failed: %{name}", - "installed": "Installed", - "installedPlugin": "Installed %{name}", - "installedPlugins": "Installed plugins", - "lastUsed": "Last used", - "latest": "Latest", - "listEmpty": "Enable languages from settings", - "migration": { - "dialogMessage": "Migrate %{url}?", - "novelAlreadyInLibrary": "Novel already in library", - "selectSource": "Select Source", - "selectSourceDesc": "Select a Source To Migrate From" - }, - "noSource": "Your library does not have any novels from this source", - "pinnedPlugin": "Pinned %{name}", - "pinnedPlugins": "Pinned plugins", - "removeFromLibrary": "Removed from library", - "searchbar": "Search sources", - "searchResults": "Search results", - "selectNovel": "Select Novel", - "uninstalledPlugin": "Uninstalled %{name}", - "unpinnedPlugin": "Unpinned %{name}", - "updateFailed": "Update failed", - "updatedTo": "Updated to %{version}", - "settings": { - "title": "Plugin Settings", - "description": "Fill in the plugin settings. Restart app to apply the settings." - } - }, - "browseSettings": "Browse Settings", - "browseSettingsScreen": { - "concurrentSearches": "Concurrent Source Searches", - "multi": "Multi", - "languages": "Languages" - }, - "categories": { - "addCategories": "Add category", - "cantDeleteDefault": "You cant delete default category", - "default": "Default", - "defaultCategory": "Default category", - "deleteModal": { - "desc": "Do you wish to delete category", - "header": "Delete category" - }, - "duplicateError": "A category with this name already exists!", - "editCategories": "Rename category", - "emptyMsg": "You have no categories. Tap the plus button to create one for organizing your library", - "header": "Edit categories", - "local": "Local", - "setCategories": "Set categories", - "setModalEmptyMsg": "You have no categories. Tap the Edit button to create one for organizing your library" - }, - "repositories": { - "emptyMsg": "You have no repositories. Add your first plugin repository to get started." - }, - "common": { - "about": "About", - "add": "Add", - "all": "All", - "backup": "Backup", - "cancel": "Cancel", - "categories": "Categories", - "chapters": "Chapters", - "clear": "Clear", - "copiedToClipboard": "Copied to clipboard: %{name}", - "delete": "Delete", - "deleted": "Deleted %{name}", - "deprecated": "Deprecated", - "display": "Display", - "done": "Done", - "downloads": "Downloads", - "edit": "Edit", - "example": "Example", - "filter": "Filter", - "globally": "globally", - "install": "Install", - "logout": "Logout", - "name": "Name", - "newUpdateAvailable": "New update available", - "ok": "Ok", - "pause": "Pause", - "preparing": "Preparing", - "remove": "Remove", - "reset": "Reset", - "restore": "Restore", - "resume": "Resume", - "retry": "Retry", - "save": "Save", - "search": "Search", - "searchFor": "Search for", - "searchResults": "Search results", - "settings": "Settings", - "show": "Show", - "signIn": "Sign in", - "signOut": "Sign out", - "sort": "Sort", - "submit": "Submit", - "loading": "Loading ...", - "warning": "Warning" - }, - "webview": { - "refresh": "Refresh", - "share": "Share", - "openInBrowser": "Open in browser", - "clearCookies": "Clear cookies", - "cookiesCleared": "Cookies cleared", - "clearData": "Clear WebView data", - "dataDeleted": "WebView data cleared" - }, - "date": { - "calendar": { - "lastDay": "[Yesterday]", - "lastWeek": "[Last] dddd", - "nextDay": "[Tomorrow]", - "sameDay": "[Today]" - } - }, - "downloadScreen": { - "cancelDownloads": "Cancel downloads", - "cancelled": "Downloads cancelled.", - "chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it", - "chapterName": "Chapter: %{name}", - "completed": "Download completed", - "dbInfo": "Downloads are saved in a SQLite Database.", - "downloading": "Downloading", - "downloadingNovel": "Downloading: %{name}", - "downloadsLower": "downloads", - "noDownloads": "No downloads", - "pluginNotFound": "Plugin not found!", - "removeDownloadsWarning": "Are you sure? All downloaded chapters will be deleted." - }, - "generalSettings": "General", - "generalSettingsScreen": { - "asc": "(Ascending)", - "autoDownload": "Auto-download", - "bySource": "By source", - "chapterSort": "Default chapter sort", - "desc": "(Descending)", - "disableLoadingAnimations": "Disable loading animations", - "disableLoadingAnimationsDesc": "May improve performance on slower devices", - "disableHapticFeedback": "Disable haptic feedback", - "disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.", - "displayMode": "Display Mode", - "downloadNewChapters": "Download new chapters", - "epub": "EPUB", - "epubLocation": "EPUB Location", - "epubLocationDescription": "The place where you open and export your EPUB files.", - "globalUpdate": "Global update", - "gridSize": "Grid size", - "gridSizeDesc": "%{num} per row", - "itemsPerRow": "Items per row", - "itemsPerRowLibrary": "Items per row in library", - "jumpToLastReadChapter": "Jump to last read chapter in list", - "novel": "Novel", - "novelBadges": "Novel Badges", - "novelSort": "Novel Sort", - "refreshMetadata": "Automatically refresh metadata", - "refreshMetadataDescription": "Check for new cover and details when updating library", - "sortOrder": "Sort Order", - "updateLibrary": "Update library on launch", - "updateLibraryDesc": "Not recommended for low devices", - "updateOngoing": "Only update ongoing novels", - "updateTime": "Show last update time", - "useFAB": "Use FAB in Library" - }, - "globalSearch": { - "allSources": "all sources", - "searchIn": "Search a novel in" - }, - "history": "History", - "historyScreen": { - "chapter": "Chapter", - "clearHistorWarning": "Are you sure? All history will be lost.", - "deleted": "History deleted.", - "nothingReadRecently": "Nothing read recently", - "searchbar": "Search history" - }, - "library": "Library", - "libraryScreen": { - "bottomSheet": { - "display": { - "badges": "Badges", - "comfortable": "Comfortable grid", - "compact": "Compact grid", - "displayMode": "Display mode", - "download": "Download", - "downloadBadges": "Download badges", - "list": "List", - "noTitle": "Cover only grid", - "numberOfItems": "Number of Items", - "showNoOfItems": "Show number of items", - "unread": "Unread", - "unreadBadges": "Unread badges" - }, - "filters": { - "completed": "Completed", - "downloaded": "Downloaded", - "started": "Started", - "unread": "Unread" - }, - "sortOrders": { - "alphabetically": "Alphabetically", - "dateAdded": "Date added", - "download": "Downloaded", - "lastRead": "Last read", - "lastUpdated": "Last updated", - "totalChapters": "Total chapters", - "unread": "Unread" - } - }, - "empty": "Your library is empty. Add series to your library from Browse.", - "extraMenu": { - "importEpub": "Import Epub", - "openRandom": "Open Random Entry", - "updateCategory": "Update Category", - "updateLibrary": "Update Library" - }, - "searchbar": "Search library" - }, - "more": "More", - "moreScreen": { - "downloadOnly": "Downloaded only", - "downloadOnlyDesc": "Filters all novels in your library", - "downloadQueue": "Download queue", - "incognitoMode": "Incognito mode", - "incognitoModeDesc": "Pauses reading history" - }, - "novelScreen": { - "addToLibaray": "Add to library", - "bottomSheet": { - "displays": { - "chapterNumber": "Chapter number", - "sourceTitle": "Source title" - }, - "filters": { - "bookmarked": "Bookmarked", - "downloaded": "Downloaded", - "unread": "Unread" - }, - "order": { - "byChapterName": "By chapter name", - "bySource": "By source" - } - }, - "chapterChapnum": "Chapter %{num}", - "chapters": "chapters", - "continueReading": "Continue reading", - "exportEpubModal": { - "applyReaderTheme": "Apply reader theme to EPUB", - "customJSWarning": "Custom JS may not be supported by all EPUB readers", - "downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file", - "endChapter": "End Chapter", - "exportAll": "Export All Chapters", - "includeCustomCSS": "Include Custom CSS", - "includeCustomJS": "Include Custom JS", - "invalidRange": "Please enter valid chapter numbers", - "selectFolder": "Select destination folder for EPUB file", - "startChapter": "Start Chapter", - "startGreaterThanEnd": "Start chapter must be less than or equal to end chapter", - "title": "Export Novel as EPUB" - }, - "epub": { - "exportFailed": "Failed to export EPUB: %{error}", - "exportSuccess": "Successfully exported %{chapters} chapters as EPUB", - "noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.", - "noNovelSelected": "No novel selected for export" - }, - "coverSaved": "Cover saved", - "coverNotSaved": "Cover not saved", - "deleteChapterError": "Cant delete chapter chapter folder", - "deleteMessage": "Delete downloaded chapters?", - "deletedAllDownloads": "Deleted all Downloads", - "download": { - "custom": "Custom", - "customAmount": "Download custom amount", - "delete": "Delete downloads", - "next": "Next chapter", - "next10": "Next 10 chapter", - "next5": "Next 5 chapter", - "unread": "Unread" - }, - "edit": { - "addTag": "Add Tag", - "author": "Author: %{author}", - "cover": "Edit cover", - "info": "Edit info", - "status": "Status:", - "summary": "Description: %{summary}...", - "title": "Title: %{title}" - }, - "inLibaray": "In library", - "jumpToChapterModal": { - "chapterName": "Chapter Name", - "chapterNumber": "Chapter Number", - "error": { - "validChapterName": "Enter a valid chapter name", - "validChapterNumber": "Enter a valid chapter number" - }, - "jumpToChapter": "Jump to Chapter", - "openChapter": "Open Chapter" - }, - "migrate": "Migrate", - "noSummary": "No summary", - "noCoverFound": "No cover found", - "progress": "Progress %{progress} %", - "readChaptersDeleted": "Read chapters deleted", - "startReadingChapters": "Start reading %{name}", - "status": { - "cancelled": "Cancelled", - "completed": "Completed", - "licensed": "Licensed", - "onHiatus": "On Hiatus", - "ongoing": "Ongoing", - "publishingFinished": "Publishing Finished", - "unknown": "Unknown" - }, - "tracked": "Tracked", - "tracking": "Tracking", - "unknownStatus": "Unknown status", - "updatedToast": "Updated %{name}" - }, - "readerScreen": { - "bottomSheet": { - "allowTextSelection": "Text selection", - "autoscroll": "Auto-scroll", - "bionicReading": "Bionic reading", - "tapToScroll": "Tap to scroll", - "color": "Color", - "fontStyle": "Font style", - "fullscreen": "Fullscreen", - "lineHeight": "Line height", - "padding": "Padding", - "pageReader": "Paged reading (Experimental)", - "removeExtraSpacing": "Remove extra spacing", - "scrollAmount": "Scroll amount (screen height by default)", - "showBatteryAndTime": "Battery & time", - "showProgressPercentage": "Reading progress", - "swipeGestures": "Swipe between chapters", - "textAlign": "Text alignment", - "textSize": "Text size", - "useChapterDrawerSwipeNavigation": "Swipe to open drawer", - "verticalSeekbar": "Vertical seekbar", - "keepScreenOn": "Keep screen on", - "volumeButtonsScroll": "Volume button scrolling" - }, - "drawer": { - "scrollToBottom": "Scroll to bottom", - "scrollToCurrentChapter": "Scroll to current chapter", - "scrollToTop": "Scroll to top" - }, - "emptyChapterMessage": "

    Chapter is empty.

    Report on GitHub if it's available in WebView.

    Plugin: %{pluginId}

    Novel: %{novelName}

    Chapter: %{chapterName}

    ", - "finished": "Finished", - "nextChapter": "Next: %{name}", - "noNextChapter": "There's no next chapter", - "noPreviousChapter": "There's no previous chapter" - }, - "readerSettings": { - "autoScrollInterval": "Scroll interval (seconds)", - "autoScrollOffset": "Scroll offset (screen heights)", - "backgroundColor": "Background color", - "backgroundColorModal": "Background color", - "clearCustomCSS": "Reset your custom CSS?", - "clearCustomJS": "Reset your custom JS?", - "cssHint": "Target specific sources using #sourceId-[SOURCEID] in your selectors", - "customCSS": "Custom CSS", - "customJS": "Custom JS", - "deleteCustomTheme": "Delete theme", - "jsHint": "Available variables: html, novelName, chapterName, sourceId, chapterId, novelId", - "navigationControls": "Navigation Controls", - "notSaved": "Not saved", - "openCSSFile": "Import CSS file", - "openJSFile": "Import JS file", - "preset": "Preset", - "readingMode": "Reading Mode", - "readerTheme": "Theme", - "saveCustomTheme": "Save theme", - "textColor": "Text color", - "textColorModal": "Text color", - "title": "Reader", - "verticalSeekbarDesc": "Use vertical seekbar" - }, - "sourceScreen": { - "noResultsFound": "No results found" - }, - "statsScreen": { - "downloadedChapters": "Downloaded chapters", - "genreDistribution": "Genre distribution", - "readChapters": "Read chapters", - "sources": "Sources", - "statusDistribution": "Status distribution", - "title": "Statistics", - "titlesInLibrary": "Titles in library", - "totalChapters": "Total chapters", - "unreadChapters": "Unread chapters" - }, - "tracking": "Tracking", - "trackingScreen": { - "logOutMessage": "Log out from %{name}?", - "revalidate": "Revalidate", - "services": "Services" - }, - "updates": "Updates", - "updatesScreen": { - "deletedChapters": "Deleted %{num} chapters", - "emptyView": "No recent updates", - "lastUpdatedAt": "Library last updated:", - "libraryUpdated": "Library Updated", - "newChapters": "new Chapters", - "novelsUpdated": "%{num} novels updated", - "searchbar": "Search updates", - "unableToGetNovel": "Unable to get novel", - "updatesLower": "updates", - "updatingLibrary": "Updating library" - }, - "onboardingScreen": { - "welcome": "Welcome", - "pickATheme": "Pick a theme", - "light": "Light", - "dark": "Dark", - "system": "System", - "complete": "Complete" - }, - "notifications": { - "IMPORT_EPUB": "Importing EPUB", - "UPDATE_LIBRARY": "Updating Library", - "DRIVE_BACKUP": "Google Drive Backup", - "DRIVE_RESTORE": "Google Drive Restore", - "SELF_HOST_BACKUP": "Self-Host Backup", - "SELF_HOST_RESTORE": "Self-Host Restore", - "LOCAL_BACKUP": "Local Backup", - "LOCAL_RESTORE": "Local Restore", - "MIGRATE_NOVEL": "Migrating Novel", - "DOWNLOAD_CHAPTER": "Downloading Chapter" - } -} diff --git a/strings/languages/sq_AL/strings.json b/strings/languages/sq_AL/strings.json deleted file mode 100644 index 1896da6be..000000000 --- a/strings/languages/sq_AL/strings.json +++ /dev/null @@ -1,574 +0,0 @@ -{ - "aboutScreen": { - "website": "Website", - "discord": "Discord", - "github": "Github", - "helpTranslate": "Help translate", - "plugins": "Plugins", - "version": "Version", - "whatsNew": "What's new" - }, - "advancedSettings": "Advanced", - "advancedSettingsScreen": { - "cachedNovelsDeletedToast": "Cached novels deleted", - "chapterInsertFailed": "chapter insert failed", - "clearCachedNovels": "Clear cached novels", - "clearCachedNovelsDesc": "Delete cached novels which not in your library", - "clearDatabaseWarning": "Read and Downloaded chapters and progress of non-library novels will be lost.", - "clearUpdatesMessage": "Updates cleared.", - "clearUpdatesTab": "Clear updates tab", - "clearUpdatesWarning": "Updates tab will be cleared.", - "clearupdatesTabDesc": "Clears chapter entries in updates tab", - "dataManagement": "Data Management", - "deleteReadChapters": "Delete read chapters", - "deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.", - "importEpub": "Import Epub", - "importNovel": "Import Novel", - "importStaticFiles": "Import Static Files", - "novelInsertFailed": "novel insert failed", - "useFAB": "Use FAB instead of button", - "userAgent": "User Agent", - "recreateDBIndexes": "Recreate DB indexes", - "recreateDBIndexesToast": "Recreated DB indexes", - "recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.", - "recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices." - }, - "appearance": "Appearance", - "appearanceScreen": { - "accentColor": "Accent Color", - "alwaysShowNavLabels": "Always show nav labels", - "appLanguage": "App language", - "languagePickerModal": { - "title": "Select Language", - "restartNote": "You will need to restart the app for the language change to take full effect." - }, - "appLanguageDefault": "Default", - "appTheme": "App theme", - "darkTheme": "Dark Theme", - "hideBackdrop": "Hide backdrop", - "lightTheme": "Light Theme", - "navbar": "Navbar", - "novelInfo": "Novel info", - "pureBlackDarkMode": "Pure black dark mode", - "showHistoryInTheNav": "Show history in the nav", - "showUpdatesInTheNav": "Show updates in the nav", - "themeMode": "Theme mode", - "themeModeLight": "Light", - "themeModeDark": "Dark", - "themeModeSystem": "System", - "theme": { - "default": "Default", - "lavender": "Lavender", - "midnightDusk": "Midnight Dusk", - "daybreakBloom": "Daybreak Bloom", - "strawberry": "Strawberry Daiquiri", - "tako": "Tako", - "teal": "Teal", - "turquoise": "Turquoise", - "yotsuba": "Yotsuba", - "catppuccin": "Catppuccin", - "yinyang": "Yin & Yang" - } - }, - "backupScreen": { - "backupName": "Backup name", - "backupCreated": "Backup created successfully", - "backupRestored": "Backup restored successfully", - "savingBackup": "Saving Backup", - "categoriesRestored": "Restored %{count} categories", - "categoriesRestoredWithErrors": "Restored %{count} categories (%{failedCount} failed)", - "categoryRestoreFailed": "Failed to restore category: %{categoryName} - %{error}", - "categoryFileNotFound": "Category file not found in backup", - "categoryFileReadFailed": "Failed to read category file: %{error}", - "categoryFileWriteFailed": "Failed to write category file: %{error}", - "createBackup": "Create backup", - "createBackupDesc": "Can be used to restore current library", - "createBackupWarning": "Create backup may not work on devices with Android 9 or lower.", - "downloadingData": "Downloading Data", - "downloadingDownloadedFiles": "Downloading Downloaded Files", - "failed": "Backup failed", - "novelsRestored": "Restored %{count} novels", - "novelsRestoredWithErrors": "Restored %{count} novels (%{failedCount} failed)", - "novelBackupFailed": "Failed to backup novel: %{novelName} - %{error}", - "novelRestoreFailed": "Failed to restore novel: %{novelName} - %{error}", - "novelDirectoryNotFound": "Novel directory not found in backup", - "novelDirectoryReadFailed": "Failed to read novel directory: %{error}", - "restoringCategories": "Restoring Categories", - "restoringNovels": "Restoring Novels", - "restoringSettings": "Restoring Settings", - "settingsRestored": "Settings restored", - "settingsFileNotFound": "Settings file not found in backup", - "settingsRestoreFailed": "Failed to restore settings: %{error}", - "settingsFileWriteFailed": "Failed to write settings file: %{error}", - "versionFileWriteFailed": "Failed to write version file: %{error}", - "drive": { - "backup": "Drive Backup", - "backupInterruped": "Drive Backup Interrupted", - "googleDriveBackup": "Google Drive Backup", - "restore": "Drive Restore", - "restoreInterruped": "Drive Restore Interrupted" - }, - "googeDrive": "Googe Drive", - "googeDriveDesc": "Backup to your Google Drive", - "invalidBackupFolder": "Invalid backup folder", - "localBackup": "Local Backup", - "noBackupFound": "No backup found", - "preparingData": "Preparing Data", - "remote": { - "backup": "Self Host Backup", - "host": "Host", - "unknownHost": "Unknown host" - }, - "remoteBackup": "Remote Backup", - "restoreBackup": "Restore backup", - "restoreBackupDesc": "Restore library from backup file", - "restoreLargeBackupsWarning": "Restoring large backups may freeze the app until restoring is finished", - "restorinBackup": "Restoring backup", - "restoringData": "Restoring Data", - "selfHost": "Self Host", - "selfHostDesc": "Backup to your server", - "uploadingData": "Uploading Data", - "uploadingDownloadedFiles": "Uploading Downloaded Files" - }, - "browse": "Browse", - "browseScreen": { - "addedToLibrary": "Added to library", - "available": "Available", - "deletePluginMessage": "Are you sure you want to uninstall %{name}?", - "discover": "Discover", - "globalSearch": "Global search", - "installFailed": "Installation failed: %{name}", - "installed": "Installed", - "installedPlugin": "Installed %{name}", - "installedPlugins": "Installed plugins", - "lastUsed": "Last used", - "latest": "Latest", - "listEmpty": "Enable languages from settings", - "migration": { - "dialogMessage": "Migrate %{url}?", - "novelAlreadyInLibrary": "Novel already in library", - "selectSource": "Select Source", - "selectSourceDesc": "Select a Source To Migrate From" - }, - "noSource": "Your library does not have any novels from this source", - "pinnedPlugin": "Pinned %{name}", - "pinnedPlugins": "Pinned plugins", - "removeFromLibrary": "Removed from library", - "searchbar": "Search sources", - "searchResults": "Search results", - "selectNovel": "Select Novel", - "uninstalledPlugin": "Uninstalled %{name}", - "unpinnedPlugin": "Unpinned %{name}", - "updateFailed": "Update failed", - "updatedTo": "Updated to %{version}", - "settings": { - "title": "Plugin Settings", - "description": "Fill in the plugin settings. Restart app to apply the settings." - } - }, - "browseSettings": "Browse Settings", - "browseSettingsScreen": { - "concurrentSearches": "Concurrent Source Searches", - "multi": "Multi", - "languages": "Languages" - }, - "categories": { - "addCategories": "Add category", - "cantDeleteDefault": "You cant delete default category", - "default": "Default", - "defaultCategory": "Default category", - "deleteModal": { - "desc": "Do you wish to delete category", - "header": "Delete category" - }, - "duplicateError": "A category with this name already exists!", - "editCategories": "Rename category", - "emptyMsg": "You have no categories. Tap the plus button to create one for organizing your library", - "header": "Edit categories", - "local": "Local", - "setCategories": "Set categories", - "setModalEmptyMsg": "You have no categories. Tap the Edit button to create one for organizing your library" - }, - "repositories": { - "emptyMsg": "You have no repositories. Add your first plugin repository to get started." - }, - "common": { - "about": "About", - "add": "Add", - "all": "All", - "backup": "Backup", - "cancel": "Cancel", - "categories": "Categories", - "chapters": "Chapters", - "clear": "Clear", - "copiedToClipboard": "Copied to clipboard: %{name}", - "delete": "Delete", - "deleted": "Deleted %{name}", - "deprecated": "Deprecated", - "display": "Display", - "done": "Done", - "downloads": "Downloads", - "edit": "Edit", - "example": "Example", - "filter": "Filter", - "globally": "globally", - "install": "Install", - "logout": "Logout", - "name": "Name", - "newUpdateAvailable": "New update available", - "ok": "Ok", - "pause": "Pause", - "preparing": "Preparing", - "remove": "Remove", - "reset": "Reset", - "restore": "Restore", - "resume": "Resume", - "retry": "Retry", - "save": "Save", - "search": "Search", - "searchFor": "Search for", - "searchResults": "Search results", - "settings": "Settings", - "show": "Show", - "signIn": "Sign in", - "signOut": "Sign out", - "sort": "Sort", - "submit": "Submit", - "loading": "Loading ...", - "warning": "Warning" - }, - "webview": { - "refresh": "Refresh", - "share": "Share", - "openInBrowser": "Open in browser", - "clearCookies": "Clear cookies", - "cookiesCleared": "Cookies cleared", - "clearData": "Clear WebView data", - "dataDeleted": "WebView data cleared" - }, - "date": { - "calendar": { - "lastDay": "[Yesterday]", - "lastWeek": "[Last] dddd", - "nextDay": "[Tomorrow]", - "sameDay": "[Today]" - } - }, - "downloadScreen": { - "cancelDownloads": "Cancel downloads", - "cancelled": "Downloads cancelled.", - "chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it", - "chapterName": "Chapter: %{name}", - "completed": "Download completed", - "dbInfo": "Downloads are saved in a SQLite Database.", - "downloading": "Downloading", - "downloadingNovel": "Downloading: %{name}", - "downloadsLower": "downloads", - "noDownloads": "No downloads", - "pluginNotFound": "Plugin not found!", - "removeDownloadsWarning": "Are you sure? All downloaded chapters will be deleted." - }, - "generalSettings": "General", - "generalSettingsScreen": { - "asc": "(Ascending)", - "autoDownload": "Auto-download", - "bySource": "By source", - "chapterSort": "Default chapter sort", - "desc": "(Descending)", - "disableLoadingAnimations": "Disable loading animations", - "disableLoadingAnimationsDesc": "May improve performance on slower devices", - "disableHapticFeedback": "Disable haptic feedback", - "disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.", - "displayMode": "Display Mode", - "downloadNewChapters": "Download new chapters", - "epub": "EPUB", - "epubLocation": "EPUB Location", - "epubLocationDescription": "The place where you open and export your EPUB files.", - "globalUpdate": "Global update", - "gridSize": "Grid size", - "gridSizeDesc": "%{num} per row", - "itemsPerRow": "Items per row", - "itemsPerRowLibrary": "Items per row in library", - "jumpToLastReadChapter": "Jump to last read chapter in list", - "novel": "Novel", - "novelBadges": "Novel Badges", - "novelSort": "Novel Sort", - "refreshMetadata": "Automatically refresh metadata", - "refreshMetadataDescription": "Check for new cover and details when updating library", - "sortOrder": "Sort Order", - "updateLibrary": "Update library on launch", - "updateLibraryDesc": "Not recommended for low devices", - "updateOngoing": "Only update ongoing novels", - "updateTime": "Show last update time", - "useFAB": "Use FAB in Library" - }, - "globalSearch": { - "allSources": "all sources", - "searchIn": "Search a novel in" - }, - "history": "History", - "historyScreen": { - "chapter": "Chapter", - "clearHistorWarning": "Are you sure? All history will be lost.", - "deleted": "History deleted.", - "nothingReadRecently": "Nothing read recently", - "searchbar": "Search history" - }, - "library": "Library", - "libraryScreen": { - "bottomSheet": { - "display": { - "badges": "Badges", - "comfortable": "Comfortable grid", - "compact": "Compact grid", - "displayMode": "Display mode", - "download": "Download", - "downloadBadges": "Download badges", - "list": "List", - "noTitle": "Cover only grid", - "numberOfItems": "Number of Items", - "showNoOfItems": "Show number of items", - "unread": "Unread", - "unreadBadges": "Unread badges" - }, - "filters": { - "completed": "Completed", - "downloaded": "Downloaded", - "started": "Started", - "unread": "Unread" - }, - "sortOrders": { - "alphabetically": "Alphabetically", - "dateAdded": "Date added", - "download": "Downloaded", - "lastRead": "Last read", - "lastUpdated": "Last updated", - "totalChapters": "Total chapters", - "unread": "Unread" - } - }, - "empty": "Your library is empty. Add series to your library from Browse.", - "extraMenu": { - "importEpub": "Import Epub", - "openRandom": "Open Random Entry", - "updateCategory": "Update Category", - "updateLibrary": "Update Library" - }, - "searchbar": "Search library" - }, - "more": "More", - "moreScreen": { - "downloadOnly": "Downloaded only", - "downloadOnlyDesc": "Filters all novels in your library", - "downloadQueue": "Download queue", - "incognitoMode": "Incognito mode", - "incognitoModeDesc": "Pauses reading history" - }, - "novelScreen": { - "addToLibaray": "Add to library", - "bottomSheet": { - "displays": { - "chapterNumber": "Chapter number", - "sourceTitle": "Source title" - }, - "filters": { - "bookmarked": "Bookmarked", - "downloaded": "Downloaded", - "unread": "Unread" - }, - "order": { - "byChapterName": "By chapter name", - "bySource": "By source" - } - }, - "chapterChapnum": "Chapter %{num}", - "chapters": "chapters", - "continueReading": "Continue reading", - "exportEpubModal": { - "applyReaderTheme": "Apply reader theme to EPUB", - "customJSWarning": "Custom JS may not be supported by all EPUB readers", - "downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file", - "endChapter": "End Chapter", - "exportAll": "Export All Chapters", - "includeCustomCSS": "Include Custom CSS", - "includeCustomJS": "Include Custom JS", - "invalidRange": "Please enter valid chapter numbers", - "selectFolder": "Select destination folder for EPUB file", - "startChapter": "Start Chapter", - "startGreaterThanEnd": "Start chapter must be less than or equal to end chapter", - "title": "Export Novel as EPUB" - }, - "epub": { - "exportFailed": "Failed to export EPUB: %{error}", - "exportSuccess": "Successfully exported %{chapters} chapters as EPUB", - "noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.", - "noNovelSelected": "No novel selected for export" - }, - "coverSaved": "Cover saved", - "coverNotSaved": "Cover not saved", - "deleteChapterError": "Cant delete chapter chapter folder", - "deleteMessage": "Delete downloaded chapters?", - "deletedAllDownloads": "Deleted all Downloads", - "download": { - "custom": "Custom", - "customAmount": "Download custom amount", - "delete": "Delete downloads", - "next": "Next chapter", - "next10": "Next 10 chapter", - "next5": "Next 5 chapter", - "unread": "Unread" - }, - "edit": { - "addTag": "Add Tag", - "author": "Author: %{author}", - "cover": "Edit cover", - "info": "Edit info", - "status": "Status:", - "summary": "Description: %{summary}...", - "title": "Title: %{title}" - }, - "inLibaray": "In library", - "jumpToChapterModal": { - "chapterName": "Chapter Name", - "chapterNumber": "Chapter Number", - "error": { - "validChapterName": "Enter a valid chapter name", - "validChapterNumber": "Enter a valid chapter number" - }, - "jumpToChapter": "Jump to Chapter", - "openChapter": "Open Chapter" - }, - "migrate": "Migrate", - "noSummary": "No summary", - "noCoverFound": "No cover found", - "progress": "Progress %{progress} %", - "readChaptersDeleted": "Read chapters deleted", - "startReadingChapters": "Start reading %{name}", - "status": { - "cancelled": "Cancelled", - "completed": "Completed", - "licensed": "Licensed", - "onHiatus": "On Hiatus", - "ongoing": "Ongoing", - "publishingFinished": "Publishing Finished", - "unknown": "Unknown" - }, - "tracked": "Tracked", - "tracking": "Tracking", - "unknownStatus": "Unknown status", - "updatedToast": "Updated %{name}" - }, - "readerScreen": { - "bottomSheet": { - "allowTextSelection": "Text selection", - "autoscroll": "Auto-scroll", - "bionicReading": "Bionic reading", - "tapToScroll": "Tap to scroll", - "color": "Color", - "fontStyle": "Font style", - "fullscreen": "Fullscreen", - "lineHeight": "Line height", - "padding": "Padding", - "pageReader": "Paged reading (Experimental)", - "removeExtraSpacing": "Remove extra spacing", - "scrollAmount": "Scroll amount (screen height by default)", - "showBatteryAndTime": "Battery & time", - "showProgressPercentage": "Reading progress", - "swipeGestures": "Swipe between chapters", - "textAlign": "Text alignment", - "textSize": "Text size", - "useChapterDrawerSwipeNavigation": "Swipe to open drawer", - "verticalSeekbar": "Vertical seekbar", - "keepScreenOn": "Keep screen on", - "volumeButtonsScroll": "Volume button scrolling" - }, - "drawer": { - "scrollToBottom": "Scroll to bottom", - "scrollToCurrentChapter": "Scroll to current chapter", - "scrollToTop": "Scroll to top" - }, - "emptyChapterMessage": "

    Chapter is empty.

    Report on GitHub if it's available in WebView.

    Plugin: %{pluginId}

    Novel: %{novelName}

    Chapter: %{chapterName}

    ", - "finished": "Finished", - "nextChapter": "Next: %{name}", - "noNextChapter": "There's no next chapter", - "noPreviousChapter": "There's no previous chapter" - }, - "readerSettings": { - "autoScrollInterval": "Scroll interval (seconds)", - "autoScrollOffset": "Scroll offset (screen heights)", - "backgroundColor": "Background color", - "backgroundColorModal": "Background color", - "clearCustomCSS": "Reset your custom CSS?", - "clearCustomJS": "Reset your custom JS?", - "cssHint": "Target specific sources using #sourceId-[SOURCEID] in your selectors", - "customCSS": "Custom CSS", - "customJS": "Custom JS", - "deleteCustomTheme": "Delete theme", - "jsHint": "Available variables: html, novelName, chapterName, sourceId, chapterId, novelId", - "navigationControls": "Navigation Controls", - "notSaved": "Not saved", - "openCSSFile": "Import CSS file", - "openJSFile": "Import JS file", - "preset": "Preset", - "readingMode": "Reading Mode", - "readerTheme": "Theme", - "saveCustomTheme": "Save theme", - "textColor": "Text color", - "textColorModal": "Text color", - "title": "Reader", - "verticalSeekbarDesc": "Use vertical seekbar" - }, - "sourceScreen": { - "noResultsFound": "No results found" - }, - "statsScreen": { - "downloadedChapters": "Downloaded chapters", - "genreDistribution": "Genre distribution", - "readChapters": "Read chapters", - "sources": "Sources", - "statusDistribution": "Status distribution", - "title": "Statistics", - "titlesInLibrary": "Titles in library", - "totalChapters": "Total chapters", - "unreadChapters": "Unread chapters" - }, - "tracking": "Tracking", - "trackingScreen": { - "logOutMessage": "Log out from %{name}?", - "revalidate": "Revalidate", - "services": "Services" - }, - "updates": "Updates", - "updatesScreen": { - "deletedChapters": "Deleted %{num} chapters", - "emptyView": "No recent updates", - "lastUpdatedAt": "Library last updated:", - "libraryUpdated": "Library Updated", - "newChapters": "new Chapters", - "novelsUpdated": "%{num} novels updated", - "searchbar": "Search updates", - "unableToGetNovel": "Unable to get novel", - "updatesLower": "updates", - "updatingLibrary": "Updating library" - }, - "onboardingScreen": { - "welcome": "Welcome", - "pickATheme": "Pick a theme", - "light": "Light", - "dark": "Dark", - "system": "System", - "complete": "Complete" - }, - "notifications": { - "IMPORT_EPUB": "Importing EPUB", - "UPDATE_LIBRARY": "Updating Library", - "DRIVE_BACKUP": "Google Drive Backup", - "DRIVE_RESTORE": "Google Drive Restore", - "SELF_HOST_BACKUP": "Self-Host Backup", - "SELF_HOST_RESTORE": "Self-Host Restore", - "LOCAL_BACKUP": "Local Backup", - "LOCAL_RESTORE": "Local Restore", - "MIGRATE_NOVEL": "Migrating Novel", - "DOWNLOAD_CHAPTER": "Downloading Chapter" - } -} diff --git a/strings/languages/sr_SP/strings.json b/strings/languages/sr_SP/strings.json deleted file mode 100644 index 1896da6be..000000000 --- a/strings/languages/sr_SP/strings.json +++ /dev/null @@ -1,574 +0,0 @@ -{ - "aboutScreen": { - "website": "Website", - "discord": "Discord", - "github": "Github", - "helpTranslate": "Help translate", - "plugins": "Plugins", - "version": "Version", - "whatsNew": "What's new" - }, - "advancedSettings": "Advanced", - "advancedSettingsScreen": { - "cachedNovelsDeletedToast": "Cached novels deleted", - "chapterInsertFailed": "chapter insert failed", - "clearCachedNovels": "Clear cached novels", - "clearCachedNovelsDesc": "Delete cached novels which not in your library", - "clearDatabaseWarning": "Read and Downloaded chapters and progress of non-library novels will be lost.", - "clearUpdatesMessage": "Updates cleared.", - "clearUpdatesTab": "Clear updates tab", - "clearUpdatesWarning": "Updates tab will be cleared.", - "clearupdatesTabDesc": "Clears chapter entries in updates tab", - "dataManagement": "Data Management", - "deleteReadChapters": "Delete read chapters", - "deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.", - "importEpub": "Import Epub", - "importNovel": "Import Novel", - "importStaticFiles": "Import Static Files", - "novelInsertFailed": "novel insert failed", - "useFAB": "Use FAB instead of button", - "userAgent": "User Agent", - "recreateDBIndexes": "Recreate DB indexes", - "recreateDBIndexesToast": "Recreated DB indexes", - "recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.", - "recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices." - }, - "appearance": "Appearance", - "appearanceScreen": { - "accentColor": "Accent Color", - "alwaysShowNavLabels": "Always show nav labels", - "appLanguage": "App language", - "languagePickerModal": { - "title": "Select Language", - "restartNote": "You will need to restart the app for the language change to take full effect." - }, - "appLanguageDefault": "Default", - "appTheme": "App theme", - "darkTheme": "Dark Theme", - "hideBackdrop": "Hide backdrop", - "lightTheme": "Light Theme", - "navbar": "Navbar", - "novelInfo": "Novel info", - "pureBlackDarkMode": "Pure black dark mode", - "showHistoryInTheNav": "Show history in the nav", - "showUpdatesInTheNav": "Show updates in the nav", - "themeMode": "Theme mode", - "themeModeLight": "Light", - "themeModeDark": "Dark", - "themeModeSystem": "System", - "theme": { - "default": "Default", - "lavender": "Lavender", - "midnightDusk": "Midnight Dusk", - "daybreakBloom": "Daybreak Bloom", - "strawberry": "Strawberry Daiquiri", - "tako": "Tako", - "teal": "Teal", - "turquoise": "Turquoise", - "yotsuba": "Yotsuba", - "catppuccin": "Catppuccin", - "yinyang": "Yin & Yang" - } - }, - "backupScreen": { - "backupName": "Backup name", - "backupCreated": "Backup created successfully", - "backupRestored": "Backup restored successfully", - "savingBackup": "Saving Backup", - "categoriesRestored": "Restored %{count} categories", - "categoriesRestoredWithErrors": "Restored %{count} categories (%{failedCount} failed)", - "categoryRestoreFailed": "Failed to restore category: %{categoryName} - %{error}", - "categoryFileNotFound": "Category file not found in backup", - "categoryFileReadFailed": "Failed to read category file: %{error}", - "categoryFileWriteFailed": "Failed to write category file: %{error}", - "createBackup": "Create backup", - "createBackupDesc": "Can be used to restore current library", - "createBackupWarning": "Create backup may not work on devices with Android 9 or lower.", - "downloadingData": "Downloading Data", - "downloadingDownloadedFiles": "Downloading Downloaded Files", - "failed": "Backup failed", - "novelsRestored": "Restored %{count} novels", - "novelsRestoredWithErrors": "Restored %{count} novels (%{failedCount} failed)", - "novelBackupFailed": "Failed to backup novel: %{novelName} - %{error}", - "novelRestoreFailed": "Failed to restore novel: %{novelName} - %{error}", - "novelDirectoryNotFound": "Novel directory not found in backup", - "novelDirectoryReadFailed": "Failed to read novel directory: %{error}", - "restoringCategories": "Restoring Categories", - "restoringNovels": "Restoring Novels", - "restoringSettings": "Restoring Settings", - "settingsRestored": "Settings restored", - "settingsFileNotFound": "Settings file not found in backup", - "settingsRestoreFailed": "Failed to restore settings: %{error}", - "settingsFileWriteFailed": "Failed to write settings file: %{error}", - "versionFileWriteFailed": "Failed to write version file: %{error}", - "drive": { - "backup": "Drive Backup", - "backupInterruped": "Drive Backup Interrupted", - "googleDriveBackup": "Google Drive Backup", - "restore": "Drive Restore", - "restoreInterruped": "Drive Restore Interrupted" - }, - "googeDrive": "Googe Drive", - "googeDriveDesc": "Backup to your Google Drive", - "invalidBackupFolder": "Invalid backup folder", - "localBackup": "Local Backup", - "noBackupFound": "No backup found", - "preparingData": "Preparing Data", - "remote": { - "backup": "Self Host Backup", - "host": "Host", - "unknownHost": "Unknown host" - }, - "remoteBackup": "Remote Backup", - "restoreBackup": "Restore backup", - "restoreBackupDesc": "Restore library from backup file", - "restoreLargeBackupsWarning": "Restoring large backups may freeze the app until restoring is finished", - "restorinBackup": "Restoring backup", - "restoringData": "Restoring Data", - "selfHost": "Self Host", - "selfHostDesc": "Backup to your server", - "uploadingData": "Uploading Data", - "uploadingDownloadedFiles": "Uploading Downloaded Files" - }, - "browse": "Browse", - "browseScreen": { - "addedToLibrary": "Added to library", - "available": "Available", - "deletePluginMessage": "Are you sure you want to uninstall %{name}?", - "discover": "Discover", - "globalSearch": "Global search", - "installFailed": "Installation failed: %{name}", - "installed": "Installed", - "installedPlugin": "Installed %{name}", - "installedPlugins": "Installed plugins", - "lastUsed": "Last used", - "latest": "Latest", - "listEmpty": "Enable languages from settings", - "migration": { - "dialogMessage": "Migrate %{url}?", - "novelAlreadyInLibrary": "Novel already in library", - "selectSource": "Select Source", - "selectSourceDesc": "Select a Source To Migrate From" - }, - "noSource": "Your library does not have any novels from this source", - "pinnedPlugin": "Pinned %{name}", - "pinnedPlugins": "Pinned plugins", - "removeFromLibrary": "Removed from library", - "searchbar": "Search sources", - "searchResults": "Search results", - "selectNovel": "Select Novel", - "uninstalledPlugin": "Uninstalled %{name}", - "unpinnedPlugin": "Unpinned %{name}", - "updateFailed": "Update failed", - "updatedTo": "Updated to %{version}", - "settings": { - "title": "Plugin Settings", - "description": "Fill in the plugin settings. Restart app to apply the settings." - } - }, - "browseSettings": "Browse Settings", - "browseSettingsScreen": { - "concurrentSearches": "Concurrent Source Searches", - "multi": "Multi", - "languages": "Languages" - }, - "categories": { - "addCategories": "Add category", - "cantDeleteDefault": "You cant delete default category", - "default": "Default", - "defaultCategory": "Default category", - "deleteModal": { - "desc": "Do you wish to delete category", - "header": "Delete category" - }, - "duplicateError": "A category with this name already exists!", - "editCategories": "Rename category", - "emptyMsg": "You have no categories. Tap the plus button to create one for organizing your library", - "header": "Edit categories", - "local": "Local", - "setCategories": "Set categories", - "setModalEmptyMsg": "You have no categories. Tap the Edit button to create one for organizing your library" - }, - "repositories": { - "emptyMsg": "You have no repositories. Add your first plugin repository to get started." - }, - "common": { - "about": "About", - "add": "Add", - "all": "All", - "backup": "Backup", - "cancel": "Cancel", - "categories": "Categories", - "chapters": "Chapters", - "clear": "Clear", - "copiedToClipboard": "Copied to clipboard: %{name}", - "delete": "Delete", - "deleted": "Deleted %{name}", - "deprecated": "Deprecated", - "display": "Display", - "done": "Done", - "downloads": "Downloads", - "edit": "Edit", - "example": "Example", - "filter": "Filter", - "globally": "globally", - "install": "Install", - "logout": "Logout", - "name": "Name", - "newUpdateAvailable": "New update available", - "ok": "Ok", - "pause": "Pause", - "preparing": "Preparing", - "remove": "Remove", - "reset": "Reset", - "restore": "Restore", - "resume": "Resume", - "retry": "Retry", - "save": "Save", - "search": "Search", - "searchFor": "Search for", - "searchResults": "Search results", - "settings": "Settings", - "show": "Show", - "signIn": "Sign in", - "signOut": "Sign out", - "sort": "Sort", - "submit": "Submit", - "loading": "Loading ...", - "warning": "Warning" - }, - "webview": { - "refresh": "Refresh", - "share": "Share", - "openInBrowser": "Open in browser", - "clearCookies": "Clear cookies", - "cookiesCleared": "Cookies cleared", - "clearData": "Clear WebView data", - "dataDeleted": "WebView data cleared" - }, - "date": { - "calendar": { - "lastDay": "[Yesterday]", - "lastWeek": "[Last] dddd", - "nextDay": "[Tomorrow]", - "sameDay": "[Today]" - } - }, - "downloadScreen": { - "cancelDownloads": "Cancel downloads", - "cancelled": "Downloads cancelled.", - "chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it", - "chapterName": "Chapter: %{name}", - "completed": "Download completed", - "dbInfo": "Downloads are saved in a SQLite Database.", - "downloading": "Downloading", - "downloadingNovel": "Downloading: %{name}", - "downloadsLower": "downloads", - "noDownloads": "No downloads", - "pluginNotFound": "Plugin not found!", - "removeDownloadsWarning": "Are you sure? All downloaded chapters will be deleted." - }, - "generalSettings": "General", - "generalSettingsScreen": { - "asc": "(Ascending)", - "autoDownload": "Auto-download", - "bySource": "By source", - "chapterSort": "Default chapter sort", - "desc": "(Descending)", - "disableLoadingAnimations": "Disable loading animations", - "disableLoadingAnimationsDesc": "May improve performance on slower devices", - "disableHapticFeedback": "Disable haptic feedback", - "disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.", - "displayMode": "Display Mode", - "downloadNewChapters": "Download new chapters", - "epub": "EPUB", - "epubLocation": "EPUB Location", - "epubLocationDescription": "The place where you open and export your EPUB files.", - "globalUpdate": "Global update", - "gridSize": "Grid size", - "gridSizeDesc": "%{num} per row", - "itemsPerRow": "Items per row", - "itemsPerRowLibrary": "Items per row in library", - "jumpToLastReadChapter": "Jump to last read chapter in list", - "novel": "Novel", - "novelBadges": "Novel Badges", - "novelSort": "Novel Sort", - "refreshMetadata": "Automatically refresh metadata", - "refreshMetadataDescription": "Check for new cover and details when updating library", - "sortOrder": "Sort Order", - "updateLibrary": "Update library on launch", - "updateLibraryDesc": "Not recommended for low devices", - "updateOngoing": "Only update ongoing novels", - "updateTime": "Show last update time", - "useFAB": "Use FAB in Library" - }, - "globalSearch": { - "allSources": "all sources", - "searchIn": "Search a novel in" - }, - "history": "History", - "historyScreen": { - "chapter": "Chapter", - "clearHistorWarning": "Are you sure? All history will be lost.", - "deleted": "History deleted.", - "nothingReadRecently": "Nothing read recently", - "searchbar": "Search history" - }, - "library": "Library", - "libraryScreen": { - "bottomSheet": { - "display": { - "badges": "Badges", - "comfortable": "Comfortable grid", - "compact": "Compact grid", - "displayMode": "Display mode", - "download": "Download", - "downloadBadges": "Download badges", - "list": "List", - "noTitle": "Cover only grid", - "numberOfItems": "Number of Items", - "showNoOfItems": "Show number of items", - "unread": "Unread", - "unreadBadges": "Unread badges" - }, - "filters": { - "completed": "Completed", - "downloaded": "Downloaded", - "started": "Started", - "unread": "Unread" - }, - "sortOrders": { - "alphabetically": "Alphabetically", - "dateAdded": "Date added", - "download": "Downloaded", - "lastRead": "Last read", - "lastUpdated": "Last updated", - "totalChapters": "Total chapters", - "unread": "Unread" - } - }, - "empty": "Your library is empty. Add series to your library from Browse.", - "extraMenu": { - "importEpub": "Import Epub", - "openRandom": "Open Random Entry", - "updateCategory": "Update Category", - "updateLibrary": "Update Library" - }, - "searchbar": "Search library" - }, - "more": "More", - "moreScreen": { - "downloadOnly": "Downloaded only", - "downloadOnlyDesc": "Filters all novels in your library", - "downloadQueue": "Download queue", - "incognitoMode": "Incognito mode", - "incognitoModeDesc": "Pauses reading history" - }, - "novelScreen": { - "addToLibaray": "Add to library", - "bottomSheet": { - "displays": { - "chapterNumber": "Chapter number", - "sourceTitle": "Source title" - }, - "filters": { - "bookmarked": "Bookmarked", - "downloaded": "Downloaded", - "unread": "Unread" - }, - "order": { - "byChapterName": "By chapter name", - "bySource": "By source" - } - }, - "chapterChapnum": "Chapter %{num}", - "chapters": "chapters", - "continueReading": "Continue reading", - "exportEpubModal": { - "applyReaderTheme": "Apply reader theme to EPUB", - "customJSWarning": "Custom JS may not be supported by all EPUB readers", - "downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file", - "endChapter": "End Chapter", - "exportAll": "Export All Chapters", - "includeCustomCSS": "Include Custom CSS", - "includeCustomJS": "Include Custom JS", - "invalidRange": "Please enter valid chapter numbers", - "selectFolder": "Select destination folder for EPUB file", - "startChapter": "Start Chapter", - "startGreaterThanEnd": "Start chapter must be less than or equal to end chapter", - "title": "Export Novel as EPUB" - }, - "epub": { - "exportFailed": "Failed to export EPUB: %{error}", - "exportSuccess": "Successfully exported %{chapters} chapters as EPUB", - "noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.", - "noNovelSelected": "No novel selected for export" - }, - "coverSaved": "Cover saved", - "coverNotSaved": "Cover not saved", - "deleteChapterError": "Cant delete chapter chapter folder", - "deleteMessage": "Delete downloaded chapters?", - "deletedAllDownloads": "Deleted all Downloads", - "download": { - "custom": "Custom", - "customAmount": "Download custom amount", - "delete": "Delete downloads", - "next": "Next chapter", - "next10": "Next 10 chapter", - "next5": "Next 5 chapter", - "unread": "Unread" - }, - "edit": { - "addTag": "Add Tag", - "author": "Author: %{author}", - "cover": "Edit cover", - "info": "Edit info", - "status": "Status:", - "summary": "Description: %{summary}...", - "title": "Title: %{title}" - }, - "inLibaray": "In library", - "jumpToChapterModal": { - "chapterName": "Chapter Name", - "chapterNumber": "Chapter Number", - "error": { - "validChapterName": "Enter a valid chapter name", - "validChapterNumber": "Enter a valid chapter number" - }, - "jumpToChapter": "Jump to Chapter", - "openChapter": "Open Chapter" - }, - "migrate": "Migrate", - "noSummary": "No summary", - "noCoverFound": "No cover found", - "progress": "Progress %{progress} %", - "readChaptersDeleted": "Read chapters deleted", - "startReadingChapters": "Start reading %{name}", - "status": { - "cancelled": "Cancelled", - "completed": "Completed", - "licensed": "Licensed", - "onHiatus": "On Hiatus", - "ongoing": "Ongoing", - "publishingFinished": "Publishing Finished", - "unknown": "Unknown" - }, - "tracked": "Tracked", - "tracking": "Tracking", - "unknownStatus": "Unknown status", - "updatedToast": "Updated %{name}" - }, - "readerScreen": { - "bottomSheet": { - "allowTextSelection": "Text selection", - "autoscroll": "Auto-scroll", - "bionicReading": "Bionic reading", - "tapToScroll": "Tap to scroll", - "color": "Color", - "fontStyle": "Font style", - "fullscreen": "Fullscreen", - "lineHeight": "Line height", - "padding": "Padding", - "pageReader": "Paged reading (Experimental)", - "removeExtraSpacing": "Remove extra spacing", - "scrollAmount": "Scroll amount (screen height by default)", - "showBatteryAndTime": "Battery & time", - "showProgressPercentage": "Reading progress", - "swipeGestures": "Swipe between chapters", - "textAlign": "Text alignment", - "textSize": "Text size", - "useChapterDrawerSwipeNavigation": "Swipe to open drawer", - "verticalSeekbar": "Vertical seekbar", - "keepScreenOn": "Keep screen on", - "volumeButtonsScroll": "Volume button scrolling" - }, - "drawer": { - "scrollToBottom": "Scroll to bottom", - "scrollToCurrentChapter": "Scroll to current chapter", - "scrollToTop": "Scroll to top" - }, - "emptyChapterMessage": "

    Chapter is empty.

    Report on GitHub if it's available in WebView.

    Plugin: %{pluginId}

    Novel: %{novelName}

    Chapter: %{chapterName}

    ", - "finished": "Finished", - "nextChapter": "Next: %{name}", - "noNextChapter": "There's no next chapter", - "noPreviousChapter": "There's no previous chapter" - }, - "readerSettings": { - "autoScrollInterval": "Scroll interval (seconds)", - "autoScrollOffset": "Scroll offset (screen heights)", - "backgroundColor": "Background color", - "backgroundColorModal": "Background color", - "clearCustomCSS": "Reset your custom CSS?", - "clearCustomJS": "Reset your custom JS?", - "cssHint": "Target specific sources using #sourceId-[SOURCEID] in your selectors", - "customCSS": "Custom CSS", - "customJS": "Custom JS", - "deleteCustomTheme": "Delete theme", - "jsHint": "Available variables: html, novelName, chapterName, sourceId, chapterId, novelId", - "navigationControls": "Navigation Controls", - "notSaved": "Not saved", - "openCSSFile": "Import CSS file", - "openJSFile": "Import JS file", - "preset": "Preset", - "readingMode": "Reading Mode", - "readerTheme": "Theme", - "saveCustomTheme": "Save theme", - "textColor": "Text color", - "textColorModal": "Text color", - "title": "Reader", - "verticalSeekbarDesc": "Use vertical seekbar" - }, - "sourceScreen": { - "noResultsFound": "No results found" - }, - "statsScreen": { - "downloadedChapters": "Downloaded chapters", - "genreDistribution": "Genre distribution", - "readChapters": "Read chapters", - "sources": "Sources", - "statusDistribution": "Status distribution", - "title": "Statistics", - "titlesInLibrary": "Titles in library", - "totalChapters": "Total chapters", - "unreadChapters": "Unread chapters" - }, - "tracking": "Tracking", - "trackingScreen": { - "logOutMessage": "Log out from %{name}?", - "revalidate": "Revalidate", - "services": "Services" - }, - "updates": "Updates", - "updatesScreen": { - "deletedChapters": "Deleted %{num} chapters", - "emptyView": "No recent updates", - "lastUpdatedAt": "Library last updated:", - "libraryUpdated": "Library Updated", - "newChapters": "new Chapters", - "novelsUpdated": "%{num} novels updated", - "searchbar": "Search updates", - "unableToGetNovel": "Unable to get novel", - "updatesLower": "updates", - "updatingLibrary": "Updating library" - }, - "onboardingScreen": { - "welcome": "Welcome", - "pickATheme": "Pick a theme", - "light": "Light", - "dark": "Dark", - "system": "System", - "complete": "Complete" - }, - "notifications": { - "IMPORT_EPUB": "Importing EPUB", - "UPDATE_LIBRARY": "Updating Library", - "DRIVE_BACKUP": "Google Drive Backup", - "DRIVE_RESTORE": "Google Drive Restore", - "SELF_HOST_BACKUP": "Self-Host Backup", - "SELF_HOST_RESTORE": "Self-Host Restore", - "LOCAL_BACKUP": "Local Backup", - "LOCAL_RESTORE": "Local Restore", - "MIGRATE_NOVEL": "Migrating Novel", - "DOWNLOAD_CHAPTER": "Downloading Chapter" - } -} diff --git a/strings/languages/sv_SE/strings.json b/strings/languages/sv_SE/strings.json deleted file mode 100644 index 1896da6be..000000000 --- a/strings/languages/sv_SE/strings.json +++ /dev/null @@ -1,574 +0,0 @@ -{ - "aboutScreen": { - "website": "Website", - "discord": "Discord", - "github": "Github", - "helpTranslate": "Help translate", - "plugins": "Plugins", - "version": "Version", - "whatsNew": "What's new" - }, - "advancedSettings": "Advanced", - "advancedSettingsScreen": { - "cachedNovelsDeletedToast": "Cached novels deleted", - "chapterInsertFailed": "chapter insert failed", - "clearCachedNovels": "Clear cached novels", - "clearCachedNovelsDesc": "Delete cached novels which not in your library", - "clearDatabaseWarning": "Read and Downloaded chapters and progress of non-library novels will be lost.", - "clearUpdatesMessage": "Updates cleared.", - "clearUpdatesTab": "Clear updates tab", - "clearUpdatesWarning": "Updates tab will be cleared.", - "clearupdatesTabDesc": "Clears chapter entries in updates tab", - "dataManagement": "Data Management", - "deleteReadChapters": "Delete read chapters", - "deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.", - "importEpub": "Import Epub", - "importNovel": "Import Novel", - "importStaticFiles": "Import Static Files", - "novelInsertFailed": "novel insert failed", - "useFAB": "Use FAB instead of button", - "userAgent": "User Agent", - "recreateDBIndexes": "Recreate DB indexes", - "recreateDBIndexesToast": "Recreated DB indexes", - "recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.", - "recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices." - }, - "appearance": "Appearance", - "appearanceScreen": { - "accentColor": "Accent Color", - "alwaysShowNavLabels": "Always show nav labels", - "appLanguage": "App language", - "languagePickerModal": { - "title": "Select Language", - "restartNote": "You will need to restart the app for the language change to take full effect." - }, - "appLanguageDefault": "Default", - "appTheme": "App theme", - "darkTheme": "Dark Theme", - "hideBackdrop": "Hide backdrop", - "lightTheme": "Light Theme", - "navbar": "Navbar", - "novelInfo": "Novel info", - "pureBlackDarkMode": "Pure black dark mode", - "showHistoryInTheNav": "Show history in the nav", - "showUpdatesInTheNav": "Show updates in the nav", - "themeMode": "Theme mode", - "themeModeLight": "Light", - "themeModeDark": "Dark", - "themeModeSystem": "System", - "theme": { - "default": "Default", - "lavender": "Lavender", - "midnightDusk": "Midnight Dusk", - "daybreakBloom": "Daybreak Bloom", - "strawberry": "Strawberry Daiquiri", - "tako": "Tako", - "teal": "Teal", - "turquoise": "Turquoise", - "yotsuba": "Yotsuba", - "catppuccin": "Catppuccin", - "yinyang": "Yin & Yang" - } - }, - "backupScreen": { - "backupName": "Backup name", - "backupCreated": "Backup created successfully", - "backupRestored": "Backup restored successfully", - "savingBackup": "Saving Backup", - "categoriesRestored": "Restored %{count} categories", - "categoriesRestoredWithErrors": "Restored %{count} categories (%{failedCount} failed)", - "categoryRestoreFailed": "Failed to restore category: %{categoryName} - %{error}", - "categoryFileNotFound": "Category file not found in backup", - "categoryFileReadFailed": "Failed to read category file: %{error}", - "categoryFileWriteFailed": "Failed to write category file: %{error}", - "createBackup": "Create backup", - "createBackupDesc": "Can be used to restore current library", - "createBackupWarning": "Create backup may not work on devices with Android 9 or lower.", - "downloadingData": "Downloading Data", - "downloadingDownloadedFiles": "Downloading Downloaded Files", - "failed": "Backup failed", - "novelsRestored": "Restored %{count} novels", - "novelsRestoredWithErrors": "Restored %{count} novels (%{failedCount} failed)", - "novelBackupFailed": "Failed to backup novel: %{novelName} - %{error}", - "novelRestoreFailed": "Failed to restore novel: %{novelName} - %{error}", - "novelDirectoryNotFound": "Novel directory not found in backup", - "novelDirectoryReadFailed": "Failed to read novel directory: %{error}", - "restoringCategories": "Restoring Categories", - "restoringNovels": "Restoring Novels", - "restoringSettings": "Restoring Settings", - "settingsRestored": "Settings restored", - "settingsFileNotFound": "Settings file not found in backup", - "settingsRestoreFailed": "Failed to restore settings: %{error}", - "settingsFileWriteFailed": "Failed to write settings file: %{error}", - "versionFileWriteFailed": "Failed to write version file: %{error}", - "drive": { - "backup": "Drive Backup", - "backupInterruped": "Drive Backup Interrupted", - "googleDriveBackup": "Google Drive Backup", - "restore": "Drive Restore", - "restoreInterruped": "Drive Restore Interrupted" - }, - "googeDrive": "Googe Drive", - "googeDriveDesc": "Backup to your Google Drive", - "invalidBackupFolder": "Invalid backup folder", - "localBackup": "Local Backup", - "noBackupFound": "No backup found", - "preparingData": "Preparing Data", - "remote": { - "backup": "Self Host Backup", - "host": "Host", - "unknownHost": "Unknown host" - }, - "remoteBackup": "Remote Backup", - "restoreBackup": "Restore backup", - "restoreBackupDesc": "Restore library from backup file", - "restoreLargeBackupsWarning": "Restoring large backups may freeze the app until restoring is finished", - "restorinBackup": "Restoring backup", - "restoringData": "Restoring Data", - "selfHost": "Self Host", - "selfHostDesc": "Backup to your server", - "uploadingData": "Uploading Data", - "uploadingDownloadedFiles": "Uploading Downloaded Files" - }, - "browse": "Browse", - "browseScreen": { - "addedToLibrary": "Added to library", - "available": "Available", - "deletePluginMessage": "Are you sure you want to uninstall %{name}?", - "discover": "Discover", - "globalSearch": "Global search", - "installFailed": "Installation failed: %{name}", - "installed": "Installed", - "installedPlugin": "Installed %{name}", - "installedPlugins": "Installed plugins", - "lastUsed": "Last used", - "latest": "Latest", - "listEmpty": "Enable languages from settings", - "migration": { - "dialogMessage": "Migrate %{url}?", - "novelAlreadyInLibrary": "Novel already in library", - "selectSource": "Select Source", - "selectSourceDesc": "Select a Source To Migrate From" - }, - "noSource": "Your library does not have any novels from this source", - "pinnedPlugin": "Pinned %{name}", - "pinnedPlugins": "Pinned plugins", - "removeFromLibrary": "Removed from library", - "searchbar": "Search sources", - "searchResults": "Search results", - "selectNovel": "Select Novel", - "uninstalledPlugin": "Uninstalled %{name}", - "unpinnedPlugin": "Unpinned %{name}", - "updateFailed": "Update failed", - "updatedTo": "Updated to %{version}", - "settings": { - "title": "Plugin Settings", - "description": "Fill in the plugin settings. Restart app to apply the settings." - } - }, - "browseSettings": "Browse Settings", - "browseSettingsScreen": { - "concurrentSearches": "Concurrent Source Searches", - "multi": "Multi", - "languages": "Languages" - }, - "categories": { - "addCategories": "Add category", - "cantDeleteDefault": "You cant delete default category", - "default": "Default", - "defaultCategory": "Default category", - "deleteModal": { - "desc": "Do you wish to delete category", - "header": "Delete category" - }, - "duplicateError": "A category with this name already exists!", - "editCategories": "Rename category", - "emptyMsg": "You have no categories. Tap the plus button to create one for organizing your library", - "header": "Edit categories", - "local": "Local", - "setCategories": "Set categories", - "setModalEmptyMsg": "You have no categories. Tap the Edit button to create one for organizing your library" - }, - "repositories": { - "emptyMsg": "You have no repositories. Add your first plugin repository to get started." - }, - "common": { - "about": "About", - "add": "Add", - "all": "All", - "backup": "Backup", - "cancel": "Cancel", - "categories": "Categories", - "chapters": "Chapters", - "clear": "Clear", - "copiedToClipboard": "Copied to clipboard: %{name}", - "delete": "Delete", - "deleted": "Deleted %{name}", - "deprecated": "Deprecated", - "display": "Display", - "done": "Done", - "downloads": "Downloads", - "edit": "Edit", - "example": "Example", - "filter": "Filter", - "globally": "globally", - "install": "Install", - "logout": "Logout", - "name": "Name", - "newUpdateAvailable": "New update available", - "ok": "Ok", - "pause": "Pause", - "preparing": "Preparing", - "remove": "Remove", - "reset": "Reset", - "restore": "Restore", - "resume": "Resume", - "retry": "Retry", - "save": "Save", - "search": "Search", - "searchFor": "Search for", - "searchResults": "Search results", - "settings": "Settings", - "show": "Show", - "signIn": "Sign in", - "signOut": "Sign out", - "sort": "Sort", - "submit": "Submit", - "loading": "Loading ...", - "warning": "Warning" - }, - "webview": { - "refresh": "Refresh", - "share": "Share", - "openInBrowser": "Open in browser", - "clearCookies": "Clear cookies", - "cookiesCleared": "Cookies cleared", - "clearData": "Clear WebView data", - "dataDeleted": "WebView data cleared" - }, - "date": { - "calendar": { - "lastDay": "[Yesterday]", - "lastWeek": "[Last] dddd", - "nextDay": "[Tomorrow]", - "sameDay": "[Today]" - } - }, - "downloadScreen": { - "cancelDownloads": "Cancel downloads", - "cancelled": "Downloads cancelled.", - "chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it", - "chapterName": "Chapter: %{name}", - "completed": "Download completed", - "dbInfo": "Downloads are saved in a SQLite Database.", - "downloading": "Downloading", - "downloadingNovel": "Downloading: %{name}", - "downloadsLower": "downloads", - "noDownloads": "No downloads", - "pluginNotFound": "Plugin not found!", - "removeDownloadsWarning": "Are you sure? All downloaded chapters will be deleted." - }, - "generalSettings": "General", - "generalSettingsScreen": { - "asc": "(Ascending)", - "autoDownload": "Auto-download", - "bySource": "By source", - "chapterSort": "Default chapter sort", - "desc": "(Descending)", - "disableLoadingAnimations": "Disable loading animations", - "disableLoadingAnimationsDesc": "May improve performance on slower devices", - "disableHapticFeedback": "Disable haptic feedback", - "disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.", - "displayMode": "Display Mode", - "downloadNewChapters": "Download new chapters", - "epub": "EPUB", - "epubLocation": "EPUB Location", - "epubLocationDescription": "The place where you open and export your EPUB files.", - "globalUpdate": "Global update", - "gridSize": "Grid size", - "gridSizeDesc": "%{num} per row", - "itemsPerRow": "Items per row", - "itemsPerRowLibrary": "Items per row in library", - "jumpToLastReadChapter": "Jump to last read chapter in list", - "novel": "Novel", - "novelBadges": "Novel Badges", - "novelSort": "Novel Sort", - "refreshMetadata": "Automatically refresh metadata", - "refreshMetadataDescription": "Check for new cover and details when updating library", - "sortOrder": "Sort Order", - "updateLibrary": "Update library on launch", - "updateLibraryDesc": "Not recommended for low devices", - "updateOngoing": "Only update ongoing novels", - "updateTime": "Show last update time", - "useFAB": "Use FAB in Library" - }, - "globalSearch": { - "allSources": "all sources", - "searchIn": "Search a novel in" - }, - "history": "History", - "historyScreen": { - "chapter": "Chapter", - "clearHistorWarning": "Are you sure? All history will be lost.", - "deleted": "History deleted.", - "nothingReadRecently": "Nothing read recently", - "searchbar": "Search history" - }, - "library": "Library", - "libraryScreen": { - "bottomSheet": { - "display": { - "badges": "Badges", - "comfortable": "Comfortable grid", - "compact": "Compact grid", - "displayMode": "Display mode", - "download": "Download", - "downloadBadges": "Download badges", - "list": "List", - "noTitle": "Cover only grid", - "numberOfItems": "Number of Items", - "showNoOfItems": "Show number of items", - "unread": "Unread", - "unreadBadges": "Unread badges" - }, - "filters": { - "completed": "Completed", - "downloaded": "Downloaded", - "started": "Started", - "unread": "Unread" - }, - "sortOrders": { - "alphabetically": "Alphabetically", - "dateAdded": "Date added", - "download": "Downloaded", - "lastRead": "Last read", - "lastUpdated": "Last updated", - "totalChapters": "Total chapters", - "unread": "Unread" - } - }, - "empty": "Your library is empty. Add series to your library from Browse.", - "extraMenu": { - "importEpub": "Import Epub", - "openRandom": "Open Random Entry", - "updateCategory": "Update Category", - "updateLibrary": "Update Library" - }, - "searchbar": "Search library" - }, - "more": "More", - "moreScreen": { - "downloadOnly": "Downloaded only", - "downloadOnlyDesc": "Filters all novels in your library", - "downloadQueue": "Download queue", - "incognitoMode": "Incognito mode", - "incognitoModeDesc": "Pauses reading history" - }, - "novelScreen": { - "addToLibaray": "Add to library", - "bottomSheet": { - "displays": { - "chapterNumber": "Chapter number", - "sourceTitle": "Source title" - }, - "filters": { - "bookmarked": "Bookmarked", - "downloaded": "Downloaded", - "unread": "Unread" - }, - "order": { - "byChapterName": "By chapter name", - "bySource": "By source" - } - }, - "chapterChapnum": "Chapter %{num}", - "chapters": "chapters", - "continueReading": "Continue reading", - "exportEpubModal": { - "applyReaderTheme": "Apply reader theme to EPUB", - "customJSWarning": "Custom JS may not be supported by all EPUB readers", - "downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file", - "endChapter": "End Chapter", - "exportAll": "Export All Chapters", - "includeCustomCSS": "Include Custom CSS", - "includeCustomJS": "Include Custom JS", - "invalidRange": "Please enter valid chapter numbers", - "selectFolder": "Select destination folder for EPUB file", - "startChapter": "Start Chapter", - "startGreaterThanEnd": "Start chapter must be less than or equal to end chapter", - "title": "Export Novel as EPUB" - }, - "epub": { - "exportFailed": "Failed to export EPUB: %{error}", - "exportSuccess": "Successfully exported %{chapters} chapters as EPUB", - "noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.", - "noNovelSelected": "No novel selected for export" - }, - "coverSaved": "Cover saved", - "coverNotSaved": "Cover not saved", - "deleteChapterError": "Cant delete chapter chapter folder", - "deleteMessage": "Delete downloaded chapters?", - "deletedAllDownloads": "Deleted all Downloads", - "download": { - "custom": "Custom", - "customAmount": "Download custom amount", - "delete": "Delete downloads", - "next": "Next chapter", - "next10": "Next 10 chapter", - "next5": "Next 5 chapter", - "unread": "Unread" - }, - "edit": { - "addTag": "Add Tag", - "author": "Author: %{author}", - "cover": "Edit cover", - "info": "Edit info", - "status": "Status:", - "summary": "Description: %{summary}...", - "title": "Title: %{title}" - }, - "inLibaray": "In library", - "jumpToChapterModal": { - "chapterName": "Chapter Name", - "chapterNumber": "Chapter Number", - "error": { - "validChapterName": "Enter a valid chapter name", - "validChapterNumber": "Enter a valid chapter number" - }, - "jumpToChapter": "Jump to Chapter", - "openChapter": "Open Chapter" - }, - "migrate": "Migrate", - "noSummary": "No summary", - "noCoverFound": "No cover found", - "progress": "Progress %{progress} %", - "readChaptersDeleted": "Read chapters deleted", - "startReadingChapters": "Start reading %{name}", - "status": { - "cancelled": "Cancelled", - "completed": "Completed", - "licensed": "Licensed", - "onHiatus": "On Hiatus", - "ongoing": "Ongoing", - "publishingFinished": "Publishing Finished", - "unknown": "Unknown" - }, - "tracked": "Tracked", - "tracking": "Tracking", - "unknownStatus": "Unknown status", - "updatedToast": "Updated %{name}" - }, - "readerScreen": { - "bottomSheet": { - "allowTextSelection": "Text selection", - "autoscroll": "Auto-scroll", - "bionicReading": "Bionic reading", - "tapToScroll": "Tap to scroll", - "color": "Color", - "fontStyle": "Font style", - "fullscreen": "Fullscreen", - "lineHeight": "Line height", - "padding": "Padding", - "pageReader": "Paged reading (Experimental)", - "removeExtraSpacing": "Remove extra spacing", - "scrollAmount": "Scroll amount (screen height by default)", - "showBatteryAndTime": "Battery & time", - "showProgressPercentage": "Reading progress", - "swipeGestures": "Swipe between chapters", - "textAlign": "Text alignment", - "textSize": "Text size", - "useChapterDrawerSwipeNavigation": "Swipe to open drawer", - "verticalSeekbar": "Vertical seekbar", - "keepScreenOn": "Keep screen on", - "volumeButtonsScroll": "Volume button scrolling" - }, - "drawer": { - "scrollToBottom": "Scroll to bottom", - "scrollToCurrentChapter": "Scroll to current chapter", - "scrollToTop": "Scroll to top" - }, - "emptyChapterMessage": "

    Chapter is empty.

    Report on GitHub if it's available in WebView.

    Plugin: %{pluginId}

    Novel: %{novelName}

    Chapter: %{chapterName}

    ", - "finished": "Finished", - "nextChapter": "Next: %{name}", - "noNextChapter": "There's no next chapter", - "noPreviousChapter": "There's no previous chapter" - }, - "readerSettings": { - "autoScrollInterval": "Scroll interval (seconds)", - "autoScrollOffset": "Scroll offset (screen heights)", - "backgroundColor": "Background color", - "backgroundColorModal": "Background color", - "clearCustomCSS": "Reset your custom CSS?", - "clearCustomJS": "Reset your custom JS?", - "cssHint": "Target specific sources using #sourceId-[SOURCEID] in your selectors", - "customCSS": "Custom CSS", - "customJS": "Custom JS", - "deleteCustomTheme": "Delete theme", - "jsHint": "Available variables: html, novelName, chapterName, sourceId, chapterId, novelId", - "navigationControls": "Navigation Controls", - "notSaved": "Not saved", - "openCSSFile": "Import CSS file", - "openJSFile": "Import JS file", - "preset": "Preset", - "readingMode": "Reading Mode", - "readerTheme": "Theme", - "saveCustomTheme": "Save theme", - "textColor": "Text color", - "textColorModal": "Text color", - "title": "Reader", - "verticalSeekbarDesc": "Use vertical seekbar" - }, - "sourceScreen": { - "noResultsFound": "No results found" - }, - "statsScreen": { - "downloadedChapters": "Downloaded chapters", - "genreDistribution": "Genre distribution", - "readChapters": "Read chapters", - "sources": "Sources", - "statusDistribution": "Status distribution", - "title": "Statistics", - "titlesInLibrary": "Titles in library", - "totalChapters": "Total chapters", - "unreadChapters": "Unread chapters" - }, - "tracking": "Tracking", - "trackingScreen": { - "logOutMessage": "Log out from %{name}?", - "revalidate": "Revalidate", - "services": "Services" - }, - "updates": "Updates", - "updatesScreen": { - "deletedChapters": "Deleted %{num} chapters", - "emptyView": "No recent updates", - "lastUpdatedAt": "Library last updated:", - "libraryUpdated": "Library Updated", - "newChapters": "new Chapters", - "novelsUpdated": "%{num} novels updated", - "searchbar": "Search updates", - "unableToGetNovel": "Unable to get novel", - "updatesLower": "updates", - "updatingLibrary": "Updating library" - }, - "onboardingScreen": { - "welcome": "Welcome", - "pickATheme": "Pick a theme", - "light": "Light", - "dark": "Dark", - "system": "System", - "complete": "Complete" - }, - "notifications": { - "IMPORT_EPUB": "Importing EPUB", - "UPDATE_LIBRARY": "Updating Library", - "DRIVE_BACKUP": "Google Drive Backup", - "DRIVE_RESTORE": "Google Drive Restore", - "SELF_HOST_BACKUP": "Self-Host Backup", - "SELF_HOST_RESTORE": "Self-Host Restore", - "LOCAL_BACKUP": "Local Backup", - "LOCAL_RESTORE": "Local Restore", - "MIGRATE_NOVEL": "Migrating Novel", - "DOWNLOAD_CHAPTER": "Downloading Chapter" - } -} diff --git a/__mocks__/database.js b/test/mocks/database.js similarity index 90% rename from __mocks__/database.js rename to test/mocks/database.js index f023116ec..00c509a8b 100644 --- a/__mocks__/database.js +++ b/test/mocks/database.js @@ -1,4 +1,5 @@ jest.mock('@database/queries/NovelQueries', () => ({ + getNovelById: jest.fn(), getNovelByPath: jest.fn(), deleteCachedNovels: jest.fn(), getCachedNovels: jest.fn(), @@ -30,9 +31,13 @@ jest.mock('@database/queries/ChapterQueries', () => ({ insertChapters: jest.fn(), getCustomPages: jest.fn(), getChapterCount: jest.fn(), + getChapterCountSync: jest.fn(), getPageChaptersBatched: jest.fn(), + getNovelChaptersSync: jest.fn(), getFirstUnreadChapter: jest.fn(), updateChapterProgress: jest.fn(), + getNovelScanlators: jest.fn(() => []), + getNovelScanlatorsSync: jest.fn(() => []), })); jest.mock('@database/queries/HistoryQueries', () => ({ @@ -44,6 +49,7 @@ jest.mock('@database/queries/HistoryQueries', () => ({ jest.mock('@database/queries/LibraryQueries', () => ({ getLibraryNovelsFromDb: jest.fn(), + getLibraryNovelsQuery: jest.fn(), getLibraryWithCategory: jest.fn(), })); diff --git a/test/mocks/fileMock.js b/test/mocks/fileMock.js new file mode 100644 index 000000000..86059f362 --- /dev/null +++ b/test/mocks/fileMock.js @@ -0,0 +1 @@ +module.exports = 'test-file-stub'; diff --git a/__mocks__/index.js b/test/mocks/index.js similarity index 100% rename from __mocks__/index.js rename to test/mocks/index.js diff --git a/test/mocks/nativeModules.js b/test/mocks/nativeModules.js new file mode 100644 index 000000000..9ba7dbebe --- /dev/null +++ b/test/mocks/nativeModules.js @@ -0,0 +1,58 @@ +// require('react-native-gesture-handler/jestSetup'); +// require('react-native-reanimated').setUpTests(); + +jest.mock('@modules/native-file', () => ({ + __esModule: true, + default: { + DocumentDirectoryPath: '/mock/documents', + ExternalDirectoryPath: '/mock/external', + ExternalCachesDirectoryPath: '/mock/caches', + writeFile: jest.fn(), + readFile: jest.fn(() => ''), + copyFile: jest.fn(), + copyFileToDirectory: jest.fn(() => + Promise.resolve({ uri: '/mock/export.epub', size: 1 }), + ), + pickDirectory: jest.fn(() => + Promise.resolve({ uri: '/mock/export', name: 'export' }), + ), + moveFile: jest.fn(), + exists: jest.fn(() => true), + mkdir: jest.fn(), + unlink: jest.fn(), + readDir: jest.fn(() => []), + downloadFile: jest.fn().mockResolvedValue(), + }, +})); + +const mockEpubNovel = { + name: 'Mock Novel', + cover: null, + summary: null, + author: null, + artist: null, + chapters: [], + cssPaths: [], + imagePaths: [], +}; + +global.mockEpubNovel = mockEpubNovel; + +jest.mock('@modules/native-volume-button-listener', () => ({ + __esModule: true, + default: { + addListener: jest.fn(() => ({ remove: jest.fn() })), + removeListeners: jest.fn(), + setActive: jest.fn(), + }, +})); + +jest.mock('@modules/native-zip-archive', () => ({ + __esModule: true, + default: { + zip: jest.fn().mockResolvedValue(), + unzip: jest.fn().mockResolvedValue(), + remoteUnzip: jest.fn().mockResolvedValue(), + remoteZip: jest.fn().mockResolvedValue(''), + }, +})); diff --git a/test/mocks/react-native-nitro-modules.js b/test/mocks/react-native-nitro-modules.js new file mode 100644 index 000000000..d687ab035 --- /dev/null +++ b/test/mocks/react-native-nitro-modules.js @@ -0,0 +1,33 @@ +const mockSubscription = { remove: jest.fn() }; +const mockTtsSession = { + load: jest.fn(async () => undefined), + play: jest.fn(async () => undefined), + pause: jest.fn(async () => undefined), + stop: jest.fn(async () => undefined), + skipPrevious: jest.fn(async () => undefined), + skipNext: jest.fn(async () => undefined), + replayCurrent: jest.fn(async () => undefined), + seekTo: jest.fn(async () => undefined), + updateSettings: jest.fn(async () => undefined), + addOnStateChangedListener: jest.fn(() => mockSubscription), + addOnProgressChangedListener: jest.fn(() => mockSubscription), + addOnErrorListener: jest.fn(() => mockSubscription), +}; + +jest.mock('react-native-nitro-modules', () => ({ + __esModule: true, + NitroModules: { + createHybridObject: jest.fn(name => { + if (name === 'TtsFactory') { + return { + createSession: jest.fn(async () => mockTtsSession), + getEngines: jest.fn(async () => []), + getVoices: jest.fn(async () => []), + }; + } + return { + parseNovelAndChapters: jest.fn(() => global.mockEpubNovel), + }; + }), + }, +})); diff --git a/__mocks__/react-navigation.js b/test/mocks/react-navigation.js similarity index 100% rename from __mocks__/react-navigation.js rename to test/mocks/react-navigation.js diff --git a/__tests__/jest.setup.ts b/test/setup/jest.ts similarity index 100% rename from __tests__/jest.setup.ts rename to test/setup/jest.ts diff --git a/__tests-modules__/test-utils.tsx b/test/test-utils.tsx similarity index 82% rename from __tests-modules__/test-utils.tsx rename to test/test-utils.tsx index 9e16ced51..ce0305fa0 100644 --- a/__tests-modules__/test-utils.tsx +++ b/test/test-utils.tsx @@ -4,6 +4,7 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { Provider as PaperProvider } from 'react-native-paper'; import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; +import { ThemeProvider } from '@hooks/persisted/useTheme'; import AppErrorBoundary from '@components/AppErrorBoundary/AppErrorBoundary'; import { NovelContextProvider } from '@screens/novel/NovelContext'; @@ -13,11 +14,13 @@ const AllTheProviders = ({ children }: { children: React.ReactElement }) => { return ( - - - {children} - - + + + + {children} + + + ); diff --git a/tsconfig.json b/tsconfig.json index 944a7e841..11cb4c204 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@react-native/typescript-config", + "extends": "expo/tsconfig.base", "include": ["**/*.ts", "**/*.tsx"], "exclude": [ "**/node_modules", @@ -14,7 +14,6 @@ "module": "ES2022", "lib": ["ES2022", "DOM"], "target": "ES2022", - "baseUrl": ".", "paths": { "@components": ["./src/components/index"], "@components/*": ["./src/components/*"], @@ -22,7 +21,7 @@ "@hooks/*": ["./src/hooks/*"], "@hooks": ["./src/hooks/index"], "@screens/*": ["./src/screens/*"], - "@strings/*": ["./strings/*"], + "@i18n/*": ["./src/i18n/*"], "@theme/*": ["./src/theme/*"], "@utils/*": ["./src/utils/*"], "@plugins/*": ["./src/plugins/*"], @@ -32,7 +31,11 @@ "@api/*": ["./src/api/*"], "@type/*": ["./src/type/*"], "@specs/*": ["./specs/*"], - "@test-utils": ["./__tests-modules__/test-utils"], + "@test-utils": ["./test/test-utils"], + "@env": ["./src/generated/build-info"], + "@modules/nitro-epub": ["./modules/nitro-epub/src/index"], + "@modules/nitro-tts": ["./modules/nitro-tts/src/index"], + "@modules/*": ["./modules/*"] }, "types": ["react-native", "jest"] } diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo deleted file mode 100644 index 1f0d273d2..000000000 --- a/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"fileNames":["./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/.pnpm/drizzle-kit@1.0.0-beta.13-f728631/node_modules/drizzle-kit/index.d.mts","./drizzle.config.ts","./env.d.ts","./__mocks__/react-navigation.ts","./__tests__/jest.setup.ts","./src/plugins/types/filterTypes.ts","./src/plugins/types/index.ts","./src/database/types/index.ts","./src/database/constants.ts","./node_modules/.pnpm/expo-localization@17.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_r_d4ceac9a56e4f1f21f9b0bc5c97d2953/node_modules/expo-localization/build/Localization.types.d.ts","./node_modules/.pnpm/expo-localization@17.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_r_d4ceac9a56e4f1f21f9b0bc5c97d2953/node_modules/expo-localization/build/Localization.d.ts","./node_modules/.pnpm/dayjs@1.11.19/node_modules/dayjs/locale/types.d.ts","./node_modules/.pnpm/dayjs@1.11.19/node_modules/dayjs/locale/index.d.ts","./node_modules/.pnpm/dayjs@1.11.19/node_modules/dayjs/index.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/typing.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/Locales.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/Pluralization.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/MissingTranslation.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/camelCaseKeys.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/createTranslationOptions.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/expandRoundMode.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/formatNumber.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/getFullScope.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/inferType.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/interpolate.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/isSet.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/lookup.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/numberToDelimited.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/numberToHuman.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/numberToHumanSize.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/parseDate.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/pluralize.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/roundNumber.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/strftime.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/timeAgoInWords.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/helpers/index.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/I18n.d.ts","./node_modules/.pnpm/i18n-js@4.5.2/node_modules/i18n-js/typings/index.d.ts","./node_modules/.pnpm/react-native-mmkv@3.3.3_react-native@0.81.6_@babel+core@7.29.0_@react-native-community+_5fce81f557aeac0e803cdccc1d4ee265/node_modules/react-native-mmkv/lib/typescript/src/Types.d.ts","./node_modules/.pnpm/react-native-mmkv@3.3.3_react-native@0.81.6_@babel+core@7.29.0_@react-native-community+_5fce81f557aeac0e803cdccc1d4ee265/node_modules/react-native-mmkv/lib/typescript/src/MMKV.d.ts","./node_modules/.pnpm/react-native-mmkv@3.3.3_react-native@0.81.6_@babel+core@7.29.0_@react-native-community+_5fce81f557aeac0e803cdccc1d4ee265/node_modules/react-native-mmkv/lib/typescript/src/hooks.d.ts","./node_modules/.pnpm/react-native-mmkv@3.3.3_react-native@0.81.6_@babel+core@7.29.0_@react-native-community+_5fce81f557aeac0e803cdccc1d4ee265/node_modules/react-native-mmkv/lib/typescript/src/index.d.ts","./src/utils/mmkv/mmkv.ts","./node_modules/.pnpm/dayjs@1.11.19/node_modules/dayjs/plugin/customParseFormat.d.ts","./node_modules/.pnpm/dayjs@1.11.19/node_modules/dayjs/plugin/localeData.d.ts","./node_modules/.pnpm/dayjs@1.11.19/node_modules/dayjs/plugin/localizedFormat.d.ts","./node_modules/.pnpm/dayjs@1.11.19/node_modules/dayjs/plugin/relativeTime.d.ts","./node_modules/.pnpm/dayjs@1.11.19/node_modules/dayjs/plugin/calendar.d.ts","./strings/languages/af_ZA/strings.json","./strings/languages/ar_SA/strings.json","./strings/languages/as_IN/strings.json","./strings/languages/ca_ES/strings.json","./strings/languages/cs_CZ/strings.json","./strings/languages/da_DK/strings.json","./strings/languages/de_DE/strings.json","./strings/languages/el_GR/strings.json","./strings/languages/en/strings.json","./strings/languages/es_ES/strings.json","./strings/languages/fi_FI/strings.json","./strings/languages/fr_FR/strings.json","./strings/languages/he_IL/strings.json","./strings/languages/hi_IN/strings.json","./strings/languages/hu_HU/strings.json","./strings/languages/id_ID/strings.json","./strings/languages/it_IT/strings.json","./strings/languages/ja_JP/strings.json","./strings/languages/ko_KR/strings.json","./strings/languages/nl_NL/strings.json","./strings/languages/no_NO/strings.json","./strings/languages/or_IN/strings.json","./strings/languages/pl_PL/strings.json","./strings/languages/pt_PT/strings.json","./strings/languages/pt_BR/strings.json","./strings/languages/ro_RO/strings.json","./strings/languages/ru_RU/strings.json","./strings/languages/sq_AL/strings.json","./strings/languages/sr_SP/strings.json","./strings/languages/sv_SE/strings.json","./strings/languages/tr_TR/strings.json","./strings/languages/uk_UA/strings.json","./strings/languages/vi_VN/strings.json","./strings/languages/zh_CN/strings.json","./strings/languages/zh_TW/strings.json","./strings/types/index.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/types/modules/BatchedBridge.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/vendor/emitter/EventEmitter.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/types/modules/Codegen.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/types/modules/Devtools.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/vendor/core/ErrorUtils.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/src/types/globals.d.ts","./node_modules/.pnpm/@types+react@19.1.17/node_modules/@types/react/global.d.ts","./node_modules/.pnpm/csstype@3.2.3/node_modules/csstype/index.d.ts","./node_modules/.pnpm/@types+react@19.1.17/node_modules/@types/react/index.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/types/private/Utilities.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/types/public/Insets.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/types/public/ReactNativeTypes.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Types/CoreEventTypes.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/types/public/ReactNativeRenderer.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/Touchable/Touchable.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/View/ViewAccessibility.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/View/ViewPropTypes.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/RefreshControl/RefreshControl.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/View/View.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/ScrollView/ScrollView.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Image/ImageResizeMode.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Image/ImageSource.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Image/Image.d.ts","./node_modules/.pnpm/@react-native+virtualized-lists@0.81.6_@types+react@19.1.17_react-native@0.81.6_@babel+_b4909423bbc87f4e48ec18063a95f13a/node_modules/@react-native/virtualized-lists/Lists/VirtualizedList.d.ts","./node_modules/.pnpm/@react-native+virtualized-lists@0.81.6_@types+react@19.1.17_react-native@0.81.6_@babel+_b4909423bbc87f4e48ec18063a95f13a/node_modules/@react-native/virtualized-lists/index.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Lists/FlatList.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/ReactNative/RendererProxy.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Lists/SectionList.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Text/Text.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Animated/Animated.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/StyleSheet/StyleSheet.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/StyleSheet/processColor.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/ActionSheetIOS/ActionSheetIOS.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Alert/Alert.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Animated/Easing.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Animated/useAnimatedValue.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/EventEmitter/RCTDeviceEventEmitter.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/EventEmitter/RCTNativeAppEventEmitter.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/AppState/AppState.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/BatchedBridge/NativeModules.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/Clipboard/Clipboard.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/EventEmitter/NativeEventEmitter.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/Keyboard/Keyboard.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/types/private/TimerMixin.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/LayoutConformance/LayoutConformance.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/Pressable/Pressable.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/SafeAreaView/SafeAreaView.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/StatusBar/StatusBar.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/Switch/Switch.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/TextInput/InputAccessoryView.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/ToastAndroid/ToastAndroid.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/Touchable/TouchableWithoutFeedback.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/Touchable/TouchableHighlight.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/Touchable/TouchableOpacity.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/Touchable/TouchableNativeFeedback.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Components/Button.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Core/registerCallableModule.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Interaction/InteractionManager.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Interaction/PanResponder.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/LayoutAnimation/LayoutAnimation.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Linking/Linking.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/LogBox/LogBox.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Modal/Modal.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Performance/Systrace.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/PermissionsAndroid/PermissionsAndroid.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/PushNotificationIOS/PushNotificationIOS.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Utilities/IPerformanceLogger.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/ReactNative/AppRegistry.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/ReactNative/I18nManager.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/ReactNative/RootTag.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/ReactNative/UIManager.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/ReactNative/requireNativeComponent.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Settings/Settings.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Share/Share.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/StyleSheet/PlatformColorValueTypesIOS.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/StyleSheet/PlatformColorValueTypes.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/TurboModule/RCTExport.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/TurboModule/TurboModuleRegistry.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Types/CodegenTypesNamespace.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Utilities/Appearance.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Utilities/BackHandler.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/src/private/devsupport/devmenu/DevMenu.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Utilities/DevSettings.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Utilities/Dimensions.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Utilities/PixelRatio.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Utilities/Platform.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Vibration/Vibration.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/types/public/DeprecatedPropertiesAlias.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Utilities/codegenNativeCommands.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/Libraries/Utilities/codegenNativeComponent.d.ts","./node_modules/.pnpm/react-native@0.81.6_@babel+core@7.29.0_@react-native-community+cli@20.1.1_typescript@5._0aa80424664275e64fcee0cb402c8ca8/node_modules/react-native/types/index.d.ts","./src/utils/showToast.ts","./strings/translations.ts","./src/screens/library/constants/constants.ts","./node_modules/.pnpm/expo-speech@14.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_react-n_e3c45bf2c734d9bb3216ac0a3025db22/node_modules/expo-speech/build/Speech.types.d.ts","./node_modules/.pnpm/expo-speech@14.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_react-n_e3c45bf2c734d9bb3216ac0a3025db22/node_modules/expo-speech/build/Speech.d.ts","./src/hooks/persisted/useSettings.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/typescript.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.d.ts","./android/app/build/intermediates/assets/debug/mergeDebugAssets/js/van.d.ts","./android/app/build/intermediates/assets/debug/mergeDebugAssets/js/index.d.ts","./android/app/build/intermediates/assets/release/mergeReleaseAssets/js/van.d.ts","./android/app/build/intermediates/assets/release/mergeReleaseAssets/js/index.d.ts","./android/app/src/main/assets/js/van.d.ts","./android/app/src/main/assets/js/index.d.ts","./specs/NativeEpub.ts","./specs/NativeFile.ts","./specs/NativeTTSMediaControl.ts","./specs/NativeVolumeButtonListener.ts","./specs/NativeZipArchive.ts","./src/api/constants.ts","./node_modules/.pnpm/@react-native-google-signin+google-signin@16.1.1_expo@54.0.33_@babel+core@7.29.0_react-_aa79306f271ebe43e712882a5a70c215/node_modules/@react-native-google-signin/google-signin/lib/typescript/src/types.d.ts","./node_modules/.pnpm/@react-native-google-signin+google-signin@16.1.1_expo@54.0.33_@babel+core@7.29.0_react-_aa79306f271ebe43e712882a5a70c215/node_modules/@react-native-google-signin/google-signin/lib/typescript/src/signIn/GoogleSignin.d.ts","./node_modules/.pnpm/@react-native-google-signin+google-signin@16.1.1_expo@54.0.33_@babel+core@7.29.0_react-_aa79306f271ebe43e712882a5a70c215/node_modules/@react-native-google-signin/google-signin/lib/typescript/src/errors/errorCodes.d.ts","./node_modules/.pnpm/@react-native-google-signin+google-signin@16.1.1_expo@54.0.33_@babel+core@7.29.0_react-_aa79306f271ebe43e712882a5a70c215/node_modules/@react-native-google-signin/google-signin/lib/typescript/src/buttons/GoogleSigninButton.d.ts","./node_modules/.pnpm/@react-native-google-signin+google-signin@16.1.1_expo@54.0.33_@babel+core@7.29.0_react-_aa79306f271ebe43e712882a5a70c215/node_modules/@react-native-google-signin/google-signin/lib/typescript/src/functions.d.ts","./node_modules/.pnpm/@react-native-google-signin+google-signin@16.1.1_expo@54.0.33_@babel+core@7.29.0_react-_aa79306f271ebe43e712882a5a70c215/node_modules/@react-native-google-signin/google-signin/lib/typescript/src/index.d.ts","./src/api/drive/types.ts","./src/api/drive/request.ts","./src/api/drive/index.ts","./src/utils/fetch/fetch.ts","./src/api/remote/index.ts","./node_modules/.pnpm/@react-native-vector-icons+material-design-icons@12.4.0_react-native@0.81.6_@babel+core_ed79d2d08ef06eb274f94ea8f2685d94/node_modules/@react-native-vector-icons/material-design-icons/glyphmaps/MaterialDesignIcons.json","./node_modules/.pnpm/@react-native-vector-icons+common@12.4.0_react-native@0.81.6_@babel+core@7.29.0_@react-_732b0fe6e68ac61d44eb7612fa99f80d/node_modules/@react-native-vector-icons/common/lib/typescript/module/src/dynamicLoading/types.d.ts","./node_modules/.pnpm/@react-native-vector-icons+common@12.4.0_react-native@0.81.6_@babel+core@7.29.0_@react-_732b0fe6e68ac61d44eb7612fa99f80d/node_modules/@react-native-vector-icons/common/lib/typescript/module/src/create-icon-set.d.ts","./node_modules/.pnpm/@react-native-vector-icons+common@12.4.0_react-native@0.81.6_@babel+core@7.29.0_@react-_732b0fe6e68ac61d44eb7612fa99f80d/node_modules/@react-native-vector-icons/common/lib/typescript/module/src/defaults.d.ts","./node_modules/.pnpm/@react-native-vector-icons+common@12.4.0_react-native@0.81.6_@babel+core@7.29.0_@react-_732b0fe6e68ac61d44eb7612fa99f80d/node_modules/@react-native-vector-icons/common/lib/typescript/module/src/dynamicLoading/dynamic-loading-setting.d.ts","./node_modules/.pnpm/@react-native-vector-icons+common@12.4.0_react-native@0.81.6_@babel+core@7.29.0_@react-_732b0fe6e68ac61d44eb7612fa99f80d/node_modules/@react-native-vector-icons/common/lib/typescript/module/src/index.d.ts","./node_modules/.pnpm/@react-native-vector-icons+material-design-icons@12.4.0_react-native@0.81.6_@babel+core_ed79d2d08ef06eb274f94ea8f2685d94/node_modules/@react-native-vector-icons/material-design-icons/lib/typescript/module/src/index.d.ts","./node_modules/.pnpm/color-convert@3.1.3/node_modules/color-convert/index.d.ts","./node_modules/.pnpm/color@5.0.3/node_modules/color/index.d.ts","./src/theme/types/index.ts","./src/type/icon.ts","./src/components/IconButtonV2/IconButtonV2.tsx","./src/components/SearchbarV2/SearchbarV2.tsx","./src/components/LoadingScreenV2/LoadingScreenV2.tsx","./node_modules/.pnpm/@callstack+react-theme-provider@3.0.9_react@19.1.4/node_modules/@callstack/react-theme-provider/typings/hoist-non-react-statics.d.ts","./node_modules/.pnpm/@callstack+react-theme-provider@3.0.9_react@19.1.4/node_modules/@callstack/react-theme-provider/typings/index.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/types.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/styles/themes/v3/tokens.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/core/theming.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/styles/themes/v3/LightTheme.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/styles/themes/v3/DarkTheme.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/styles/themes/v2/LightTheme.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/styles/themes/v2/DarkTheme.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/styles/themes/index.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/MaterialCommunityIcon.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/core/settings.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/core/PaperProvider.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/styles/shadow.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/styles/overlay.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/styles/fonts.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Icon.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Avatar/AvatarIcon.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Avatar/AvatarImage.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Avatar/AvatarText.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Avatar/Avatar.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/TouchableRipple/Pressable.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/utils/forwardRef.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/TouchableRipple/TouchableRipple.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Drawer/DrawerItem.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Drawer/DrawerCollapsedItem.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Drawer/DrawerSection.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Drawer/Drawer.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/List/utils.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/List/ListAccordion.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/List/ListAccordionGroup.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/List/ListIcon.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/List/ListItem.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/List/ListSection.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Typography/types.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Typography/Text.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/List/ListSubheader.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/List/ListImage.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/List/List.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/styles/themes/v2/colors.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Surface.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/FAB/AnimatedFAB.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Badge.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/ActivityIndicator.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Button/Button.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Banner.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/BottomNavigation/BottomNavigationBar.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/BottomNavigation/BottomNavigation.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Card/CardActions.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Card/CardContent.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Card/CardCover.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Card/CardTitle.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Card/Card.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Checkbox/Checkbox.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Checkbox/CheckboxItem.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Checkbox/CheckboxAndroid.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Checkbox/CheckboxIOS.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Checkbox/index.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Chip/Chip.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/DataTable/DataTableHeader.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/DataTable/DataTableTitle.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/DataTable/DataTableRow.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/DataTable/DataTableCell.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/DataTable/DataTablePagination.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/DataTable/DataTable.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Dialog/DialogContent.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Dialog/DialogActions.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Typography/v2/Title.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Dialog/DialogTitle.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Dialog/DialogScrollArea.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Dialog/DialogIcon.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Dialog/Dialog.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Divider.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/FAB/FAB.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/FAB/FABGroup.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/FAB/index.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Typography/AnimatedText.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/HelperText/HelperText.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/IconButton/IconButton.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Menu/MenuItem.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Menu/Menu.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Modal.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Portal/PortalHost.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Portal/Portal.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/ProgressBar.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/RadioButton/RadioButton.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/RadioButton/RadioButtonGroup.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/RadioButton/RadioButtonAndroid.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/RadioButton/RadioButtonIOS.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/RadioButton/RadioButtonItem.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/RadioButton/index.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Searchbar.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Snackbar.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Switch/Switch.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Appbar/Appbar.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Appbar/AppbarContent.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Appbar/AppbarAction.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Appbar/AppbarBackAction.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Appbar/AppbarHeader.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Appbar/index.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/TextInput/Adornment/enums.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/TextInput/Adornment/TextInputAffix.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/TextInput/Adornment/TextInputIcon.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/TextInput/types.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/TextInput/TextInput.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/ToggleButton/ToggleButton.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/ToggleButton/ToggleButtonGroup.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/ToggleButton/ToggleButtonRow.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/ToggleButton/index.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/SegmentedButtons/SegmentedButtons.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Tooltip/Tooltip.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Typography/v2/Caption.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Typography/v2/Headline.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Typography/v2/Paragraph.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Typography/v2/Subheading.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/components/Typography/v2/index.d.ts","./node_modules/.pnpm/@react-navigation+routers@7.5.3/node_modules/@react-navigation/routers/lib/typescript/src/types.d.ts","./node_modules/.pnpm/@react-navigation+routers@7.5.3/node_modules/@react-navigation/routers/lib/typescript/src/CommonActions.d.ts","./node_modules/.pnpm/@react-navigation+routers@7.5.3/node_modules/@react-navigation/routers/lib/typescript/src/BaseRouter.d.ts","./node_modules/.pnpm/@react-navigation+routers@7.5.3/node_modules/@react-navigation/routers/lib/typescript/src/TabRouter.d.ts","./node_modules/.pnpm/@react-navigation+routers@7.5.3/node_modules/@react-navigation/routers/lib/typescript/src/DrawerRouter.d.ts","./node_modules/.pnpm/@react-navigation+routers@7.5.3/node_modules/@react-navigation/routers/lib/typescript/src/StackRouter.d.ts","./node_modules/.pnpm/@react-navigation+routers@7.5.3/node_modules/@react-navigation/routers/lib/typescript/src/index.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/types.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/BaseNavigationContainer.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/createNavigationContainerRef.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/createNavigatorFactory.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/CurrentRenderContext.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/findFocusedRoute.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/getActionFromState.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/getFocusedRouteNameFromRoute.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/getPathFromState.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/getStateFromPath.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/NavigationContainerRefContext.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/NavigationContext.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/NavigationHelpersContext.d.ts","./node_modules/.pnpm/@types+react@19.1.17/node_modules/@types/react/jsx-runtime.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/NavigationIndependentTree.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/NavigationMetaContext.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/NavigationRouteContext.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/PreventRemoveContext.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/PreventRemoveProvider.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/StaticNavigation.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/theming/ThemeContext.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/theming/ThemeProvider.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/theming/useTheme.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/useFocusEffect.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/useIsFocused.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/useNavigation.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/useNavigationBuilder.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/useNavigationContainerRef.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/useNavigationIndependentTree.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/useNavigationState.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/usePreventRemove.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/usePreventRemoveContext.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/useRoute.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/useStateForPath.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/validatePathConfig.d.ts","./node_modules/.pnpm/@react-navigation+core@7.14.0_react@19.1.4/node_modules/@react-navigation/core/lib/typescript/src/index.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/types.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/NavigationContainer.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/createStaticNavigation.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/useLinkProps.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/Link.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/LinkingContext.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/LocaleDirContext.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/ServerContext.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/ServerContainer.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/theming/DarkTheme.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/theming/DefaultTheme.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/UnhandledLinkingContext.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/useLinkBuilder.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/useLinkTo.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/useLocale.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/useRoutePath.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/useScrollToTop.d.ts","./node_modules/.pnpm/@react-navigation+native@7.1.28_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_aa848f9cc8755664e184f0f389383e2f/node_modules/@react-navigation/native/lib/typescript/src/index.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/react-navigation/types.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/react-navigation/navigators/createMaterialBottomTabNavigator.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/react-navigation/views/MaterialBottomTabView.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/react-navigation/index.d.ts","./node_modules/.pnpm/react-native-paper@5.15.0_react-native-safe-area-context@5.6.2_react-native@0.81.6_@bab_89be0712effdc077d690ce3b7d145bb8/node_modules/react-native-paper/lib/typescript/index.d.ts","./src/theme/md3/defaultTheme.ts","./src/theme/md3/mignightDusk.ts","./src/theme/md3/tealTurquoise.ts","./src/theme/md3/yotsuba.ts","./src/theme/md3/lavender.ts","./src/theme/md3/strawberry.ts","./src/theme/md3/tako.ts","./src/theme/md3/catppuccin.ts","./src/theme/md3/yinyang.ts","./src/theme/md3/index.ts","./src/hooks/persisted/useTheme.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/entity.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cache/core/types.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cache/core/cache.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/logger.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/casing.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sql/expressions/conditions.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sql/expressions/select.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sql/expressions/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/relations.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/utils.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/sequence.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/int.common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/bigint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/bit.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/bool.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/char.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sql/functions/aggregate.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/query-builders/query-builder.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sql/functions/vector.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sql/functions/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sql/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/checks.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/date.common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/date.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/decimal.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/float.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/geometry.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/inet.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/integer.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/timestamp.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/interval.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/jsonb.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/real.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/smallint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/string.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/time.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/uuid.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/varbit.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/varchar.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/vector.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/all.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/indexes.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/roles.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/policies.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/primary-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/unique-constraint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/table.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/custom.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/enum.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/foreign-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/columns/common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/view-base.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/query-builders/select.types.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/view.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/_relations.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/migrator.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/query-promise.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/runnable-query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/query-builders/refresh-materialized-view.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/query-builders/delete.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/query-builders/update.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/query-builders/insert.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/query-builders/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/dialect.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/query-builders/count.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/query-builders/query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/session.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/query-builders/raw.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/db.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/session.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/query-builders/select.types.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/query-builders/select.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/query-builders/query-builder.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/subquery.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/alias.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/schema.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/utils.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/utils/array.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cockroach-core/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/checks.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/sequence.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/int.common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/bigintT.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/boolean.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/bytes.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/custom.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/date-duration.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/decimal.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/double-precision.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/duration.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/integer.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/json.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/date.common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/localdate.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/localtime.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/real.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/relative-duration.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/smallint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/text.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/timestamp.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/timestamptz.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/uuid.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/bigint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/all.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/foreign-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/roles.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/policies.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/primary-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/unique-constraint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/table.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/indexes.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/columns/common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/subquery.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/query-builders/query-builder.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/view-base.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/view-common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/view.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/query-builders/delete.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/query-builders/update.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/query-builders/insert.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/query-builders/refresh-materialized-view.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/query-builders/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/dialect.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/query-builders/_query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/query-builders/count.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/query-builders/query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/query-builders/raw.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/db.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/session.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/query-builders/select.types.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/query-builders/select.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/alias.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/schema.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/utils.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/gel-core/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/checks.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/binary.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/bit.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/char.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/custom.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/date.common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/date.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/datetime.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/datetime2.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/datetimeoffset.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/decimal.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/float.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/int.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/numeric.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/real.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/smallint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/text.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/time.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/tinyint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/varbinary.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/varchar.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/all.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/indexes.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/primary-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/unique-constraint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/table.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/bigint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/foreign-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/columns/common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/view-base.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/query-builders/update.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/subquery.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/query-builders/query-builder.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/query-builders/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/query-builders/query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/db.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/session.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/query-builders/insert.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/dialect.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/query-builders/select.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/view-common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/view.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/query-builders/select.types.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/query-builders/delete.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/alias.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/schema.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/utils.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mssql-core/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/bigint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/binary.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/blob.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/boolean.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/string.common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/char.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/checks.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/date.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/date.common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/timestamp.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/datetime.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/decimal.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/double.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/enum.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/float.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/int.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/json.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/mediumint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/real.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/serial.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/smallint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/text.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/time.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/tinyint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/varbinary.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/varchar.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/year.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/all.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/primary-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/unique-constraint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/indexes.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/table.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/custom.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/foreign-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/columns/common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/view-base.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/subquery.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/query-builders/query-builder.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/query-builders/insert.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/query-builders/_query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/query-builders/count.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/query-builders/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/view-common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/view.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/query-builders/query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/db.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/session.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/query-builders/update.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/dialect.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/query-builders/select.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/query-builders/select.types.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/query-builders/delete.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/alias.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/schema.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/utils.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/mysql-core/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/sequence.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/bigserial.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/boolean.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/bytea.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/checks.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/cidr.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/custom.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/date.common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/date.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/double-precision.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/inet.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/int.common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/integer.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/timestamp.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/interval.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/json.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/jsonb.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/line.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/macaddr.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/macaddr8.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/numeric.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/point.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/postgis_extension/geometry.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/real.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/serial.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/smallint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/smallserial.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/text.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/time.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/uuid.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/varchar.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/vector_extension/bit.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/vector_extension/halfvec.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/vector_extension/sparsevec.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/vector_extension/vector.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/bigint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/all.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/foreign-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/roles.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/policies.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/primary-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/unique-constraint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/table.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/char.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/enum.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/indexes.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/columns/common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/view-base.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/subquery.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/session.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/query-builders/delete.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/query-builders/update.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/query-builders/insert.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/query-builders/refresh-materialized-view.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/query-builders/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/dialect.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/query-builders/query-builder.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/view-common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/view.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/query-builders/select.types.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/query-builders/select.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/alias.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/query-builders/_query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/query-builders/query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/async/insert.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/async/query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/query-builders/raw.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/async/raw.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/async/refresh-materialized-view.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/async/select.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/async/update.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/query-builders/count.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/async/count.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/async/db.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/async/session.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/async/delete.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/async/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/schema.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/utils.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/utils/array.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/pg-core/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/bigint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/binary.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/boolean.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/char.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/custom.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/date.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/datetime.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/decimal.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/double.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/enum.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/float.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/int.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/json.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/mediumint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/real.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/serial.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/smallint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/text.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/time.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/date.common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/timestamp.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/tinyint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/varbinary.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/varchar.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/vector.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/year.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/all.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/indexes.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/primary-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/unique-constraint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/table.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/columns/common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/query-builders/insert.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/query-builders/count.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/subquery.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/query-builders/query-builder.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/query-builders/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/view-base.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/view-common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/view.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/query-builders/query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/cache/core/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore/session.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore/driver.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/db.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/session.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/query-builders/update.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/dialect.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/query-builders/select.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/query-builders/select.types.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/query-builders/delete.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/alias.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/schema.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/utils.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/singlestore-core/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/columns/blob.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/checks.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/view-base.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/query-builders/delete.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/query-builders/update.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/indexes.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/query-builders/insert.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/query-builders/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/dialect.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/query-builders/_query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/query-builders/count.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/query-builders/query.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/subquery.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/db.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/query-builders/raw.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/session.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/query-builders/select.types.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/query-builders/select.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/query-builders/query-builder.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/view.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/primary-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/unique-constraint.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/columns/numeric.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/columns/real.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/columns/text.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/alias.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/utils.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/columns/integer.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/columns/all.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/table.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/columns/custom.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/columns/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/foreign-keys.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sqlite-core/columns/common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/column-builder.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/column.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/operations.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/table.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/sql/sql.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/subquery.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/alias.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/errors.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/view-common.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/index.d.ts","./src/utils/Storages.ts","./node_modules/.pnpm/@op-engineering+op-sqlite@15.2.5_react-native@0.81.6_@babel+core@7.29.0_@react-native-c_d742991af47408b1040f72efd3c63926/node_modules/@op-engineering/op-sqlite/lib/typescript/src/types.d.ts","./node_modules/.pnpm/@op-engineering+op-sqlite@15.2.5_react-native@0.81.6_@babel+core@7.29.0_@react-native-c_d742991af47408b1040f72efd3c63926/node_modules/@op-engineering/op-sqlite/lib/typescript/src/functions.d.ts","./node_modules/.pnpm/@op-engineering+op-sqlite@15.2.5_react-native@0.81.6_@babel+core@7.29.0_@react-native-c_d742991af47408b1040f72efd3c63926/node_modules/@op-engineering/op-sqlite/lib/typescript/src/Storage.d.ts","./node_modules/.pnpm/@op-engineering+op-sqlite@15.2.5_react-native@0.81.6_@babel+core@7.29.0_@react-native-c_d742991af47408b1040f72efd3c63926/node_modules/@op-engineering/op-sqlite/lib/typescript/src/index.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/op-sqlite/driver.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/op-sqlite/session.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/op-sqlite/index.d.ts","./src/database/schema/category.ts","./src/database/schema/novel.ts","./src/database/schema/chapter.ts","./src/database/schema/novelCategory.ts","./src/database/schema/repository.ts","./src/database/schema/index.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/op-sqlite/migrator.d.ts","./drizzle/migrations.js","./src/database/manager/manager.d.ts","./src/utils/sleep.ts","./src/database/manager/types.ts","./src/database/manager/queue.ts","./src/database/manager/manager.ts","./src/database/queryStrings/populate.ts","./src/database/queryStrings/triggers.ts","./src/database/db.ts","./src/database/utils/parser.ts","./src/database/queries/ChapterQueries.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/common.d.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/array.d.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/collection.d.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/date.d.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/function.d.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/lang.d.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/math.d.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/number.d.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/object.d.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/seq.d.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/string.d.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/util.d.ts","./node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/index.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/add.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/after.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/ary.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/assign.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/assignIn.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/assignInWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/assignWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/at.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/attempt.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/before.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/bind.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/bindAll.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/bindKey.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/camelCase.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/capitalize.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/castArray.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/ceil.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/chain.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/chunk.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/clamp.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/clone.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/cloneDeep.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/cloneDeepWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/cloneWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/compact.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/concat.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/cond.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/conforms.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/conformsTo.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/constant.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/countBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/create.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/curry.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/curryRight.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/debounce.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/deburr.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/defaults.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/defaultsDeep.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/defaultTo.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/defer.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/delay.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/difference.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/differenceBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/differenceWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/divide.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/drop.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/dropRight.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/dropRightWhile.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/dropWhile.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/each.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/eachRight.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/endsWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/entries.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/entriesIn.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/eq.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/escape.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/escapeRegExp.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/every.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/extend.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/extendWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/fill.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/filter.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/find.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/findIndex.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/findKey.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/findLast.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/findLastIndex.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/findLastKey.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/first.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flatMap.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flatMapDeep.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flatMapDepth.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flatten.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flattenDeep.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flattenDepth.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flip.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/floor.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flow.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/flowRight.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/forEach.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/forEachRight.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/forIn.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/forInRight.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/forOwn.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/forOwnRight.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/fromPairs.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/functions.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/functionsIn.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/get.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/groupBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/gt.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/gte.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/has.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/hasIn.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/head.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/identity.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/includes.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/indexOf.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/initial.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/inRange.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/intersection.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/intersectionBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/intersectionWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/invert.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/invertBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/invoke.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/invokeMap.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isArguments.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isArray.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isArrayBuffer.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isArrayLike.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isArrayLikeObject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isBoolean.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isBuffer.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isDate.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isElement.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isEmpty.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isEqual.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isEqualWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isError.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isFinite.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isFunction.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isInteger.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isLength.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isMap.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isMatch.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isMatchWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isNaN.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isNative.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isNil.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isNull.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isNumber.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isObject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isObjectLike.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isPlainObject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isRegExp.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isSafeInteger.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isSet.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isString.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isSymbol.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isTypedArray.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isUndefined.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isWeakMap.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/isWeakSet.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/iteratee.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/join.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/kebabCase.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/keyBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/keys.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/keysIn.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/last.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/lastIndexOf.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/lowerCase.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/lowerFirst.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/lt.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/lte.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/map.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/mapKeys.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/mapValues.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/matches.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/matchesProperty.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/max.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/maxBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/mean.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/meanBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/memoize.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/merge.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/mergeWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/method.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/methodOf.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/min.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/minBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/mixin.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/multiply.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/negate.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/noop.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/now.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/nth.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/nthArg.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/omit.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/omitBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/once.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/orderBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/over.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/overArgs.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/overEvery.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/overSome.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pad.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/padEnd.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/padStart.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/parseInt.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/partial.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/partialRight.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/partition.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pick.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pickBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/property.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/propertyOf.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pull.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pullAll.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pullAllBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pullAllWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/pullAt.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/random.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/range.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/rangeRight.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/rearg.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/reduce.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/reduceRight.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/reject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/remove.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/repeat.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/replace.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/rest.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/result.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/reverse.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/round.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sample.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sampleSize.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/set.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/setWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/shuffle.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/size.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/slice.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/snakeCase.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/some.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedIndex.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedIndexBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedIndexOf.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedLastIndex.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedLastIndexBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedLastIndexOf.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedUniq.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sortedUniqBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/split.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/spread.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/startCase.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/startsWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/stubArray.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/stubFalse.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/stubObject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/stubString.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/stubTrue.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/subtract.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sum.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/sumBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/tail.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/take.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/takeRight.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/takeRightWhile.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/takeWhile.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/tap.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/template.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/templateSettings.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/throttle.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/thru.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/times.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toArray.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toFinite.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toInteger.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toLength.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toLower.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toNumber.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toPairs.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toPairsIn.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toPath.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toPlainObject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toSafeInteger.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toString.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/toUpper.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/transform.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/trim.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/trimEnd.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/trimStart.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/truncate.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unary.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unescape.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/union.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unionBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unionWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/uniq.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/uniqBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/uniqueId.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/uniqWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unset.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unzip.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/unzipWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/update.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/updateWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/upperCase.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/upperFirst.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/values.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/valuesIn.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/without.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/words.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/wrap.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/xor.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/xorBy.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/xorWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/zip.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/zipObject.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/zipObjectDeep.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/zipWith.d.ts","./node_modules/.pnpm/@types+lodash-es@4.17.12/node_modules/@types/lodash-es/index.d.ts","./src/utils/parseChapterNumber.ts","./src/hooks/persisted/useUpdates.ts","./src/database/queries/CategoryQueries.ts","./src/hooks/persisted/useCategories.ts","./src/database/queries/HistoryQueries.ts","./src/hooks/persisted/useHistory.ts","./src/utils/constants/languages.ts","./node_modules/.pnpm/@noble+ciphers@2.1.1/node_modules/@noble/ciphers/utils.d.ts","./node_modules/.pnpm/@noble+ciphers@2.1.1/node_modules/@noble/ciphers/aes.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/globals.typedarray.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/buffer.buffer.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/globals.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/blob.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/console.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/crypto.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/encoding.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/events.d.ts","./node_modules/.pnpm/buffer@5.7.1/node_modules/buffer/index.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/utility.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/header.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/readable.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/fetch.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/formdata.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/connector.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/client-stats.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/client.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/errors.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/dispatcher.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/global-dispatcher.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/global-origin.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/pool-stats.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/pool.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/handlers.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/balanced-pool.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/h2c-client.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/agent.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/mock-interceptor.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/mock-call-history.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/mock-agent.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/mock-client.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/mock-pool.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/snapshot-agent.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/mock-errors.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/proxy-agent.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/retry-handler.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/retry-agent.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/api.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/cache-interceptor.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/interceptors.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/util.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/cookies.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/patch.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/websocket.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/eventsource.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/content-type.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/cache.d.ts","./node_modules/.pnpm/undici-types@7.16.0/node_modules/undici-types/index.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/importmeta.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/messaging.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/performance.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/storage.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/streams.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/timers.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/web-globals/url.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/assert.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/assert/strict.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/async_hooks.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/buffer.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/child_process.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/cluster.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/console.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/constants.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/crypto.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/dgram.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/dns.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/dns/promises.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/domain.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/events.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/fs.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/fs/promises.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/http.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/http2.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/https.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/inspector.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/inspector.generated.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/inspector/promises.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/module.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/net.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/os.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/path.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/path/posix.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/path/win32.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/perf_hooks.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/process.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/punycode.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/querystring.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/quic.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/readline.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/readline/promises.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/repl.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/sea.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/sqlite.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/stream.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/stream/consumers.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/stream/promises.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/stream/web.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/string_decoder.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/test.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/test/reporters.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/timers.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/timers/promises.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/tls.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/trace_events.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/tty.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/url.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/util.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/util/types.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/v8.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/vm.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/wasi.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/worker_threads.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/zlib.d.ts","./node_modules/.pnpm/@types+node@25.2.3/node_modules/@types/node/index.d.ts","./node_modules/.pnpm/domelementtype@2.3.0/node_modules/domelementtype/lib/esm/index.d.ts","./node_modules/.pnpm/domhandler@5.0.3/node_modules/domhandler/lib/esm/node.d.ts","./node_modules/.pnpm/domhandler@5.0.3/node_modules/domhandler/lib/esm/index.d.ts","./node_modules/.pnpm/htmlparser2@8.0.2/node_modules/htmlparser2/lib/esm/Tokenizer.d.ts","./node_modules/.pnpm/htmlparser2@8.0.2/node_modules/htmlparser2/lib/esm/Parser.d.ts","./node_modules/.pnpm/dom-serializer@2.0.0/node_modules/dom-serializer/lib/esm/index.d.ts","./node_modules/.pnpm/domutils@3.2.2/node_modules/domutils/lib/esm/stringify.d.ts","./node_modules/.pnpm/domutils@3.2.2/node_modules/domutils/lib/esm/traversal.d.ts","./node_modules/.pnpm/domutils@3.2.2/node_modules/domutils/lib/esm/manipulation.d.ts","./node_modules/.pnpm/domutils@3.2.2/node_modules/domutils/lib/esm/querying.d.ts","./node_modules/.pnpm/domutils@3.2.2/node_modules/domutils/lib/esm/legacy.d.ts","./node_modules/.pnpm/domutils@3.2.2/node_modules/domutils/lib/esm/helpers.d.ts","./node_modules/.pnpm/domutils@3.2.2/node_modules/domutils/lib/esm/feeds.d.ts","./node_modules/.pnpm/domutils@3.2.2/node_modules/domutils/lib/esm/index.d.ts","./node_modules/.pnpm/htmlparser2@8.0.2/node_modules/htmlparser2/lib/esm/index.d.ts","./node_modules/.pnpm/css-what@6.2.2/node_modules/css-what/lib/es/types.d.ts","./node_modules/.pnpm/css-what@6.2.2/node_modules/css-what/lib/es/parse.d.ts","./node_modules/.pnpm/css-what@6.2.2/node_modules/css-what/lib/es/stringify.d.ts","./node_modules/.pnpm/css-what@6.2.2/node_modules/css-what/lib/es/index.d.ts","./node_modules/.pnpm/css-select@5.2.2/node_modules/css-select/lib/esm/types.d.ts","./node_modules/.pnpm/css-select@5.2.2/node_modules/css-select/lib/esm/pseudo-selectors/filters.d.ts","./node_modules/.pnpm/css-select@5.2.2/node_modules/css-select/lib/esm/pseudo-selectors/pseudos.d.ts","./node_modules/.pnpm/css-select@5.2.2/node_modules/css-select/lib/esm/pseudo-selectors/aliases.d.ts","./node_modules/.pnpm/css-select@5.2.2/node_modules/css-select/lib/esm/pseudo-selectors/index.d.ts","./node_modules/.pnpm/css-select@5.2.2/node_modules/css-select/lib/esm/index.d.ts","./node_modules/.pnpm/cheerio-select@2.1.0/node_modules/cheerio-select/lib/esm/index.d.ts","./node_modules/.pnpm/cheerio@1.0.0-rc.12/node_modules/cheerio/lib/esm/options.d.ts","./node_modules/.pnpm/cheerio@1.0.0-rc.12/node_modules/cheerio/lib/esm/types.d.ts","./node_modules/.pnpm/cheerio@1.0.0-rc.12/node_modules/cheerio/lib/esm/api/attributes.d.ts","./node_modules/.pnpm/cheerio@1.0.0-rc.12/node_modules/cheerio/lib/esm/api/traversing.d.ts","./node_modules/.pnpm/cheerio@1.0.0-rc.12/node_modules/cheerio/lib/esm/api/manipulation.d.ts","./node_modules/.pnpm/cheerio@1.0.0-rc.12/node_modules/cheerio/lib/esm/api/css.d.ts","./node_modules/.pnpm/cheerio@1.0.0-rc.12/node_modules/cheerio/lib/esm/api/forms.d.ts","./node_modules/.pnpm/cheerio@1.0.0-rc.12/node_modules/cheerio/lib/esm/cheerio.d.ts","./node_modules/.pnpm/cheerio@1.0.0-rc.12/node_modules/cheerio/lib/esm/static.d.ts","./node_modules/.pnpm/cheerio@1.0.0-rc.12/node_modules/cheerio/lib/esm/load.d.ts","./node_modules/.pnpm/cheerio@1.0.0-rc.12/node_modules/cheerio/lib/esm/index.d.ts","./node_modules/.pnpm/htmlparser2@10.1.0/node_modules/htmlparser2/dist/esm/Tokenizer.d.ts","./node_modules/.pnpm/htmlparser2@10.1.0/node_modules/htmlparser2/dist/esm/Parser.d.ts","./node_modules/.pnpm/htmlparser2@10.1.0/node_modules/htmlparser2/dist/esm/index.d.ts","./node_modules/.pnpm/urlencode@2.0.0/node_modules/urlencode/dist/esm/index.d.ts","./src/database/queries/RepositoryQueries.ts","./node_modules/.pnpm/react-native-device-info@14.1.1_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_a2076dc483e2ed29ac183fe84f0f9564/node_modules/react-native-device-info/lib/typescript/internal/types.d.ts","./node_modules/.pnpm/react-native-device-info@14.1.1_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_a2076dc483e2ed29ac183fe84f0f9564/node_modules/react-native-device-info/lib/typescript/internal/privateTypes.d.ts","./node_modules/.pnpm/react-native-device-info@14.1.1_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_a2076dc483e2ed29ac183fe84f0f9564/node_modules/react-native-device-info/lib/typescript/index.d.ts","./src/hooks/persisted/useUserAgent.ts","./src/utils/compareVersion.ts","./src/plugins/helpers/storage.ts","./src/plugins/helpers/constants.ts","./node_modules/.pnpm/protobufjs@7.5.4/node_modules/protobufjs/index.d.ts","./src/plugins/helpers/fetch.ts","./src/plugins/helpers/isAbsoluteUrl.ts","./src/plugins/pluginManager.ts","./src/hooks/persisted/usePlugins.ts","./src/services/Trackers/index.ts","./node_modules/.pnpm/expo-linking@8.0.11_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_react-_554cd68c5c1b8af3f9813cad6d6f5b28/node_modules/expo-linking/build/Linking.types.d.ts","./node_modules/.pnpm/expo-linking@8.0.11_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_react-_554cd68c5c1b8af3f9813cad6d6f5b28/node_modules/expo-linking/build/Schemes.d.ts","./node_modules/.pnpm/expo-linking@8.0.11_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_react-_554cd68c5c1b8af3f9813cad6d6f5b28/node_modules/expo-linking/build/createURL.d.ts","./node_modules/.pnpm/expo-linking@8.0.11_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_react-_554cd68c5c1b8af3f9813cad6d6f5b28/node_modules/expo-linking/build/Linking.d.ts","./node_modules/.pnpm/expo-web-browser@15.0.10_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_r_d5c8d47977f1e77abd642a6d59379c0c/node_modules/expo-web-browser/build/WebBrowser.types.d.ts","./node_modules/.pnpm/expo-web-browser@15.0.10_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_r_d5c8d47977f1e77abd642a6d59379c0c/node_modules/expo-web-browser/build/WebBrowser.d.ts","./src/services/Trackers/aniList.ts","./src/services/Trackers/myAnimeList.ts","./src/services/Trackers/mangaUpdates.ts","./src/services/Trackers/kitsu.ts","./src/hooks/persisted/migrations/trackerMigration.ts","./src/hooks/persisted/useTracker.ts","./src/utils/error.ts","./src/hooks/persisted/useTrackedNovel.ts","./src/database/queries/LibraryQueries.ts","./node_modules/.pnpm/expo-document-picker@14.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15._65c25fb981811568705fb6d24abdb2d0/node_modules/expo-document-picker/build/types.d.ts","./node_modules/.pnpm/expo-document-picker@14.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15._65c25fb981811568705fb6d24abdb2d0/node_modules/expo-document-picker/build/index.d.ts","./src/services/plugin/fetch.ts","./src/database/queries/NovelQueries.ts","./node_modules/.pnpm/eventemitter3@4.0.7/node_modules/eventemitter3/index.d.ts","./node_modules/.pnpm/react-native-background-actions@4.0.1_react-native@0.81.6_@babel+core@7.29.0_@react-nat_2c6b767f56f6a7a8e0645efe6762c9c2/node_modules/react-native-background-actions/lib/types/index.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/sweet/setUpJsLogger.fx.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/polyfill/index.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/ts-declarations/EventEmitter.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/ts-declarations/NativeModule.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/ts-declarations/SharedObject.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/ts-declarations/SharedRef.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/ts-declarations/global.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/EventEmitter.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/NativeModule.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/SharedObject.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/SharedRef.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/Platform.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/uuid/uuid.types.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/uuid/index.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/NativeModulesProxy.types.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/NativeViewManagerAdapter.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/requireNativeModule.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/registerWebModule.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/TypedArrays.types.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/PermissionsInterface.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/PermissionsHook.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/Refs.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/hooks/useReleasingSharedObject.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/reload.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/errors/CodedError.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/errors/UnavailabilityError.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/LegacyEventEmitter.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/NativeModulesProxy.d.ts","./node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_77b2b42c94d25623a344e45fb59114ce/node_modules/expo-modules-core/build/index.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/Tokens.types.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/getDevicePushTokenAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/unregisterForNotificationsAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/getExpoPushTokenAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/Notifications.types.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/getPresentedNotificationsAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/dismissNotificationAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/dismissAllNotificationsAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/NotificationChannelManager.types.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/getNotificationChannelsAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/getNotificationChannelAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/setNotificationChannelAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/deleteNotificationChannelAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/NotificationChannelGroupManager.types.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/getNotificationChannelGroupsAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/getNotificationChannelGroupAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/setNotificationChannelGroupAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/deleteNotificationChannelGroupAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/getBadgeCountAsync.d.ts","./node_modules/.pnpm/badgin@1.2.3/node_modules/badgin/build/favicon.d.ts","./node_modules/.pnpm/badgin@1.2.3/node_modules/badgin/build/title.d.ts","./node_modules/.pnpm/badgin@1.2.3/node_modules/badgin/build/index.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/BadgeModule.types.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/setBadgeCountAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/getAllScheduledNotificationsAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/NotificationScheduler.types.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/scheduleNotificationAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/cancelScheduledNotificationAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/cancelAllScheduledNotificationsAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/getNotificationCategoriesAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/setNotificationCategoryAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/deleteNotificationCategoryAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/getNextTriggerDateAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/useLastNotificationResponse.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/DevicePushTokenAutoRegistration.fx.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/registerTaskAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/unregisterTaskAsync.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/TokenEmitter.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/NotificationsEmitter.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/NotificationsHandler.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/NotificationPermissions.types.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/NotificationPermissions.d.ts","./node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_e95091ba7392cfe75ee82b76f98c2568/node_modules/expo-notifications/build/index.d.ts","./src/services/epub/import.ts","./src/services/updates/LibraryUpdateQueries.ts","./src/services/updates/index.ts","./src/hooks/persisted/useSelfHost.ts","./package.json","./src/services/backup/types.ts","./src/services/backup/utils.ts","./src/services/backup/drive/index.ts","./src/services/backup/selfhost/index.ts","./node_modules/.pnpm/@react-native-documents+picker@10.1.7_react-native@0.81.6_@babel+core@7.29.0_@react-nat_cc05e658a96689d19d363ce4d3f87289/node_modules/@react-native-documents/picker/lib/typescript/isKnownType.d.ts","./node_modules/.pnpm/@react-native-documents+picker@10.1.7_react-native@0.81.6_@babel+core@7.29.0_@react-nat_cc05e658a96689d19d363ce4d3f87289/node_modules/@react-native-documents/picker/lib/typescript/types.d.ts","./node_modules/.pnpm/@react-native-documents+picker@10.1.7_react-native@0.81.6_@babel+core@7.29.0_@react-nat_cc05e658a96689d19d363ce4d3f87289/node_modules/@react-native-documents/picker/lib/typescript/spec/NativeDocumentPicker.d.ts","./node_modules/.pnpm/@react-native-documents+picker@10.1.7_react-native@0.81.6_@babel+core@7.29.0_@react-nat_cc05e658a96689d19d363ce4d3f87289/node_modules/@react-native-documents/picker/lib/typescript/keepLocalCopy.d.ts","./node_modules/.pnpm/@react-native-documents+picker@10.1.7_react-native@0.81.6_@babel+core@7.29.0_@react-nat_cc05e658a96689d19d363ce4d3f87289/node_modules/@react-native-documents/picker/lib/typescript/fileTypes.d.ts","./node_modules/.pnpm/@react-native-documents+picker@10.1.7_react-native@0.81.6_@babel+core@7.29.0_@react-nat_cc05e658a96689d19d363ce4d3f87289/node_modules/@react-native-documents/picker/lib/typescript/errors.d.ts","./node_modules/.pnpm/@react-native-documents+picker@10.1.7_react-native@0.81.6_@babel+core@7.29.0_@react-nat_cc05e658a96689d19d363ce4d3f87289/node_modules/@react-native-documents/picker/lib/typescript/pickDirectory.d.ts","./node_modules/.pnpm/@react-native-documents+picker@10.1.7_react-native@0.81.6_@babel+core@7.29.0_@react-nat_cc05e658a96689d19d363ce4d3f87289/node_modules/@react-native-documents/picker/lib/typescript/pick.d.ts","./node_modules/.pnpm/@react-native-documents+picker@10.1.7_react-native@0.81.6_@babel+core@7.29.0_@react-nat_cc05e658a96689d19d363ce4d3f87289/node_modules/@react-native-documents/picker/lib/typescript/saveDocuments.d.ts","./node_modules/.pnpm/@react-native-documents+picker@10.1.7_react-native@0.81.6_@babel+core@7.29.0_@react-nat_cc05e658a96689d19d363ce4d3f87289/node_modules/@react-native-documents/picker/lib/typescript/release.d.ts","./node_modules/.pnpm/@react-native-documents+picker@10.1.7_react-native@0.81.6_@babel+core@7.29.0_@react-nat_cc05e658a96689d19d363ce4d3f87289/node_modules/@react-native-documents/picker/lib/typescript/index.d.ts","./src/services/backup/local/index.ts","./src/services/migrate/migrateNovel.ts","./src/services/download/downloadChapter.ts","./src/utils/askForPostNoftificationsPermission.ts","./src/services/ServiceManager.ts","./src/screens/library/hooks/useLibrary.ts","./src/components/Context/LibraryContext.tsx","./src/hooks/persisted/useNovel.ts","./src/hooks/persisted/useDownload.ts","./src/hooks/persisted/index.ts","./src/components/ErrorScreenV2/ErrorScreenV2.tsx","./src/components/EmptyView/EmptyView.tsx","./src/components/Chip/Chip.tsx","./src/components/Chip/SelectableChip.tsx","./src/components/Button/Button.tsx","./src/components/Appbar/Appbar.tsx","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/publicGlobals.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/memory/types.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/memory/serializable.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/deprecated.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/featureFlags/types.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/featureFlags/featureFlags.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/memory/isSynchronizable.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/memory/serializableMappingCache.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/memory/synchronizable.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/runtimeKind.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/types.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/runtimes.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/threads.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/workletFunction.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/WorkletsModule/workletsModuleProxy.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/WorkletsModule/NativeWorklets.d.ts","./node_modules/.pnpm/react-native-worklets@0.7.4_@babel+core@7.29.0_react-native@0.81.6_@babel+core@7.29.0_@_97cd7c4d3b59282a42b66573a20c638f/node_modules/react-native-worklets/lib/typescript/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/constants/font.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/constants/platform.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/constants/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/errors.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/logger.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/types/helpers.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/types/config.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/types/style.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/types/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/types.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/config.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/createStyleBuilder.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/processors/colors.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/processors/filter.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/processors/font.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/processors/insets.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/processors/others.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/processors/shadows.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/processors/transform.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/processors/transformOrigin.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/processors/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/style/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/utils/conversions.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/utils/guards.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/utils/parsers.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/utils/suffix.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/utils/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/common/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/component/LayoutAnimationConfig.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/animationsManager.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/animationBuilder/BaseAnimationBuilder.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/Easing.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/animationBuilder/ComplexAnimationBuilder.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/animationBuilder/Keyframe.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/animationBuilder/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultAnimations/Bounce.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultAnimations/Fade.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultAnimations/Flip.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultAnimations/Lightspeed.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultAnimations/Pinwheel.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultAnimations/Roll.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultAnimations/Rotate.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultAnimations/Slide.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultAnimations/Stretch.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultAnimations/Zoom.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultAnimations/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultTransitions/CurvedTransition.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultTransitions/EntryExitTransition.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultTransitions/FadingTransition.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultTransitions/JumpingTransition.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultTransitions/LinearTransition.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultTransitions/SequencedTransition.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/defaultTransitions/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/SharedTransition.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/layoutReanimation/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/ViewDescriptorsSet.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/createAnimatedComponent/commonTypes.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/helperTypes.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/easing/types.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/easing/cubicBezier.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/easing/linear.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/easing/steps.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/easing/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/types/common.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/types/helpers.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/types/animation.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/types/transition.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/types/props.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/types/interfaces.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/types/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/models/CSSKeyframesRuleBase.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/models/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/types/animation.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/types/transition.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/types/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/keyframes/CSSKeyframesRuleImpl.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/keyframes/CSSKeyframesRegistry.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/keyframes/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/managers/CSSManager.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/managers/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/normalization/animation/keyframes.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/normalization/animation/properties.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/normalization/animation/settings.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/normalization/animation/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/normalization/common/settings.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/normalization/common/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/normalization/transition/config.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/normalization/transition/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/normalization/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/proxy.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/registry.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/native/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/platform.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/component/AnimatedComponent.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/createAnimatedComponent/InlinePropManager.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/createAnimatedComponent/PropsFilter.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/createAnimatedComponent/AnimatedComponent.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/createAnimatedComponent/createAnimatedComponent.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/createAnimatedComponent/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/ReanimatedModule/reanimatedModuleProxy.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/ReanimatedModule/js-reanimated/JSReanimated.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/ReanimatedModule/js-reanimated/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/commonTypes.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useAnimatedKeyboard.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useAnimatedProps.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useAnimatedReaction.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useAnimatedRef.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useEvent.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useAnimatedScrollHandler.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useAnimatedSensor.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useAnimatedStyle.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useComposedEventHandler.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useDerivedValue.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/frameCallback/FrameCallbackRegistryUI.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useFrameCallback.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useHandler.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useReducedMotion.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useScrollOffset.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/useSharedValue.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/hook/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/component/createAnimatedComponent.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/component/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/stylesheet/keyframes.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/stylesheet/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/css/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/commonTypes.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/component/FlatList.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/component/Image.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/component/ScrollView.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/component/Text.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/component/View.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/ConfigHelper.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/Animated.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/clamp.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/commonTypes.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/decay/utils.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/decay/decay.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/decay/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/delay.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/repeat.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/sequence.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/spring/springConfigs.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/spring/spring.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/spring/springUtils.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/spring/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/styleAnimation.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/timing.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/util.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/animation/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/Colors.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/component/PerformanceMonitor.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/component/ReducedMotionConfig.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/mappers.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/mutables.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/core.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/featureFlags/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/frameCallback/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/interpolation.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/interpolateColor.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/isSharedValue.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/jestUtils/common.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/jestUtils/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/platform-specific/jsVersion.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/platformFunctions/types.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/platformFunctions/dispatchCommand.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/platformFunctions/getRelativeCoords.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/platformFunctions/measure.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/platformFunctions/scrollTo.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/platformFunctions/setGestureState.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/platformFunctions/setNativeProps.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/platformFunctions/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/pluginUtils.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/PropAdapters.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/screenTransition/commonTypes.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/screenTransition/animationManager.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/screenTransition/presets.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/screenTransition/index.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/workletFunctions.d.ts","./node_modules/.pnpm/react-native-reanimated@4.2.2_react-native-worklets@0.7.4_@babel+core@7.29.0_react-nati_667623289dbd3558e1c5ac43b1d60a2b/node_modules/react-native-reanimated/lib/typescript/index.d.ts","./src/components/Switch/Switch.tsx","./src/components/Switch/SwitchItem.tsx","./src/components/List/List.tsx","./src/components/ColorPreferenceItem/ColorPreferenceItem.tsx","./src/components/LoadingMoreIndicator/LoadingMoreIndicator.tsx","./src/components/Checkbox/Checkbox.tsx","./src/components/RadioButton/RadioButton.tsx","./src/components/ConfirmationDialog/ConfirmationDialog.tsx","./node_modules/.pnpm/react-native-safe-area-context@5.6.2_react-native@0.81.6_@babel+core@7.29.0_@react-nati_bb4a339cd89b7691618c0af2c782ca4b/node_modules/react-native-safe-area-context/lib/typescript/src/specs/NativeSafeAreaView.d.ts","./node_modules/.pnpm/react-native-safe-area-context@5.6.2_react-native@0.81.6_@babel+core@7.29.0_@react-nati_bb4a339cd89b7691618c0af2c782ca4b/node_modules/react-native-safe-area-context/lib/typescript/src/SafeArea.types.d.ts","./node_modules/.pnpm/react-native-safe-area-context@5.6.2_react-native@0.81.6_@babel+core@7.29.0_@react-nati_bb4a339cd89b7691618c0af2c782ca4b/node_modules/react-native-safe-area-context/lib/typescript/src/SafeAreaContext.d.ts","./node_modules/.pnpm/react-native-safe-area-context@5.6.2_react-native@0.81.6_@babel+core@7.29.0_@react-nati_bb4a339cd89b7691618c0af2c782ca4b/node_modules/react-native-safe-area-context/lib/typescript/src/SafeAreaView.d.ts","./node_modules/.pnpm/react-native-safe-area-context@5.6.2_react-native@0.81.6_@babel+core@7.29.0_@react-nati_bb4a339cd89b7691618c0af2c782ca4b/node_modules/react-native-safe-area-context/lib/typescript/src/InitialWindow.d.ts","./node_modules/.pnpm/react-native-safe-area-context@5.6.2_react-native@0.81.6_@babel+core@7.29.0_@react-nati_bb4a339cd89b7691618c0af2c782ca4b/node_modules/react-native-safe-area-context/lib/typescript/src/index.d.ts","./src/components/SafeAreaView/SafeAreaView.tsx","./src/components/Modal/Modal.tsx","./src/components/SegmentedControl/SegmentedControl.tsx","./src/components/SegmentedControl/index.ts","./src/components/DialogTitle/DialogTitle.tsx","./src/components/DialogTitle/index.tsx","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Background.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Badge.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/PlatformPressable.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Button.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/getDefaultSidebarWidth.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/types.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Header/getDefaultHeaderHeight.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Header/getHeaderTitle.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Header/Header.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Header/HeaderBackButton.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Header/HeaderBackContext.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Header/HeaderBackground.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Header/HeaderButton.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Header/HeaderHeightContext.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Header/HeaderShownContext.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Header/HeaderTitle.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Header/useHeaderHeight.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Label/getLabel.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Label/Label.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Lazy.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/MissingIcon.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/ResourceSavingView.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/SafeAreaProviderCompat.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Screen.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/Text.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/useFrameSize.d.ts","./node_modules/.pnpm/@react-navigation+elements@2.9.5_@react-navigation+native@7.1.28_react-native@0.81.6_@b_0d966a0643c8cf71e40a40b17ca8a163/node_modules/@react-navigation/elements/lib/typescript/src/index.d.ts","./node_modules/.pnpm/@react-navigation+bottom-tabs@7.14.0_@react-navigation+native@7.1.28_react-native@0.81._3792a573ed29a3038834244adc240c94/node_modules/@react-navigation/bottom-tabs/lib/typescript/src/types.d.ts","./node_modules/.pnpm/@react-navigation+bottom-tabs@7.14.0_@react-navigation+native@7.1.28_react-native@0.81._3792a573ed29a3038834244adc240c94/node_modules/@react-navigation/bottom-tabs/lib/typescript/src/TransitionConfigs/SceneStyleInterpolators.d.ts","./node_modules/.pnpm/@react-navigation+bottom-tabs@7.14.0_@react-navigation+native@7.1.28_react-native@0.81._3792a573ed29a3038834244adc240c94/node_modules/@react-navigation/bottom-tabs/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts","./node_modules/.pnpm/@react-navigation+bottom-tabs@7.14.0_@react-navigation+native@7.1.28_react-native@0.81._3792a573ed29a3038834244adc240c94/node_modules/@react-navigation/bottom-tabs/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts","./node_modules/.pnpm/@react-navigation+bottom-tabs@7.14.0_@react-navigation+native@7.1.28_react-native@0.81._3792a573ed29a3038834244adc240c94/node_modules/@react-navigation/bottom-tabs/lib/typescript/src/navigators/createBottomTabNavigator.d.ts","./node_modules/.pnpm/@react-navigation+bottom-tabs@7.14.0_@react-navigation+native@7.1.28_react-native@0.81._3792a573ed29a3038834244adc240c94/node_modules/@react-navigation/bottom-tabs/lib/typescript/src/views/BottomTabBar.d.ts","./node_modules/.pnpm/@react-navigation+bottom-tabs@7.14.0_@react-navigation+native@7.1.28_react-native@0.81._3792a573ed29a3038834244adc240c94/node_modules/@react-navigation/bottom-tabs/lib/typescript/src/views/BottomTabView.d.ts","./node_modules/.pnpm/@react-navigation+bottom-tabs@7.14.0_@react-navigation+native@7.1.28_react-native@0.81._3792a573ed29a3038834244adc240c94/node_modules/@react-navigation/bottom-tabs/lib/typescript/src/utils/BottomTabBarHeightCallbackContext.d.ts","./node_modules/.pnpm/@react-navigation+bottom-tabs@7.14.0_@react-navigation+native@7.1.28_react-native@0.81._3792a573ed29a3038834244adc240c94/node_modules/@react-navigation/bottom-tabs/lib/typescript/src/utils/BottomTabBarHeightContext.d.ts","./node_modules/.pnpm/@react-navigation+bottom-tabs@7.14.0_@react-navigation+native@7.1.28_react-native@0.81._3792a573ed29a3038834244adc240c94/node_modules/@react-navigation/bottom-tabs/lib/typescript/src/utils/useBottomTabBarHeight.d.ts","./node_modules/.pnpm/@react-navigation+bottom-tabs@7.14.0_@react-navigation+native@7.1.28_react-native@0.81._3792a573ed29a3038834244adc240c94/node_modules/@react-navigation/bottom-tabs/lib/typescript/src/index.d.ts","./src/components/BottomTabBar/index.tsx","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/Directions.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/State.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/PointerType.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/gestureHandlerRootHOC.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/specs/RNGestureHandlerRootViewNativeComponent.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/GestureHandlerRootView.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/TouchEventType.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/typeUtils.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestureHandlerCommon.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/gestureStateManager.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/web/interfaces.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/GestureHandlerEventPayload.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/gesture.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/TapGestureHandler.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/ForceTouchGestureHandler.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/forceTouchGesture.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/LongPressGestureHandler.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/PanGestureHandler.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/panGesture.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/PinchGestureHandler.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/pinchGesture.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/RotationGestureHandler.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/FlingGestureHandler.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/NativeViewGestureHandler.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/createNativeWrapper.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/gestureComposition.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/GestureDetector/index.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/flingGesture.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/longPressGesture.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/rotationGesture.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/tapGesture.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/nativeGesture.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/manualGesture.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/hoverGesture.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestures/gestureObjects.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/GestureButtonsProps.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/GestureHandlerButton.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/GestureButtons.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/touchables/ExtraButtonProps.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/touchables/GenericTouchableProps.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/touchables/TouchableHighlight.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/touchables/TouchableOpacity.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/touchables/GenericTouchable.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/touchables/TouchableWithoutFeedback.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/touchables/TouchableNativeFeedback.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/touchables/index.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/GestureComponents.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/Text.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/handlers/gestureHandlerTypesCompat.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/Swipeable.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/utils.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/Pressable/PressableProps.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/Pressable/Pressable.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/Pressable/index.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/DrawerLayout.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/EnableNewWebImplementation.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/index.d.ts","./src/components/Menu/index.tsx","./src/components/index.ts","./node_modules/.pnpm/@types+better-sqlite3@7.6.13/node_modules/@types/better-sqlite3/index.d.ts","./src/database/__tests__/db.test.ts","./src/database/queries/StatsQueries.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/better-sqlite3/driver.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/better-sqlite3/session.d.ts","./node_modules/.pnpm/drizzle-orm@1.0.0-beta.13-f728631_0b72bd9ef2b6872263c5ffe3abac06d7/node_modules/drizzle-orm/better-sqlite3/index.d.ts","./src/database/queries/__tests__/testDbManager.ts","./src/database/queries/__tests__/testDb.ts","./src/database/queries/__tests__/setup.ts","./src/database/queries/__tests__/mockDb.ts","./src/database/queries/__tests__/testData.ts","./src/database/queries/__tests__/CategoryQueries.test.ts","./src/database/queries/__tests__/ChapterQueries.test.ts","./src/database/queries/__tests__/HistoryQueries.test.ts","./src/database/queries/__tests__/LibraryQueries.test.ts","./src/database/queries/__tests__/NovelQueries.test.ts","./src/database/queries/__tests__/RepositoryQueries.test.ts","./src/database/queries/__tests__/StatsQueries.test.ts","./src/database/queries/__tests__/index.ts","./src/database/queryStrings/indexes.ts","./src/database/utils/convertDateToISOString.ts","./src/database/utils/filter.ts","./src/hooks/common/useSearch.ts","./node_modules/.pnpm/expo-navigation-bar@5.0.10_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_a7b125775eafc7525c15d835de2aa544/node_modules/expo-navigation-bar/build/NavigationBar.types.d.ts","./node_modules/.pnpm/expo-navigation-bar@5.0.10_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_a7b125775eafc7525c15d835de2aa544/node_modules/expo-navigation-bar/build/NavigationBar.d.ts","./node_modules/.pnpm/expo-navigation-bar@5.0.10_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_a7b125775eafc7525c15d835de2aa544/node_modules/expo-navigation-bar/build/index.d.ts","./src/theme/utils/setBarColor.ts","./node_modules/.pnpm/react-native-edge-to-edge@1.7.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_e7d05d080df7a22360194f6e6557c949/node_modules/react-native-edge-to-edge/dist/typescript/types.d.ts","./node_modules/.pnpm/react-native-edge-to-edge@1.7.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_e7d05d080df7a22360194f6e6557c949/node_modules/react-native-edge-to-edge/dist/typescript/SystemBars.d.ts","./node_modules/.pnpm/react-native-edge-to-edge@1.7.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-co_e7d05d080df7a22360194f6e6557c949/node_modules/react-native-edge-to-edge/dist/typescript/index.d.ts","./src/hooks/common/useFullscreenMode.ts","./src/hooks/common/useBoolean.ts","./src/hooks/common/usePreviousRouteName.ts","./src/hooks/common/useBackHandler.ts","./src/hooks/common/useDeviceOrientation.ts","./src/hooks/index.ts","./src/hooks/__mocks__/index.ts","./src/hooks/__tests__/mocks.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/helpers/ensure-peer-deps.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/matches.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/helpers/matchers/match-accessibility-value.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/matchers/to-have-style.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/matchers/types.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/matchers/extend-expect.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/act.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/cleanup.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/types.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/fire-event.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/helpers/map-props.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/helpers/format-element.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/helpers/debug.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/helpers/errors.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/wait-for.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/queries/make-queries.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/helpers/matchers/match-accessibility-state.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/queries/options.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/queries/role.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/render.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/render-async.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/wait-for-element-to-be-removed.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/within.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/config.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/helpers/accessibility.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/render-hook.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/screen.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/user-event/scroll/scroll-to.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/user-event/scroll/index.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/user-event/type/type.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/user-event/type/index.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/user-event/setup/setup.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/user-event/setup/index.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/user-event/press/press.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/user-event/press/index.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/user-event/index.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/pure.d.ts","./node_modules/.pnpm/@testing-library+react-native@13.3.3_jest@29.7.0_@types+node@25.2.3__react-native@0.81._3110922890f7b5bab45a84096bc57dfa/node_modules/@testing-library/react-native/build/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/constants.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/types.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetBackdrop/types.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetBackdrop/BottomSheetBackdrop.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetBackdrop/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetBackground/types.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetBackground/BottomSheetBackgroundContainer.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetBackground/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetFooter/types.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetFooter/BottomSheetFooter.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetFooter/BottomSheetFooterContainer.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetFooter/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useGestureHandler.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetHandle/types.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetHandle/BottomSheetHandle.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetHandle/BottomSheetHandleContainer.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetHandle/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheet/types.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheet/BottomSheet.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheet/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetModal/types.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetModal/BottomSheetModal.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetModal/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetModalProvider/types.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetModalProvider/BottomSheetModalProvider.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetModalProvider/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useBottomSheet.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/contexts/modal/external.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useBottomSheetModal.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useBottomSheetSpringConfigs.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useBottomSheetTimingConfigs.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/contexts/internal.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useBottomSheetInternal.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/contexts/external.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/contexts/gesture.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/contexts/modal/internal.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/contexts/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useBottomSheetModalInternal.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useScrollEventsHandlersDefault.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useGestureEventsHandlersDefault.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useBottomSheetGestureHandlers.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useScrollHandler.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useScrollableSetter.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetScrollable/createBottomSheetScrollableComponent.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetScrollable/types.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetScrollable/BottomSheetSectionList.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetScrollable/BottomSheetFlatList.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetScrollable/BottomSheetScrollView.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetScrollable/BottomSheetVirtualizedList.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/recyclerview/helpers/ConsecutiveNumbers.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/recyclerview/layout-managers/LayoutManager.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/recyclerview/viewability/ViewToken.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/FlashListProps.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/recyclerview/RecyclerViewProps.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/recyclerview/components/CompatScroller.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/FlashListRef.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/recyclerview/RecyclerView.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/FlashList.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/AnimatedFlashList.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/benchmark/JSFPSMonitor.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/benchmark/useBenchmark.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/benchmark/useDataMultiplier.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/benchmark/useFlatListBenchmark.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/recyclerview/hooks/useLayoutState.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/recyclerview/hooks/useRecyclingState.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/recyclerview/hooks/useMappingHelper.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/benchmark/AutoScrollHelper.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/recyclerview/RecyclerViewContextProvider.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/recyclerview/LayoutCommitObserver.d.ts","./node_modules/.pnpm/@shopify+flash-list@2.0.2_@babel+runtime@7.28.6_react-native@0.81.6_@babel+core@7.29.0__f19bc77b9d68708059aff7545f8ce762/node_modules/@shopify/flash-list/dist/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetScrollable/BottomSheetFlashList.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetScrollable/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/hooks/useBottomSheetScrollableCreator.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetDraggableView/types.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetDraggableView/BottomSheetDraggableView.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetDraggableView/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetView/types.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetView/BottomSheetView.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetView/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetTextInput/types.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetTextInput/BottomSheetTextInput.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/components/bottomSheetTextInput/index.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/utilities/logger.d.ts","./node_modules/.pnpm/@gorhom+bottom-sheet@5.2.8_@types+react@19.1.17_react-native-gesture-handler@2.30.0_rea_d59287f75b729ead828d36f88c79205c/node_modules/@gorhom/bottom-sheet/lib/typescript/index.d.ts","./node_modules/.pnpm/react-native-error-boundary@2.0.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-_8fc1eec4453f6b1db147f71afbaf1c06/node_modules/react-native-error-boundary/lib/ErrorBoundary/FallbackComponent/index.d.ts","./node_modules/.pnpm/react-native-error-boundary@2.0.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-_8fc1eec4453f6b1db147f71afbaf1c06/node_modules/react-native-error-boundary/lib/ErrorBoundary/index.d.ts","./node_modules/.pnpm/react-native-error-boundary@2.0.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-_8fc1eec4453f6b1db147f71afbaf1c06/node_modules/react-native-error-boundary/lib/index.d.ts","./src/components/AppErrorBoundary/AppErrorBoundary.tsx","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/types.d.ts","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/navigators/createStackNavigator.d.ts","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/views/Header/Header.d.ts","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/views/Stack/StackView.d.ts","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/utils/CardAnimationContext.d.ts","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/utils/GestureHandlerRefContext.d.ts","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/utils/useCardAnimation.d.ts","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/utils/useGestureHandlerRef.d.ts","./node_modules/.pnpm/@react-navigation+stack@7.7.2_ab852612dcbc4f4eaa1a5a4d46e7f077/node_modules/@react-navigation/stack/lib/typescript/src/index.d.ts","./src/navigators/types/index.ts","./src/screens/novel/NovelContext.tsx","./__tests__/test-utils.tsx","./src/hooks/__tests__/useNovel.test.ts","./src/hooks/common/useGithubUpdateChecker.ts","./src/hooks/persisted/useImport.ts","./src/hooks/persisted/useNovelSettings.ts","./src/hooks/persisted/__mocks__/useCategories.ts","./src/hooks/persisted/__mocks__/useDownload.ts","./src/hooks/persisted/__mocks__/useHistory.ts","./src/hooks/persisted/__mocks__/useImport.ts","./src/hooks/persisted/__mocks__/useNovel.ts","./src/hooks/persisted/__mocks__/useNovelSettings.ts","./src/hooks/persisted/__mocks__/usePlugins.ts","./src/hooks/persisted/__mocks__/useSelfHost.ts","./src/hooks/persisted/__mocks__/useSettings.ts","./src/hooks/persisted/__mocks__/useTheme.ts","./src/hooks/persisted/__mocks__/useTrackedNovel.ts","./src/hooks/persisted/__mocks__/useTracker.ts","./src/hooks/persisted/__mocks__/useUpdates.ts","./src/hooks/persisted/__mocks__/useUserAgent.ts","./src/screens/BrowseSourceScreen/useBrowseSource.ts","./src/screens/BrowseSourceScreen/components/filterUtils.ts","./src/screens/GlobalSearchScreen/hooks/useGlobalSearch.ts","./src/screens/browse/discover/MyAnimeListScraper.ts","./src/screens/novel/components/Tracker/types.ts","./src/screens/novel/components/Tracker/constants.ts","./src/components/BottomSheet/BottomSheetBackdrop.tsx","./src/components/BottomSheet/BottomSheet.tsx","./src/screens/novel/components/Tracker/TrackerCards.tsx","./src/screens/novel/components/Tracker/TrackSearchDialog.tsx","./src/components/RadioButton.tsx","./src/screens/novel/components/Tracker/SetTrackStatusDialog.tsx","./src/screens/novel/components/Tracker/ScoreSelectors.tsx","./src/screens/novel/components/Tracker/SetTrackScoreDialog.tsx","./src/screens/novel/components/Tracker/SetTrackChaptersDialog.tsx","./src/screens/novel/components/Tracker/TrackSheet.tsx","./src/screens/novel/components/Tracker/index.ts","./node_modules/.pnpm/@types+sanitize-html@2.16.0/node_modules/@types/sanitize-html/index.d.ts","./src/screens/reader/utils/sanitizeChapterText.ts","./node_modules/.pnpm/react-native-webview@13.15.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commu_e3aff2174cba2ce4b13fd6142ab0ecca/node_modules/react-native-webview/lib/RNCWebViewNativeComponent.d.ts","./node_modules/.pnpm/react-native-webview@13.15.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commu_e3aff2174cba2ce4b13fd6142ab0ecca/node_modules/react-native-webview/lib/WebViewTypes.d.ts","./node_modules/.pnpm/react-native-webview@13.15.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commu_e3aff2174cba2ce4b13fd6142ab0ecca/node_modules/react-native-webview/index.d.ts","./src/screens/reader/hooks/useChapter.ts","./src/screens/settings/SettingsReaderScreen/utils.ts","./src/services/plugin/__mocks__/fetch.ts","./src/theme/colors.ts","./src/utils/translateEnum.ts","./src/utils/ttsNotification.ts","./src/utils/useLoadingColors.ts","./src/utils/constants/readerConstants.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/fabric/NativeScreensModule.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/shared/types.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/tabs/TabsAccessory.types.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/tabs/TabsHost.types.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/tabs/TabsHost.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/tabs/TabsScreen.types.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/tabs/TabsScreen.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/tabs/index.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/types.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/core.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/Screen.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/fabric/ScreenStackHeaderSubviewNativeComponent.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/ScreenStackHeaderConfig.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/SearchBar.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/ScreenContainer.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/ScreenStack.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/ScreenStackItem.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/FullWindowOverlay.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/ScreenFooter.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/components/ScreenContentWrapper.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/utils.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/flags.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/useTransitionProgress.d.ts","./node_modules/.pnpm/react-native-screens@4.23.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-commun_dd4b49223b1c022dc843bbd6021eb1f3/node_modules/react-native-screens/lib/typescript/index.d.ts","./node_modules/.pnpm/react-native-lottie-splash-screen@1.1.2_react-native@0.81.6_@babel+core@7.29.0_@react-n_8271ea4efc4abbeff38803e9497b8747/node_modules/react-native-lottie-splash-screen/index.d.ts","./node_modules/.pnpm/sf-symbols-typescript@2.2.0/node_modules/sf-symbols-typescript/dist/index.d.ts","./node_modules/.pnpm/@react-navigation+native-stack@7.13.0_@react-navigation+native@7.1.28_react-native@0.81_4e57e88debfb2b8c99b273d0603ce411/node_modules/@react-navigation/native-stack/lib/typescript/src/types.d.ts","./node_modules/.pnpm/@react-navigation+native-stack@7.13.0_@react-navigation+native@7.1.28_react-native@0.81_4e57e88debfb2b8c99b273d0603ce411/node_modules/@react-navigation/native-stack/lib/typescript/src/navigators/createNativeStackNavigator.d.ts","./node_modules/.pnpm/@react-navigation+native-stack@7.13.0_@react-navigation+native@7.1.28_react-native@0.81_4e57e88debfb2b8c99b273d0603ce411/node_modules/@react-navigation/native-stack/lib/typescript/src/views/NativeStackView.d.ts","./node_modules/.pnpm/@react-navigation+native-stack@7.13.0_@react-navigation+native@7.1.28_react-native@0.81_4e57e88debfb2b8c99b273d0603ce411/node_modules/@react-navigation/native-stack/lib/typescript/src/utils/useAnimatedHeaderHeight.d.ts","./node_modules/.pnpm/@react-navigation+native-stack@7.13.0_@react-navigation+native@7.1.28_react-native@0.81_4e57e88debfb2b8c99b273d0603ce411/node_modules/@react-navigation/native-stack/lib/typescript/src/index.d.ts","./node_modules/.pnpm/react-native-pager-view@6.9.1_react-native@0.81.6_@babel+core@7.29.0_@react-native-comm_26edae917cb08c1a35dfe3130f3d0bac/node_modules/react-native-pager-view/lib/typescript/PagerViewNativeComponent.d.ts","./node_modules/.pnpm/react-native-pager-view@6.9.1_react-native@0.81.6_@babel+core@7.29.0_@react-native-comm_26edae917cb08c1a35dfe3130f3d0bac/node_modules/react-native-pager-view/lib/typescript/PagerView.d.ts","./node_modules/.pnpm/react-native-pager-view@6.9.1_react-native@0.81.6_@babel+core@7.29.0_@react-native-comm_26edae917cb08c1a35dfe3130f3d0bac/node_modules/react-native-pager-view/lib/typescript/usePagerView.d.ts","./node_modules/.pnpm/react-native-pager-view@6.9.1_react-native@0.81.6_@babel+core@7.29.0_@react-native-comm_26edae917cb08c1a35dfe3130f3d0bac/node_modules/react-native-pager-view/lib/typescript/index.d.ts","./node_modules/.pnpm/react-native-tab-view@4.2.2_react-native-pager-view@6.9.1_react-native@0.81.6_@babel+co_10eae6de5282a074d67aadfadba29dca/node_modules/react-native-tab-view/lib/typescript/src/types.d.ts","./node_modules/.pnpm/react-native-tab-view@4.2.2_react-native-pager-view@6.9.1_react-native@0.81.6_@babel+co_10eae6de5282a074d67aadfadba29dca/node_modules/react-native-tab-view/lib/typescript/src/SceneMap.d.ts","./node_modules/.pnpm/react-native-tab-view@4.2.2_react-native-pager-view@6.9.1_react-native@0.81.6_@babel+co_10eae6de5282a074d67aadfadba29dca/node_modules/react-native-tab-view/lib/typescript/src/TabBarIndicator.d.ts","./node_modules/.pnpm/react-native-tab-view@4.2.2_react-native-pager-view@6.9.1_react-native@0.81.6_@babel+co_10eae6de5282a074d67aadfadba29dca/node_modules/react-native-tab-view/lib/typescript/src/TabBarItem.d.ts","./node_modules/.pnpm/react-native-tab-view@4.2.2_react-native-pager-view@6.9.1_react-native@0.81.6_@babel+co_10eae6de5282a074d67aadfadba29dca/node_modules/react-native-tab-view/lib/typescript/src/TabBar.d.ts","./node_modules/.pnpm/react-native-tab-view@4.2.2_react-native-pager-view@6.9.1_react-native@0.81.6_@babel+co_10eae6de5282a074d67aadfadba29dca/node_modules/react-native-tab-view/lib/typescript/src/TabView.d.ts","./node_modules/.pnpm/react-native-tab-view@4.2.2_react-native-pager-view@6.9.1_react-native@0.81.6_@babel+co_10eae6de5282a074d67aadfadba29dca/node_modules/react-native-tab-view/lib/typescript/src/index.d.ts","./src/components/NovelList.tsx","./node_modules/.pnpm/expo-linear-gradient@15.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15._0a9929182ed917bc8e4104b3401170d9/node_modules/expo-linear-gradient/build/NativeLinearGradient.types.d.ts","./node_modules/.pnpm/expo-linear-gradient@15.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15._0a9929182ed917bc8e4104b3401170d9/node_modules/expo-linear-gradient/build/LinearGradient.d.ts","./src/components/ListView.tsx","./node_modules/.pnpm/react-native-shimmer-placeholder@2.0.9_prop-types@15.8.1_react-native-linear-gradient@2_cdc39479d6b8b145f23f338ab104877a/node_modules/react-native-shimmer-placeholder/index.d.ts","./src/screens/browse/loadingAnimation/LoadingNovel.tsx","./src/screens/browse/loadingAnimation/SourceScreenSkeletonLoading.tsx","./src/components/NovelCover.tsx","./src/screens/library/components/LibraryNovelItem.tsx","./src/screens/library/SelectionContext.tsx","./src/screens/library/components/LibraryListView.tsx","./node_modules/.pnpm/@legendapp+list@2.0.19_react-native@0.81.6_@babel+core@7.29.0_@react-native-community+c_99b2894efb9c20046d6a56b612c1a024/node_modules/@legendapp/list/index.d.ts","./src/screens/library/components/LibraryBottomSheet/LibraryBottomSheet.tsx","./src/screens/library/components/Banner.tsx","./src/components/Actionbar/Actionbar.tsx","./src/screens/novel/components/SetCategoriesModal.tsx","./src/components/Common.tsx","./src/screens/library/LibraryScreen.tsx","./src/screens/updates/components/UpdatesSkeletonLoading.tsx","./src/screens/novel/components/Chapter/ChapterDownloadButtons.tsx","./src/screens/novel/components/ChapterItem.tsx","./src/screens/updates/components/UpdateNovelCard.tsx","./src/components/Context/UpdateContext.tsx","./src/screens/updates/UpdatesScreen.tsx","./src/screens/history/components/HistoryCard/HistoryCard.tsx","./src/screens/history/components/ClearHistoryDialog.tsx","./src/screens/history/components/HistorySkeletonLoading.tsx","./src/screens/history/HistoryScreen.tsx","./src/screens/browse/components/AvailableTab.tsx","./src/screens/browse/discover/DiscoverCard.tsx","./src/screens/browse/components/Modals/SourceSettings.tsx","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeableProps.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeable.d.ts","./node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_12e95349e655f17249fdded334090563/node_modules/react-native-gesture-handler/lib/typescript/components/ReanimatedSwipeable/index.d.ts","./src/screens/browse/components/PluginListItem.tsx","./src/screens/browse/components/PluginListItemSkeleton.tsx","./src/screens/browse/components/DeferredPluginListItem.tsx","./src/screens/browse/components/InstalledTab.tsx","./src/screens/browse/BrowseScreen.tsx","./src/screens/more/components/MoreHeader.tsx","./src/screens/more/MoreScreen.tsx","./src/navigators/BottomNavigator.tsx","./node_modules/.pnpm/expo-clipboard@8.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_react_37ad938eb958f1a09c6d41342ffe2306/node_modules/expo-clipboard/build/Clipboard.types.d.ts","./node_modules/.pnpm/expo-clipboard@8.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_react_37ad938eb958f1a09c6d41342ffe2306/node_modules/expo-clipboard/build/ClipboardPasteButton.d.ts","./node_modules/.pnpm/expo-clipboard@8.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_react_37ad938eb958f1a09c6d41342ffe2306/node_modules/expo-clipboard/build/Clipboard.d.ts","./src/screens/more/About.tsx","./src/screens/settings/SettingsScreen.tsx","./src/screens/settings/components/TrackerLoginDialog.tsx","./src/screens/settings/SettingsTrackerScreen.tsx","./src/screens/settings/SettingsReaderScreen/components/TabBar.tsx","./src/screens/settings/SettingsReaderScreen/ReaderTextSize.tsx","./src/screens/reader/components/ReaderBottomSheet/ReaderValueChange.tsx","./src/components/Common/ToggleButton.tsx","./src/screens/reader/components/ReaderBottomSheet/ReaderTextAlignSelector.tsx","./src/screens/settings/SettingsReaderScreen/Modals/FontPickerModal.tsx","./src/screens/settings/SettingsReaderScreen/tabs/DisplayTab.tsx","./src/components/ColorPickerModal/ColorPickerModal.tsx","./src/screens/reader/components/ReaderBottomSheet/ReaderThemeSelector.tsx","./src/screens/settings/SettingsReaderScreen/tabs/ThemeTab.tsx","./src/screens/settings/components/SettingSwitch.tsx","./src/screens/settings/SettingsReaderScreen/tabs/NavigationTab.tsx","./src/screens/settings/SettingsReaderScreen/tabs/AccessibilityTab.tsx","./src/screens/settings/SettingsReaderScreen/tabs/AdvancedTab.tsx","./src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx","./src/screens/settings/SettingsBackupScreen/Components/GoogleDriveModal.tsx","./src/screens/settings/SettingsBackupScreen/Components/SelfHostModal.tsx","./src/screens/settings/SettingsBackupScreen/index.tsx","./node_modules/.pnpm/@react-native-cookies+cookies@6.2.1_react-native@0.81.6_@babel+core@7.29.0_@react-nativ_11a6c54b02b017272c96802369be55d5/node_modules/@react-native-cookies/cookies/index.d.ts","./src/screens/settings/SettingsAdvancedScreen.tsx","./src/screens/settings/SettingsGeneralScreen/modals/DisplayModeModal.tsx","./src/screens/settings/SettingsGeneralScreen/modals/GridSizeModal.tsx","./src/screens/settings/components/DefaultChapterSortModal.tsx","./src/screens/settings/SettingsGeneralScreen/modals/NovelSortModal.tsx","./src/screens/settings/SettingsGeneralScreen/modals/NovelBadgesModal.tsx","./src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreen.tsx","./src/screens/more/TaskQueueScreen.tsx","./src/components/EmptyView.tsx","./src/screens/more/components/RemoveDownloadsDialog.tsx","./src/screens/more/DownloadsScreen.tsx","./src/components/ThemePicker/ThemePicker.tsx","./src/screens/settings/SettingsAppearanceScreen/LanguagePickerModal.tsx","./src/screens/settings/SettingsAppearanceScreen/SettingsAppearanceScreen.tsx","./node_modules/.pnpm/react-native-draggable-flatlist@4.0.3_@babel+core@7.29.0_react-native-gesture-handler@2_d2a98cccd36476a63855cba20e0caba7/node_modules/react-native-draggable-flatlist/lib/typescript/context/animatedValueContext.d.ts","./node_modules/.pnpm/react-native-draggable-flatlist@4.0.3_@babel+core@7.29.0_react-native-gesture-handler@2_d2a98cccd36476a63855cba20e0caba7/node_modules/react-native-draggable-flatlist/lib/typescript/constants.d.ts","./node_modules/.pnpm/react-native-draggable-flatlist@4.0.3_@babel+core@7.29.0_react-native-gesture-handler@2_d2a98cccd36476a63855cba20e0caba7/node_modules/react-native-draggable-flatlist/lib/typescript/types.d.ts","./node_modules/.pnpm/react-native-draggable-flatlist@4.0.3_@babel+core@7.29.0_react-native-gesture-handler@2_d2a98cccd36476a63855cba20e0caba7/node_modules/react-native-draggable-flatlist/lib/typescript/components/DraggableFlatList.d.ts","./node_modules/.pnpm/react-native-draggable-flatlist@4.0.3_@babel+core@7.29.0_react-native-gesture-handler@2_d2a98cccd36476a63855cba20e0caba7/node_modules/react-native-draggable-flatlist/lib/typescript/hooks/useOnCellActiveAnimation.d.ts","./node_modules/.pnpm/react-native-draggable-flatlist@4.0.3_@babel+core@7.29.0_react-native-gesture-handler@2_d2a98cccd36476a63855cba20e0caba7/node_modules/react-native-draggable-flatlist/lib/typescript/components/CellDecorators.d.ts","./node_modules/.pnpm/react-native-draggable-flatlist@4.0.3_@babel+core@7.29.0_react-native-gesture-handler@2_d2a98cccd36476a63855cba20e0caba7/node_modules/react-native-draggable-flatlist/lib/typescript/components/NestableDraggableFlatList.d.ts","./node_modules/.pnpm/react-native-draggable-flatlist@4.0.3_@babel+core@7.29.0_react-native-gesture-handler@2_d2a98cccd36476a63855cba20e0caba7/node_modules/react-native-draggable-flatlist/lib/typescript/components/NestableScrollContainer.d.ts","./node_modules/.pnpm/react-native-draggable-flatlist@4.0.3_@babel+core@7.29.0_react-native-gesture-handler@2_d2a98cccd36476a63855cba20e0caba7/node_modules/react-native-draggable-flatlist/lib/typescript/index.d.ts","./src/screens/Categories/components/AddCategoryModal.tsx","./src/screens/Categories/components/DeleteCategoryModal.tsx","./src/screens/Categories/components/CategoryCard.tsx","./src/screens/Categories/components/CategorySkeletonLoading.tsx","./src/screens/Categories/CategoriesScreen.tsx","./src/screens/settings/SettingsRepositoryScreen/components/AddRepositoryModal.tsx","./src/screens/settings/SettingsRepositoryScreen/components/DeleteRepositoryModal.tsx","./src/screens/settings/SettingsRepositoryScreen/components/RepositoryCard.tsx","./src/screens/settings/SettingsRepositoryScreen/SettingsRepositoryScreen.tsx","./src/screens/StatsScreen/StatsScreen.tsx","./src/navigators/MoreStack.tsx","./src/screens/BrowseSourceScreen/components/FilterBottomSheet.tsx","./src/screens/BrowseSourceScreen/BrowseSourceScreen.tsx","./src/screens/browse/loadingAnimation/GlobalSearchSkeletonLoading.tsx","./src/screens/GlobalSearchScreen/components/GlobalSearchResultsList.tsx","./src/screens/GlobalSearchScreen/GlobalSearchScreen.tsx","./src/screens/browse/migration/MigrationSourceItem.tsx","./src/screens/browse/migration/Migration.tsx","./src/screens/browse/SourceNovels.tsx","./src/screens/browse/globalsearch/GlobalSearchNovelCover.tsx","./src/screens/browse/migration/MigrationNovelList.tsx","./src/screens/browse/migration/MigrationNovels.tsx","./src/components/ErrorView/ErrorView.tsx","./src/screens/browse/discover/DiscoverNovelCard/index.tsx","./src/screens/browse/loadingAnimation/MalLoading.tsx","./src/screens/browse/discover/MalTopNovels.tsx","./src/screens/browse/loadingAnimation/TrackerLoading.tsx","./src/screens/browse/discover/AniListTopNovels.tsx","./src/components/NewUpdateDialog.tsx","./src/screens/browse/settings/modals/ConcurrentSearchesModal.tsx","./src/screens/browse/settings/BrowseSettings.tsx","./src/screens/WebviewScreen/components/Appbar.tsx","./src/screens/WebviewScreen/components/Menu.tsx","./src/screens/WebviewScreen/WebviewScreen.tsx","./src/screens/onboarding/ThemeSelectionStep.tsx","./src/screens/onboarding/OnboardingScreen.tsx","./src/screens/novel/components/JumpToChapterModal.tsx","./src/screens/novel/components/EditInfoModal.tsx","./src/screens/novel/components/DownloadCustomChapterModal.tsx","./src/screens/novel/components/LoadingAnimation/NovelScreenLoading.tsx","./node_modules/.pnpm/@cd-z+epub-constructor@3.0.3/node_modules/@cd-z/epub-constructor/dist/src/main.d.ts","./node_modules/.pnpm/@cd-z+epub-constructor@3.0.3/node_modules/@cd-z/epub-constructor/dist/index.d.ts","./node_modules/.pnpm/react-native-zip-archive@6.1.2_react-native@0.81.6_@babel+core@7.29.0_@react-native-com_e59d994b6bc9989ca4080cd3963d015d/node_modules/react-native-zip-archive/index.d.ts","./node_modules/.pnpm/react-native-saf-x@2.2.3_react-native@0.81.6_@babel+core@7.29.0_@react-native-community_ce39d6c1d2b205a55f7b4a6de30c0e2b/node_modules/react-native-saf-x/lib/typescript/index.d.ts","./node_modules/.pnpm/react-native-file-access@3.2.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-com_395f63a3765095ce8d15fe85c60e6830/node_modules/react-native-file-access/lib/typescript/NativeFileAccess.d.ts","./node_modules/.pnpm/react-native-file-access@3.2.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-com_395f63a3765095ce8d15fe85c60e6830/node_modules/react-native-file-access/lib/typescript/types.d.ts","./node_modules/.pnpm/react-native-file-access@3.2.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-com_395f63a3765095ce8d15fe85c60e6830/node_modules/react-native-file-access/lib/typescript/util.d.ts","./node_modules/.pnpm/react-native-file-access@3.2.0_react-native@0.81.6_@babel+core@7.29.0_@react-native-com_395f63a3765095ce8d15fe85c60e6830/node_modules/react-native-file-access/lib/typescript/index.d.ts","./node_modules/.pnpm/@cd-z+react-native-epub-creator@3.0.0_react-native@0.81.6_@babel+core@7.29.0_@react-nat_4ec444dd92fa7bece6f136225b681eb4/node_modules/@cd-z/react-native-epub-creator/src/main.ts","./node_modules/.pnpm/@cd-z+react-native-epub-creator@3.0.0_react-native@0.81.6_@babel+core@7.29.0_@react-nat_4ec444dd92fa7bece6f136225b681eb4/node_modules/@cd-z/react-native-epub-creator/index.ts","./src/screens/novel/components/ExportEpubModal.tsx","./src/screens/novel/components/ExportNovelAsEpubButton.tsx","./src/screens/novel/components/NovelAppbar.tsx","./src/screens/novel/components/Info/NovelInfoComponents.tsx","./src/screens/novel/components/Info/ReadButton.tsx","./src/screens/novel/components/NovelSummary/NovelSummary.tsx","./src/screens/novel/components/NovelScreenButtonGroup/NovelScreenButtonGroup.tsx","./src/components/Skeleton/useLoadingColors.tsx","./src/components/Skeleton/Skeleton.tsx","./src/screens/novel/components/Info/NovelInfoHeader.tsx","./src/screens/novel/components/NovelBottomSheet.tsx","./src/screens/novel/components/PageNavigationBottomSheet.tsx","./node_modules/.pnpm/expo-haptics@15.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_react-_3bea76d551b3809f8d4854b1b6603f64/node_modules/expo-haptics/build/Haptics.types.d.ts","./node_modules/.pnpm/expo-haptics@15.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_react-_3bea76d551b3809f8d4854b1b6603f64/node_modules/expo-haptics/build/Haptics.d.ts","./node_modules/.pnpm/expo-file-system@19.0.21_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_r_2b783b467157c6462b3ef4b14a24dc4f/node_modules/expo-file-system/src/legacy/FileSystem.types.ts","./node_modules/.pnpm/expo-file-system@19.0.21_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_r_2b783b467157c6462b3ef4b14a24dc4f/node_modules/expo-file-system/src/legacy/types.ts","./node_modules/.pnpm/expo-file-system@19.0.21_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_r_2b783b467157c6462b3ef4b14a24dc4f/node_modules/expo-file-system/src/legacy/ExponentFileSystemShim.ts","./node_modules/.pnpm/expo-file-system@19.0.21_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_r_2b783b467157c6462b3ef4b14a24dc4f/node_modules/expo-file-system/src/legacy/ExponentFileSystem.ts","./node_modules/.pnpm/expo-file-system@19.0.21_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_r_2b783b467157c6462b3ef4b14a24dc4f/node_modules/expo-file-system/src/legacy/FileSystem.ts","./node_modules/.pnpm/expo-file-system@19.0.21_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_r_2b783b467157c6462b3ef4b14a24dc4f/node_modules/expo-file-system/src/legacy/index.ts","./node_modules/.pnpm/expo-file-system@19.0.21_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_r_2b783b467157c6462b3ef4b14a24dc4f/node_modules/expo-file-system/legacy.ts","./src/screens/novel/components/PagePaginationControl.tsx","./src/screens/novel/components/NovelScreenList.tsx","./src/screens/novel/NovelScreen.tsx","./src/screens/reader/ChapterContext.tsx","./src/screens/reader/components/ReaderAppbar.tsx","./src/screens/reader/components/ReaderFooter.tsx","./src/screens/reader/components/WebViewReader.tsx","./src/screens/reader/components/ReaderBottomSheet/ReaderSheetPreferenceItem.tsx","./node_modules/.pnpm/@react-native-community+slider@5.1.2/node_modules/@react-native-community/slider/typings/index.d.ts","./src/screens/reader/components/ReaderBottomSheet/TextSizeSlider.tsx","./src/screens/reader/components/ReaderBottomSheet/ReaderFontPicker.tsx","./src/screens/reader/components/ReaderBottomSheet/TTSTab.tsx","./src/screens/reader/components/ReaderBottomSheet/ReaderBottomSheet.tsx","./src/screens/reader/components/ChapterDrawer/RenderListChapter.tsx","./src/screens/reader/components/ChapterDrawer/index.tsx","./src/screens/reader/components/SkeletonLines.tsx","./src/screens/reader/ChapterLoadingScreen/ChapterLoadingScreen.tsx","./node_modules/.pnpm/expo-keep-awake@15.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_rea_4ba791bc2b954fd2f96e01ac3f8dc306/node_modules/expo-keep-awake/build/KeepAwake.types.d.ts","./node_modules/.pnpm/expo-keep-awake@15.0.8_expo@54.0.33_@babel+core@7.29.0_react-native-webview@13.15.0_rea_4ba791bc2b954fd2f96e01ac3f8dc306/node_modules/expo-keep-awake/build/index.d.ts","./src/screens/reader/components/KeepScreenAwake.tsx","./node_modules/.pnpm/react-native-drawer-layout@4.2.2_react-native-gesture-handler@2.30.0_react-native@0.81._f75661c15c425d0faf2f3d76374bd32e/node_modules/react-native-drawer-layout/lib/typescript/src/utils/DrawerGestureContext.d.ts","./node_modules/.pnpm/react-native-drawer-layout@4.2.2_react-native-gesture-handler@2.30.0_react-native@0.81._f75661c15c425d0faf2f3d76374bd32e/node_modules/react-native-drawer-layout/lib/typescript/src/utils/DrawerProgressContext.d.ts","./node_modules/.pnpm/react-native-drawer-layout@4.2.2_react-native-gesture-handler@2.30.0_react-native@0.81._f75661c15c425d0faf2f3d76374bd32e/node_modules/react-native-drawer-layout/lib/typescript/src/utils/useDrawerProgress.d.ts","./node_modules/.pnpm/react-native-drawer-layout@4.2.2_react-native-gesture-handler@2.30.0_react-native@0.81._f75661c15c425d0faf2f3d76374bd32e/node_modules/react-native-drawer-layout/lib/typescript/src/types.d.ts","./node_modules/.pnpm/react-native-drawer-layout@4.2.2_react-native-gesture-handler@2.30.0_react-native@0.81._f75661c15c425d0faf2f3d76374bd32e/node_modules/react-native-drawer-layout/lib/typescript/src/views/Drawer.d.ts","./node_modules/.pnpm/react-native-drawer-layout@4.2.2_react-native-gesture-handler@2.30.0_react-native@0.81._f75661c15c425d0faf2f3d76374bd32e/node_modules/react-native-drawer-layout/lib/typescript/src/index.d.ts","./src/screens/reader/ReaderScreen.tsx","./src/navigators/ReaderStack.tsx","./src/navigators/Main.tsx","./App.tsx","./src/components/Context/__mocks__/LibraryContext.tsx","./src/screens/novel/components/NovelDrawer.tsx","./src/screens/settings/SettingsLibraryScreen/DefaultCategoryDialog.tsx","./src/screens/settings/SettingsLibraryScreen/SettingsLibraryScreen.tsx","./src/screens/settings/SettingsReaderScreen/Modals/VoicePickerModal.tsx","./src/screens/settings/components/ConnectionModal.tsx","./node_modules/.pnpm/@jest+expect-utils@29.7.0/node_modules/@jest/expect-utils/build/index.d.ts","./node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/index.d.ts","./node_modules/.pnpm/@sinclair+typebox@0.27.10/node_modules/@sinclair/typebox/typebox.d.ts","./node_modules/.pnpm/@jest+schemas@29.6.3/node_modules/@jest/schemas/build/index.d.ts","./node_modules/.pnpm/pretty-format@29.7.0/node_modules/pretty-format/build/index.d.ts","./node_modules/.pnpm/jest-diff@29.7.0/node_modules/jest-diff/build/index.d.ts","./node_modules/.pnpm/jest-matcher-utils@29.7.0/node_modules/jest-matcher-utils/build/index.d.ts","./node_modules/.pnpm/expect@29.7.0/node_modules/expect/build/index.d.ts","./node_modules/.pnpm/@types+jest@29.5.14/node_modules/@types/jest/index.d.ts"],"fileIdsList":[[140,229,457,918,1250,1313,1321,1325,1328,1330,1331,1332,1344,1517,1763,1865,2027,2031,2119,2120,2328],[1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1763,1865,1943,2027,2031,2045,2046],[55,235,238,1250,1313,1321,1325,1328,1330,1331,1332,1344],[236,1250,1313,1321,1325,1328,1330,1331,1332,1344],[55,235,240,1250,1313,1321,1325,1328,1330,1331,1332,1344],[55,235,242,1250,1313,1321,1325,1328,1330,1331,1332,1344],[48,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,275,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2269],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2270,2277],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2270,2271,2272,2276],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1945,1961],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1961,1962],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1865,1944,1945,1948,1951,1955,1960],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1946],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1946,1947],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1945,1961],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1949],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1949,1950],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1945,1963],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2017],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2018],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1821],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1952],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1952,1953,1954],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1961],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1957],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1957,1958,1959],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1865,1945,1956,1963],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1945,1964],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1964,1965],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1944,1963],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1967],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1968],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1966],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1988,2013],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1988],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1988],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1944],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1987,1988,1989,1990,1991,1992,2014],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1945],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1865,2023],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2023,2024],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2020],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2021],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1945],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1945],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1821,1945],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1971,1975,1977,1978,1979],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1865,1944,1945,1961],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1945,1966],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1945],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1978],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1975],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1971],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1980],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2015],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1749],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1944,1945],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1944,1945,1948,1951,1955,1960,1963,1966,1969,1970,1972,1973,1974,1976,1981,1982,1983,1984,1985,1986,2015,2016,2019,2022,2025,2026],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1865,1944],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2338],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749],[1246,1250,1313,1321,1325,1328,1330,1331,1332,1344],[896,1250,1313,1321,1325,1328,1330,1331,1332,1344],[896,897,898,1250,1313,1321,1325,1328,1330,1331,1332,1344],[155,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1527,1528,1529,1530,1531,1532,1533,1534,1535,1536],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1528,1529],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1528,1531],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1528],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1528],[250,251,1250,1313,1321,1325,1328,1330,1331,1332,1344],[250,251,252,253,254,1250,1313,1321,1325,1328,1330,1331,1332,1344],[250,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,262,1250,1313,1321,1325,1328,1330,1331,1332,1344],[262,1250,1313,1321,1325,1328,1330,1331,1332,1344],[263,264,265,1250,1313,1321,1325,1328,1330,1331,1332,1344],[261,266,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1797],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1797,1798,1799,1800,1801,1802,1803,1804,1805,1806],[411,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,1797],[140,229,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,1763,1796],[229,411,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,1763,1797],[140,397,398,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,411,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,397,1250,1313,1321,1325,1328,1330,1331,1332,1344],[398,1250,1313,1321,1325,1328,1330,1331,1332,1344],[397,1250,1313,1321,1325,1328,1330,1331,1332,1344],[397,398,1250,1313,1321,1325,1328,1330,1331,1332,1344],[397,398,399,400,401,402,403,404,405,406,407,408,409,410,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,432,433,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,397,398,411,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,397,411,1250,1313,1321,1325,1328,1330,1331,1332,1344],[415,1250,1313,1321,1325,1328,1330,1331,1332,1344],[431,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,411,1250,1313,1321,1325,1328,1330,1331,1332,1344],[229,411,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,1772],[411,1250,1313,1321,1325,1328,1330,1331,1332,1344,1775],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1775],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1775],[140,229,411,1250,1313,1321,1325,1328,1330,1331,1332,1344,1763],[140,229,411,452,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1770,1771,1772,1773,1774,1775,1776,1777,1778,1779,1780,1781,1782,1783,1784,1785,1786,1787,1788,1789,1790,1791,1792,1793,1794,1795],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2122,2123,2124,2125],[411,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,2122],[229,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,2119,2121],[140,229,438,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,434,435,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,435,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,435,442,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,434,435,436,1250,1313,1321,1325,1328,1330,1331,1332,1344],[434,435,436,437,438,439,440,441,443,444,445,446,447,448,449,450,451,1250,1313,1321,1325,1328,1330,1331,1332,1344],[435,1250,1313,1321,1325,1328,1330,1331,1332,1344],[434,1250,1313,1321,1325,1328,1330,1331,1332,1344],[397,434,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,434,1250,1313,1321,1325,1328,1330,1331,1332,1344],[391,1250,1313,1321,1325,1328,1330,1331,1332,1344],[391,394,1250,1313,1321,1325,1328,1330,1331,1332,1344],[391,392,1250,1313,1321,1325,1328,1330,1331,1332,1344],[391,392,393,394,395,396,1250,1313,1321,1325,1328,1330,1331,1332,1344],[392,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2032],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2032,2033,2034,2035,2036,2037,2038,2039,2040,2041,2042,2043],[411,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,2032],[140,229,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,1796],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2032],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1865],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2044],[140,397,411,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,2032],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1996],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2000],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1995],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1994,1997,1998],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1999,2003],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,2004],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1995,1996,1999,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1997,1999],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1998,1999],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1996],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2007],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1993],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1918],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1914],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1917],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1916],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1907],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1911,1942],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1910],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1907,1908,1909],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1907,1912,1913,1915,1920,1925,1926,1927,1928,1929,1930,1931,1932,1941],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1920],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1907,1908,1914,1921,1922,1923],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1907,1918,1921,1923,1924],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1914],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1925],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1934,1936,1938,1940],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1939],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1938],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1933],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1914,1938],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1937],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1934,1936,1940],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1935],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1919],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1907,1921,1923,1924],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1369],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2340,2343],[933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,975,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,991,992,993,994,995,996,997,998,999,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126,1127,1128,1129,1130,1131,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188,1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1202,1203,1204,1205,1206,1207,1208,1209,1210,1211,1212,1213,1214,1215,1216,1217,1218,1219,1220,1221,1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237,1250,1313,1321,1325,1328,1330,1331,1332,1344],[921,923,924,925,926,927,928,929,930,931,932,933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[921,922,924,925,926,927,928,929,930,931,932,933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[922,923,924,925,926,927,928,929,930,931,932,933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[921,922,923,925,926,927,928,929,930,931,932,933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[921,922,923,924,926,927,928,929,930,931,932,933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[921,922,923,924,925,927,928,929,930,931,932,933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[921,922,923,924,925,926,928,929,930,931,932,933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[921,922,923,924,925,926,927,929,930,931,932,933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[921,922,923,924,925,926,927,928,930,931,932,933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[921,922,923,924,925,926,927,928,929,931,932,933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[921,922,923,924,925,926,927,928,929,930,932,933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[921,922,923,924,925,926,927,928,929,930,931,933,1250,1313,1321,1325,1328,1330,1331,1332,1344],[921,922,923,924,925,926,927,928,929,930,931,932,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1310,1311,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1312,1313,1321,1325,1328,1330,1331,1332,1344],[1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1352],[1250,1313,1314,1319,1321,1324,1325,1328,1330,1331,1332,1334,1344,1349,1361],[1250,1313,1314,1315,1321,1324,1325,1328,1330,1331,1332,1344],[1250,1313,1316,1321,1325,1328,1330,1331,1332,1344,1362],[1250,1313,1317,1318,1321,1325,1328,1330,1331,1332,1335,1344],[1250,1313,1318,1321,1325,1328,1330,1331,1332,1344,1349,1358],[1250,1313,1319,1321,1324,1325,1328,1330,1331,1332,1334,1344],[1250,1312,1313,1320,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1322,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1323,1324,1325,1328,1330,1331,1332,1344],[1250,1312,1313,1321,1324,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1324,1325,1326,1328,1330,1331,1332,1344,1349,1361],[1250,1313,1321,1324,1325,1326,1328,1330,1331,1332,1344,1349,1352],[1250,1300,1313,1321,1324,1325,1327,1328,1330,1331,1332,1334,1344,1349,1361],[1250,1313,1321,1324,1325,1327,1328,1330,1331,1332,1334,1344,1349,1358,1361],[1250,1313,1321,1325,1327,1328,1329,1330,1331,1332,1344,1349,1358,1361],[1248,1249,1250,1251,1252,1253,1254,1255,1256,1257,1258,1301,1302,1303,1304,1305,1306,1307,1308,1309,1310,1311,1312,1313,1314,1315,1316,1317,1318,1319,1320,1321,1322,1323,1324,1325,1326,1327,1328,1329,1330,1331,1332,1333,1334,1335,1336,1337,1338,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355,1356,1357,1358,1359,1360,1361,1362,1363,1364,1365,1366,1367,1368],[1250,1313,1321,1324,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1333,1344,1361],[1250,1313,1321,1324,1325,1328,1330,1331,1332,1334,1344,1349],[1250,1313,1321,1325,1328,1330,1331,1332,1335,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1336,1344],[1250,1313,1321,1324,1325,1328,1330,1331,1332,1339,1344],[1250,1310,1311,1312,1313,1314,1315,1316,1317,1318,1319,1320,1321,1322,1323,1324,1325,1326,1327,1328,1329,1330,1331,1332,1333,1334,1335,1336,1337,1338,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355,1356,1357,1358,1359,1360,1361,1362,1363,1364,1365,1366,1367,1368],[1250,1313,1321,1325,1328,1330,1331,1332,1341,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1342,1344],[1250,1313,1318,1321,1325,1328,1330,1331,1332,1334,1344,1352],[1250,1313,1321,1324,1325,1328,1330,1331,1332,1344,1345],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1346,1362,1365],[1250,1313,1321,1324,1325,1328,1330,1331,1332,1344,1349,1351,1352],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1350,1352],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1352,1362],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1353],[1250,1310,1313,1321,1325,1328,1330,1331,1332,1344,1349,1355,1361],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1349,1354],[1250,1313,1321,1324,1325,1328,1330,1331,1332,1344,1356,1357],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1356,1357],[1250,1313,1318,1321,1325,1328,1330,1331,1332,1334,1344,1349,1358],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1359],[1250,1313,1321,1325,1328,1330,1331,1332,1334,1344,1360],[1250,1313,1321,1325,1327,1328,1330,1331,1332,1342,1344,1361],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1362,1363],[1250,1313,1318,1321,1325,1328,1330,1331,1332,1344,1363],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1349,1364],[1250,1313,1321,1325,1328,1330,1331,1332,1333,1344,1365],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1366],[1250,1313,1316,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1318,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1362],[1250,1300,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1361],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1367],[1250,1313,1321,1325,1328,1330,1331,1332,1339,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1357],[1250,1300,1313,1321,1324,1325,1326,1328,1330,1331,1332,1339,1344,1349,1352,1361,1364,1365,1367],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1349,1368],[138,139,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1384],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1496],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1494,1495],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1372,1394],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1372,1403],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1372,1397,1403],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1369,1372,1396,1397,1398,1399,1400,1401,1402],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1369,1372,1396,1397,1403,1404,1405],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1369,1372,1396,1397,1403,1404],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1369,1372,1384,1395],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1372,1396,1397,1406],[268,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1388,1389,1393],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1389],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1388,1389,1390,1391,1392],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1388,1389],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1388],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1385,1386,1387],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1385],[60,1250,1313,1321,1325,1328,1330,1331,1332,1344],[59,1250,1313,1321,1325,1328,1330,1331,1332,1344],[61,92,94,1250,1313,1321,1325,1328,1330,1331,1332,1344],[61,1250,1313,1321,1325,1328,1330,1331,1332,1344],[61,94,95,1250,1313,1321,1325,1328,1330,1331,1332,1344],[61,92,95,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1372],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1371],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1370],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1372,1376,1377,1378,1379,1380,1381,1382],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1370,1372],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1372,1375],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1358],[469,474,475,476,478,886,888,889,894,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,524,886,888,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,863,1250,1313,1321,1325,1328,1330,1331,1332,1344,1868],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1871,1872],[469,470,472,477,524,836,858,865,866,876,889,1250,1313,1321,1325,1328,1330,1331,1332,1344,1868],[469,470,888,1250,1313,1321,1325,1328,1330,1331,1332,1344],[471,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[515,521,540,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,489,515,1250,1313,1321,1325,1328,1330,1331,1332,1344],[481,482,483,484,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,516,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,480,520,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,520,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,520,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,515,519,885,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,515,520,885,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,520,885,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,491,520,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,515,520,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[480,481,482,483,484,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,516,517,520,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,479,520,885,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,498,520,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,515,520,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,498,515,520,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,491,515,520,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,515,518,520,521,523,524,528,532,533,534,535,537,539,540,543,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,515,518,523,524,525,532,539,540,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,515,518,520,1250,1313,1321,1325,1328,1330,1331,1332,1344],[479,480,481,482,483,484,490,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,510,511,512,513,514,515,516,517,518,519,520,523,528,529,530,531,532,533,538,539,540,541,542,543,544,545,546,547,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,515,518,520,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,511,515,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,515,521,539,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,486,515,522,526,527,533,539,540,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[528,529,530,531,540,541,542,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,486,510,515,520,522,526,527,530,533,539,540,542,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,518,520,533,540,541,543,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,515,524,526,527,533,539,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,526,527,536,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,523,526,527,533,539,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,486,515,518,521,522,526,527,533,539,540,543,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[478,486,515,518,521,522,523,539,541,887,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,486,515,520,521,522,526,527,533,539,540,886,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,479,515,517,523,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,489,524,533,536,538,540,1250,1313,1321,1325,1328,1330,1331,1332,1344],[486,522,542,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,490,509,510,512,513,514,519,520,885,888,1250,1313,1321,1325,1328,1330,1331,1332,1344],[490,510,512,513,514,515,518,519,520,523,548,889,894,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,486,521,522,542,885,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,520,548,582,605,635,654,690,711,712,759,793,826,849,876,884,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,885,888,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[580,585,600,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,489,580,1250,1313,1321,1325,1328,1330,1331,1332,1344],[552,553,554,555,556,557,558,559,560,561,563,564,565,566,567,568,569,570,571,572,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,551,582,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,582,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,575,580,581,885,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,580,582,885,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,582,885,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,580,582,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[551,552,553,554,555,556,557,558,559,560,561,563,564,565,566,567,568,569,570,571,572,582,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,550,582,885,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,562,582,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,562,580,582,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,471,477,478,486,524,574,580,582,583,585,592,593,594,595,596,597,599,600,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,524,574,580,587,592,600,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,574,580,582,1250,1313,1321,1325,1328,1330,1331,1332,1344],[549,550,551,552,553,554,555,556,557,558,559,560,561,563,564,565,566,567,568,569,570,571,572,574,575,576,577,578,579,580,581,582,583,584,586,587,588,589,590,591,592,593,598,599,600,601,602,603,604,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,574,580,582,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,576,580,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,524,526,527,580,593,599,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,580,585,599,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,522,526,527,580,593,599,600,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[584,588,589,590,591,600,601,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,486,522,526,527,580,581,582,584,589,593,599,600,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,486,574,582,583,593,600,601,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,526,527,580,593,599,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,526,527,587,593,599,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,478,486,522,526,527,574,580,583,585,593,599,600,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[478,486,522,574,580,585,587,599,601,887,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,522,526,527,580,582,585,593,599,600,886,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,550,580,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,471,477,478,489,524,536,593,598,600,1250,1313,1321,1325,1328,1330,1331,1332,1344],[522,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,549,573,575,577,578,579,581,582,885,888,1250,1313,1321,1325,1328,1330,1331,1332,1344],[549,574,575,577,578,579,580,581,582,585,587,605,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,486,522,584,585,586,885,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,472,474,475,477,478,485,487,489,526,885,886,887,888,889,890,891,892,893,1250,1313,1321,1325,1328,1330,1331,1332,1344],[631,648,649,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,631,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[607,608,609,610,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,632,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,631,635,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,635,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,635,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,489,631,634,885,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,631,635,886,889,894,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,635,885,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,611,631,635,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[607,608,609,610,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,632,635,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,631,635,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,486,524,631,637,638,639,640,641,643,644,645,646,649,650,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,524,525,631,635,637,643,644,649,650,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,631,633,635,1250,1313,1321,1325,1328,1330,1331,1332,1344],[606,607,608,609,610,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,628,629,630,631,632,633,634,635,637,638,639,640,642,643,644,645,646,647,648,649,650,651,652,653,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,631,633,635,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,522,526,631,643,645,649,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[637,639,644,646,649,650,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,522,526,631,643,645,649,888,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,486,638,645,646,649,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,524,526,631,643,645,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,486,522,526,631,633,636,638,643,645,649,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[478,486,522,631,633,636,643,646,648,887,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,522,526,631,643,645,649,886,888,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,631,648,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,524,642,645,649,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[486,522,639,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,606,627,628,629,630,634,635,885,888,1250,1313,1321,1325,1328,1330,1331,1332,1344],[606,628,629,630,631,634,635,648,654,889,894,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,486,522,636,639,647,649,885,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[686,691,706,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,686,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[655,656,657,658,660,662,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,687,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,690,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,690,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,659,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,686,689,885,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,686,690,885,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,664,690,885,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,686,690,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,663,686,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[655,656,657,658,660,662,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,687,690,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,670,690,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,690,885,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,663,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,471,477,478,524,686,691,692,694,695,696,697,700,702,703,704,705,706,707,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,524,525,686,690,694,699,702,703,706,707,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,686,688,690,1250,1313,1321,1325,1328,1330,1331,1332,1344],[655,656,657,658,660,661,662,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,683,684,685,686,687,688,689,690,692,693,694,697,698,699,701,702,703,704,705,706,707,708,709,710,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,686,688,690,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,524,526,686,702,704,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,686,691,702,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,526,686,690,702,704,706,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[693,694,703,705,706,707,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,486,526,527,686,690,693,702,703,704,706,888,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,692,704,705,706,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,526,686,699,702,704,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,478,486,522,526,684,685,686,688,691,692,702,704,706,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[478,486,522,686,688,691,699,702,705,887,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,478,526,686,690,702,704,706,886,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,686,699,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,471,477,478,524,701,704,706,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[486,522,693,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,661,682,683,684,685,689,690,885,888,1250,1313,1321,1325,1328,1330,1331,1332,1344],[661,683,684,685,686,689,690,691,699,705,711,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,486,522,691,693,698,885,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,863,899,1250,1313,1321,1325,1328,1330,1331,1332,1344],[900,901,1250,1313,1321,1325,1328,1330,1331,1332,1344],[477,525,900,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,472,477,524,836,858,865,866,876,889,899,1250,1313,1321,1325,1328,1330,1331,1332,1344],[886,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[754,760,772,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,526,754,760,768,784,787,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,471,477,478,524,754,757,759,760,761,762,767,768,771,772,775,776,777,778,780,781,782,783,785,787,788,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,486,526,527,754,762,763,787,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[777,778,780,781,782,783,785,786,787,788,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,526,527,754,762,765,787,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,526,527,762,776,787,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,526,527,536,779,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,526,527,762,766,787,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,522,526,762,772,773,787,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,471,477,524,525,762,768,772,786,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,522,526,527,754,760,762,764,787,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,489,754,1250,1313,1321,1325,1328,1330,1331,1332,1344],[713,714,717,718,720,721,722,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,755,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,723,759,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,759,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,754,759,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,749,754,758,885,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,754,759,885,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,719,759,1250,1313,1321,1325,1328,1330,1331,1332,1344],[713,714,715,717,718,720,721,722,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,755,756,759,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,712,759,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,725,754,759,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,754,759,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,759,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,719,725,754,759,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,719,754,759,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,524,754,757,767,771,772,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,754,757,759,1250,1313,1321,1325,1328,1330,1331,1332,1344],[712,713,714,715,716,717,718,720,721,722,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,749,750,751,752,753,754,755,756,757,758,759,761,762,763,764,765,766,767,768,769,770,771,772,773,774,777,778,780,781,782,783,785,786,787,788,789,790,791,792,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,754,757,759,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,750,754,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,524,526,527,754,762,768,787,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,754,760,768,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,478,486,522,754,762,768,772,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[763,764,765,766,769,772,773,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,478,486,522,754,758,759,762,764,768,769,772,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,757,759,761,768,772,773,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,754,762,768,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,536,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,762,768,771,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,478,486,522,754,757,760,761,762,768,772,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[478,486,522,754,757,760,771,773,887,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,478,486,522,754,759,760,762,768,772,886,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,712,754,756,771,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,489,536,768,772,1250,1313,1321,1325,1328,1330,1331,1332,1344],[486,522,769,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,716,748,749,751,752,753,758,759,888,1250,1313,1321,1325,1328,1330,1331,1332,1344],[716,749,751,752,753,754,758,759,760,771,793,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,486,522,759,760,769,770,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,489,1250,1313,1321,1325,1328,1330,1331,1332,1344],[478,885,886,887,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,473,474,475,476,478,886,888,889,894,1250,1313,1321,1325,1328,1330,1331,1332,1344],[885,1250,1313,1321,1325,1328,1330,1331,1332,1344],[889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[825,844,1250,1313,1321,1325,1328,1330,1331,1332,1344],[794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,814,815,816,817,818,819,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,826,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,826,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,825,885,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,825,826,885,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,826,885,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,825,826,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,489,826,885,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,814,815,816,817,818,819,826,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,805,826,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,813,885,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,471,477,524,825,827,828,829,831,835,838,840,841,842,843,844,845,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,524,525,825,826,827,834,840,841,844,845,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,814,815,816,817,818,819,821,822,823,824,825,826,827,829,830,831,839,840,841,842,843,844,845,846,847,848,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,821,825,826,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,821,825,826,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,825,840,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,526,825,826,840,842,844,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[827,830,841,843,844,845,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,526,527,825,826,840,841,842,844,888,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,829,842,843,844,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,526,825,834,840,842,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,478,486,522,526,821,825,829,840,842,844,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[478,486,522,821,825,840,843,887,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,526,825,826,840,842,844,886,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,825,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,471,477,478,524,839,842,844,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[486,522,830,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,820,822,823,824,826,885,888,1250,1313,1321,1325,1328,1330,1331,1332,1344],[822,823,824,825,826,849,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,486,522,830,832,833,844,885,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,471,472,477,478,524,837,839,842,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,472,477,478,524,836,840,842,844,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[474,475,1250,1313,1321,1325,1328,1330,1331,1332,1344],[485,487,1250,1313,1321,1325,1328,1330,1331,1332,1344],[486,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[474,475,476,485,487,488,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,473,478,522,886,888,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[852,866,880,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,880,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[850,872,873,874,878,881,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,884,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,880,883,885,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,880,884,885,886,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[850,872,873,874,878,881,884,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,877,880,884,885,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,884,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,880,884,886,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,471,477,478,524,852,857,858,859,860,861,862,865,866,880,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,524,525,857,865,866,869,880,882,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,880,882,884,1250,1313,1321,1325,1328,1330,1331,1332,1344],[850,851,853,854,855,856,857,858,862,863,865,866,867,868,869,870,871,872,873,874,875,877,878,880,881,882,883,884,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,880,882,884,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,524,526,527,858,865,880,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,865,869,880,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,522,526,527,858,865,866,880,884,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[853,854,856,866,867,868,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,486,522,526,527,854,855,858,865,866,868,880,884,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,858,862,866,867,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,477,478,526,527,858,865,880,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,526,527,536,858,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,478,486,522,526,527,852,858,862,865,866,880,882,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[478,486,522,852,865,867,869,880,882,887,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,522,526,527,852,858,865,866,880,884,886,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,470,471,477,524,526,536,858,863,864,866,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[486,522,868,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,851,855,870,871,879,883,884,885,888,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,880,884,1250,1313,1321,1325,1328,1330,1331,1332,1344],[851,852,855,869,870,871,876,880,883,884,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,486,522,852,868,885,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[469,478,886,887,889,1250,1313,1321,1325,1328,1330,1331,1332,1344],[471,472,477,886,888,889,890,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2336,2342],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1474,2180,2181],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,2180],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1440],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2298],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1474,2295],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1474,2294],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1474,2293,2296],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2293,2297],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1474,2293],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2291],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1474,2317],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,2139],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1425,1426,1427],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1425],[57,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1448,1452],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1453],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1460],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1465],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1470],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1450],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1446,1447,1452,1453,1454,1455,1456,1457,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1471,1472,1473],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1449],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1448],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1448,1450],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1448,1449,1450,1451],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1458],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1474,1891],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1891,1892],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1474,1496],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1474,1483],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1474],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1515],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1474,1479],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1474,1475],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1479],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1475],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1483],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1488],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1498,1499,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,1515,1516],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1479,1500],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1497],[233,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1429],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1407],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1370,1372,1383,1407,1408],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1373],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1370,1372,1373,1374,1383],[62,63,64,65,83,1250,1313,1321,1325,1328,1330,1331,1332,1344],[62,84,1250,1313,1321,1325,1328,1330,1331,1332,1344],[62,1250,1313,1321,1325,1328,1330,1331,1332,1344],[66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,1250,1313,1321,1325,1328,1330,1331,1332,1344],[62,63,64,65,84,1250,1313,1321,1325,1328,1330,1331,1332,1344],[84,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2340],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2337,2341],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2339],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1444],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1412,1413],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1412],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2224],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1817,1865,2222],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1865],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1817],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1865],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2222,2223,2225,2226,2227],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1865,2220,2221],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2320,2321,2322,2324],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1865],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1827],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749],[411,1250,1313,1321,1325,1328,1330,1331,1332,1344,2323],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1895],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1895,1896],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2028],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2028,2029],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2273,2274,2275],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1817,1826],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1844,1845,1865],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1832],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1844],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1813],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1860],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1859],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1860,1861],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2169],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1817,1859],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2169,2170],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1826],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1817,1820,1848],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1817,1847],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1848],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1848,1851],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1849,1850,1852,1853],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1821],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1817,1820],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1819],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1832],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1810,1811,1815,1816],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1817,1820,1822,1823,1825,1826,1828,1830,1831,1832,1844],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1817,1821,1834],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1820,1821,1831],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1817,1820,1821,1823],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1817,1818,1820],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1821,1824,1827,1829,1834,1836,1837,1838,1839,1840,1841,1842],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1817,1820,1821],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1820,1821,1825],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1817,1821],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1820,1821,1832],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1817,1820,1821,1826],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1820,1821,1822],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1809,1810,1811,1812,1814,1817,1818,1820,1821,1822,1823,1824,1825,1826,1827,1828,1829,1830,1831,1832,1833,1834,1835,1836,1837,1838,1839,1840,1841,1842,1843,1844,1846,1854,1855,1856,1857,1858,1862,1863,1864],[134,229,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1809,1810,1811,1817],[86,1250,1313,1321,1325,1328,1330,1331,1332,1344],[86,87,1250,1313,1321,1325,1328,1330,1331,1332,1344],[86,87,88,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2127],[134,140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,2127,2128,2129],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,2127,2128],[140,229,277,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,291,297,353,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,297,371,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,310,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,369,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,297,369,370,371,372,373,1250,1313,1321,1325,1328,1330,1331,1332,1344],[292,293,294,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,291,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,291,315,319,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,291,298,321,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,291,298,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,291,297,298,315,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,297,315,323,324,325,326,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,298,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,328,329,330,331,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,291,298,315,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,334,335,336,337,338,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,340,341,343,344,345,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,291,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,342,1250,1313,1321,1325,1328,1330,1331,1332,1344],[299,300,301,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,291,297,315,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,297,348,349,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,351,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,291,297,298,1250,1313,1321,1325,1328,1330,1331,1332,1344],[304,305,306,307,308,311,312,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,298,303,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,297,298,303,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,310,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,267,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,354,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,275,276,357,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,277,298,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,360,361,362,363,364,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,291,297,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,291,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,297,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,375,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,291,353,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,376,377,378,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,297,380,381,382,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,296,297,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,297,309,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,277,309,1250,1313,1321,1325,1328,1330,1331,1332,1344],[277,1250,1313,1321,1325,1328,1330,1331,1332,1344],[342,386,387,388,389,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,277,286,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,285,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,275,276,277,1250,1313,1321,1325,1328,1330,1331,1332,1344],[277,278,279,284,287,288,289,290,291,292,293,294,295,298,299,300,301,302,304,305,306,307,308,310,311,312,313,314,315,316,317,318,319,320,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,376,377,379,380,381,382,383,384,385,386,387,388,389,390,456,1250,1313,1321,1325,1328,1330,1331,1332,1344],[453,454,455,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,452,453,1250,1313,1321,1325,1328,1330,1331,1332,1344],[322,452,1250,1313,1321,1325,1328,1330,1331,1332,1344],[229,277,1250,1313,1321,1325,1328,1330,1331,1332,1344],[280,281,282,283,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,276,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1669,1697,1698,1699,1700,1701,1702],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1598],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1670],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1627,1671,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1570,1662,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1673,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1696,1706],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1706,1707],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1704,1705,1708,1709,1710,1711,1715,1716,1717,1718],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1712,1713,1714],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1696,1712],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1696,1705],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1602,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1602,1696,1705],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1571,1572],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1573,1574,1575,1579,1592,1597],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1579,1580],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1580,1581,1582,1591],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1579,1696],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1579],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1579],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1583,1584,1585,1586,1587,1588,1589,1590],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1576],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1576,1577,1578],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1593,1594,1595,1596],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1570,1598,1602,1695],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1628,1669,1696],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1669],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1669,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1696,1723,1724],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1599,1600,1624,1627,1664,1665,1666,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1626,1627,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1627],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1599,1624,1625,1626,1696],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1598,1627,1628,1667,1673,1690,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1627,1668],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1598,1627,1640,1663],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1598,1640,1690],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1691],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1629],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1629,1630,1631,1632],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1640],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1633,1640,1692,1694],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1598,1640],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1641],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1645,1648,1650,1659,1660,1661],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1598,1646],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1598,1640,1642,1645],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1646,1647],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1627,1639,1640],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1649],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1651,1652,1653],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1598,1640,1645,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1640,1645],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1655],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1633,1640],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1654,1656,1658],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1657],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1645,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1598,1633,1640],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1643,1644],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1633],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1662],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1693,1695],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1598,1633,1634,1635],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1634,1635,1636,1637,1638,1639],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1636,1637,1638],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1598,1636,1637],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1684],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1601,1604,1624,1627,1695,1696],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1570,1598,1626,1669,1672,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1673,1674,1675,1676,1677,1678,1679,1680,1681,1682,1683,1685,1686,1687,1688,1689],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1673],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1673,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1673,1678],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1678],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1598,1673,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1554,1598,1599,1602,1625,1628,1690,1695,1696,1697,1699,1702,1703,1719,1720,1721,1722,1723,1725,1726,1727,1728,1729,1730,1732,1733,1741,1742,1743,1747,1748],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1696,1728],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1570],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1673,1731],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1605,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1601,1602,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1601,1603,1604],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1606,1607,1608,1609,1610,1611,1612,1613,1614,1615],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1602,1605,1696],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1617,1618,1619,1620,1621,1622],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1600,1605,1616,1623,1624],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1734],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1735,1736,1737,1738,1739,1740],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1744],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1744,1745,1746],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1759],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1758],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1759],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1758,1759],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1759,1760,1761,1762],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,2104],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2104],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,2104,2107],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2099],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,2098],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2101],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,2104],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2098,2099,2100,2101,2102],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2096,2103,2104,2105,2106,2108,2109,2110,2111,2112,2113,2114,2115,2116,2117,2118],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,2097,2103],[140,411,1250,1313,1321,1325,1328,1330,1331,1332,1344,2131],[140,229,411,1250,1313,1321,1325,1328,1330,1331,1332,1344,2131,2133,2134],[140,229,411,1250,1313,1321,1325,1328,1330,1331,1332,1344,2131],[229,411,1250,1313,1321,1325,1328,1330,1331,1332,1344,2131],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2131,2132,2133,2134,2135,2136],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,2130],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2086],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,2085],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1568],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1555,1564],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1555,1556],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1558],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1555,1556,1557,1559,1560,1561,1562,1563,1564,1565,1566,1567,1568,1569],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1555],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1564],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1563],[163,164,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,144,150,151,154,157,159,160,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[161,1250,1313,1321,1325,1328,1330,1331,1332,1344],[170,1250,1313,1321,1325,1328,1330,1331,1332,1344],[133,143,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,141,143,144,148,162,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,163,192,193,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,141,143,144,148,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[133,177,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,141,148,162,163,179,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,142,144,147,148,150,162,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,141,143,148,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,141,143,148,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,141,142,143,144,146,148,149,150,162,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,162,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[133,140,141,143,144,147,148,162,163,179,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,142,144,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,150,162,163,190,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,141,146,163,190,192,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,150,190,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,141,142,144,146,147,162,163,179,1250,1313,1321,1325,1328,1330,1331,1332,1344],[144,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,142,144,145,146,147,162,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[133,1250,1313,1321,1325,1328,1330,1331,1332,1344],[169,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,141,142,143,144,147,152,153,162,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[144,145,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,150,151,156,162,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,151,156,158,162,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,144,148,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,162,205,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,143,1250,1313,1321,1325,1328,1330,1331,1332,1344],[143,1250,1313,1321,1325,1328,1330,1331,1332,1344],[163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[162,1250,1313,1321,1325,1328,1330,1331,1332,1344],[152,161,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,141,143,144,147,162,163,1250,1313,1321,1325,1328,1330,1331,1332,1344],[215,1250,1313,1321,1325,1328,1330,1331,1332,1344],[133,229,1250,1313,1321,1325,1328,1330,1331,1332,1344],[177,1250,1313,1321,1325,1328,1330,1331,1332,1344],[136,1250,1313,1321,1325,1328,1330,1331,1332,1344],[132,133,134,135,136,137,142,143,144,145,146,147,148,149,150,151,152,153,154,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,1250,1313,1321,1325,1328,1330,1331,1332,1344],[135,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1266,1269,1272,1273,1313,1321,1325,1328,1330,1331,1332,1344,1361],[1250,1269,1313,1321,1325,1328,1330,1331,1332,1344,1349,1361],[1250,1269,1273,1313,1321,1325,1328,1330,1331,1332,1344,1361],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1349],[1250,1263,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1267,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1265,1266,1269,1313,1321,1325,1328,1330,1331,1332,1344,1361],[1250,1313,1321,1325,1328,1330,1331,1332,1334,1344,1358],[1250,1263,1313,1321,1325,1328,1330,1331,1332,1344,1369],[1250,1265,1269,1313,1321,1325,1328,1330,1331,1332,1334,1344,1361],[1250,1260,1261,1262,1264,1268,1313,1321,1324,1325,1328,1330,1331,1332,1344,1349,1361],[1250,1269,1277,1285,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1261,1267,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1269,1294,1295,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1261,1264,1269,1313,1321,1325,1328,1330,1331,1332,1344,1352,1361,1369],[1250,1269,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1265,1269,1313,1321,1325,1328,1330,1331,1332,1344,1361],[1250,1260,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1263,1264,1265,1267,1268,1269,1270,1271,1273,1274,1275,1276,1277,1278,1279,1280,1281,1282,1283,1284,1285,1286,1287,1288,1289,1290,1291,1292,1293,1295,1296,1297,1298,1299,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1269,1287,1290,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1269,1277,1278,1279,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1267,1269,1278,1280,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1268,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1261,1263,1269,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1269,1273,1278,1280,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1273,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1267,1269,1272,1313,1321,1325,1328,1330,1331,1332,1344,1361],[1250,1261,1265,1269,1277,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1269,1287,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1280,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1263,1269,1294,1313,1321,1325,1328,1330,1331,1332,1344,1352,1367,1369],[256,257,1250,1313,1321,1325,1328,1330,1331,1332,1344],[248,249,255,256,1250,1313,1321,1325,1328,1330,1331,1332,1344],[248,249,259,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,267,271,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1749,1763],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1763,1867,2030],[140,229,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1763,1903,1945,2027,2072],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1946,2027],[140,229,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1807],[140,277,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547],[140,229,267,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,270,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1867],[140,229,267,269,270,271,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,231,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1552],[140,235,1250,1313,1321,1325,1328,1330,1331,1332,1344,1543,1547],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1768],[140,229,267,271,1250,1313,1321,1325,1328,1330,1331,1332,1344,1437,1547],[54,55,140,229,269,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,2091],[140,229,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1749,1865],[140,229,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547],[140,229,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1428,1547,1552,1865,1867],[54,55,140,229,231,232,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1415,1418,1547,1903,2091,2140,2141,2144],[54,55,140,229,232,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1903],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1763],[140,229,270,271,272,1250,1313,1321,1325,1328,1330,1331,1332,1344,1867],[140,229,267,270,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1766],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1749,2140,2286],[269,270,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1749],[140,229,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1750],[140,229,267,269,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344],[272,273,274,1250,1313,1321,1325,1328,1330,1331,1332,1344,1548,1549,1550,1551,1552,1553,1751,1752,1753,1754,1755,1756,1757,1764,1765,1767,1769,1808,1866],[902,908,909,910,918,1250,1313,1321,1325,1328,1330,1331,1332,1344,1868],[140,894,899,902,908,909,910,915,916,917,1250,1313,1321,1325,1328,1330,1331,1332,1344],[894,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,522,876,894,908,911,914,918,1250,1313,1321,1325,1328,1330,1331,1332,1344],[912,913,1250,1313,1321,1325,1328,1330,1331,1332,1344],[55,230,231,894,908,918,1250,1313,1321,1325,1328,1330,1331,1332,1344],[54,55,56,230,231,245,894,895,908,915,918,919,1250,1313,1321,1325,1328,1330,1331,1332,1344],[230,231,894,908,918,1250,1313,1321,1325,1328,1330,1331,1332,1344],[894,908,915,918,1250,1313,1321,1325,1328,1330,1331,1332,1344],[54,55,230,231,245,894,895,908,918,920,1250,1313,1321,1325,1328,1330,1331,1332,1344,1420,1422,1441,1442],[894,908,918,1250,1313,1321,1325,1328,1330,1331,1332,1344],[55,894,908,918,1238,1250,1313,1321,1325,1328,1330,1331,1332,1344],[230,894,908,1241,1250,1313,1321,1325,1328,1330,1331,1332,1344,1876,1878],[920,1250,1313,1321,1325,1328,1330,1331,1332,1344,1876,1878],[1243,1250,1313,1321,1325,1328,1330,1331,1332,1344,1876,1878],[908,1250,1313,1321,1325,1328,1330,1331,1332,1344,1439,1875,1876,1878],[894,908,1250,1313,1321,1325,1328,1330,1331,1332,1344,1443,1876,1878],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1411,1876,1878],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1870,1876,1878],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1875,1876,1878],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1876],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1875],[908,1250,1313,1321,1325,1328,1330,1331,1332,1344,1875],[908,917,1250,1313,1321,1325,1328,1330,1331,1332,1344,1868,1873,1874],[876,894,911,1250,1313,1321,1325,1328,1330,1331,1332,1344,1868,1873],[231,1250,1313,1321,1325,1328,1330,1331,1332,1344],[876,1250,1313,1321,1325,1328,1330,1331,1332,1344],[903,904,905,906,907,1250,1313,1321,1325,1328,1330,1331,1332,1344],[54,1250,1313,1321,1325,1328,1330,1331,1332,1344],[56,1250,1313,1321,1325,1328,1330,1331,1332,1344],[56,894,1250,1313,1321,1325,1328,1330,1331,1332,1344],[55,90,245,895,920,1250,1313,1321,1325,1328,1330,1331,1332,1344,1442,1443,1545,2047],[140,229,269,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1893,1894,1897],[90,140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1416,1522],[452,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,452,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1890,1898,1899,1900,1901,1902],[235,468,1240,1242,1244,1250,1313,1321,1325,1328,1330,1331,1332,1344,1415,1423,1436,1438,1545,1546],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1424,1436],[55,140,1241,1250,1313,1321,1325,1328,1330,1331,1332,1344],[55,89,140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1542],[55,61,140,452,1239,1243,1250,1313,1321,1325,1328,1330,1331,1332,1344],[89,140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1441,1542,1544],[55,56,61,89,90,140,230,231,235,245,895,920,1239,1250,1313,1321,1325,1328,1330,1331,1332,1344,1438,1442,1443,1544],[56,89,140,235,1250,1313,1321,1325,1328,1330,1331,1332,1344,1889,2046],[54,58,89,90,140,231,1238,1245,1250,1313,1321,1325,1328,1330,1331,1332,1344,1416,1422],[89,1250,1313,1321,1325,1328,1330,1331,1332,1344],[56,89,232,234,1250,1313,1321,1325,1328,1330,1331,1332,1344],[89,140,229,269,270,457,458,467,1250,1313,1321,1325,1328,1330,1331,1332,1344],[89,90,140,230,1250,1313,1321,1325,1328,1330,1331,1332,1344,1424,1436,1437],[89,140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1424,1431,1432,1433,1434,1435],[55,61,89,140,452,920,1239,1250,1313,1321,1325,1328,1330,1331,1332,1344],[89,90,1250,1313,1321,1325,1328,1330,1331,1332,1344,1414],[140,231,267,271,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1807,1867,2045,2155,2161,2165,2176,2178],[89,140,269,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,1542,1544,1547,1894,2045,2049,2126,2160,2179,2239,2241,2244,2246,2247,2250,2254,2256,2257,2259,2262,2264,2327],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2045,2126,2183,2184,2186,2201,2204,2206,2212,2213,2216,2219,2233,2237,2238],[140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2045,2046,2126,2302,2326],[55,452,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,2044],[245,1250,1313,1321,1325,1328,1330,1331,1332,1344,1415,1419],[53,54,61,230,245,895,1238,1246,1247,1250,1313,1321,1325,1328,1330,1331,1332,1344,1406,1409,1410,1411,1415,1416,1417,1418,1420,1421],[53,1250,1313,1321,1325,1328,1330,1331,1332,1344],[54,55,140,229,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1422,1544,1547,1763,1867,1903,2027,2045,2066,2138,2144,2145,2240],[53,140,229,231,267,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1750,1755,1763,1867,1903,2027,2067,2073],[53,54,140,1250,1313,1321,1325,1328,1330,1331,1332,1344,1422],[140,229,231,452,457,1241,1250,1313,1321,1325,1328,1330,1331,1332,1344,1543,1544,1547,1763,1867,1903,2228,2229,2231,2232],[55,140,229,230,231,457,1241,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867],[55,140,229,272,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1865,1903,2229,2230],[140,229,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,2094,2140,2142],[55,140,229,231,457,1241,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867],[140,229,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,1903,2068,2243],[140,229,231,267,269,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,1422,1544,1547,1749,2044,2068,2145,2242],[54,140,452,1238,1250,1313,1321,1325,1328,1330,1331,1332,1344,1422,1547],[55,140,229,231,452,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,1870,2092,2154],[140,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1415,1417,1422,1442,1547,1763,1903,2045,2087,2260,2261],[140,229,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1763,1867,2087],[140,229,230,231,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1428,1763,2087],[140,229,231,269,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,1903,2045,2137,2166,2175],[55,140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1544,1547,1867,2045,2141],[54,140,229,230,231,270,452,1245,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1749,1867,2045,2091,2149],[54,140,229,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1903,2045,2172,2173],[54,140,229,231,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1422,1547,1903,2045,2149,2167,2168,2174],[54,140,229,231,267,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1417,1547,1867],[54,140,229,230,231,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1757,1867,1903,2045,2171],[54,140,229,231,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1867],[61,92,140,229,230,1250,1313,1321,1325,1328,1330,1331,1332,1344,1430,1431,1547,1867,2045,2251,2252,2255],[140,229,231,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1867],[140,229,230,1250,1313,1321,1325,1328,1330,1331,1332,1344,1430,1547,1867,2045,2069,2251,2252,2253],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1406],[54,140,229,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,2091],[140,229,232,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,2094,2143],[140,229,232,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,2140,2142],[140,229,232,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1903,2094,2143],[54,140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1543,1547,1867,2045,2245],[54,55,140,229,230,231,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1542,1867,2045,2248,2250],[54,140,229,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1422,1543,1547,1867,2045,2214,2242,2249],[54,140,229,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,231,1245,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,1903,2045,2258],[140,229,231,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1756,1867],[55,61,140,229,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,1888,1903,2045,2162,2163,2164],[140,229,231,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1867],[55,61,140,229,231,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,1418,1547,1867,2045,2091],[55,140,229,231,269,270,457,920,1238,1250,1313,1321,1325,1328,1330,1331,1332,1344,1441,1443,1542,1544,1547,1763,1867,1903,2027,2045,2050,2137,2144,2147,2148,2150,2151,2152,2153,2154],[140,229,267,270,271,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,231,232,269,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1755,1756,1945,2027,2073,2137,2149],[54,55,140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1422,1542,1547,1867,2045,2138,2146,2147],[54,55,140,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,2145],[55,89,140,232,452,1241,1250,1313,1321,1325,1328,1330,1331,1332,1344,1439,1443,1542,1547],[50,140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1428,1522,1547,1867,2045,2177,2182],[55,61,140,229,230,231,457,920,1239,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,2045,2156,2159,2214,2215],[89,140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1542,1547,1750,1867,2045,2177],[89,140,229,230,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1542,1547,1763,1867,2045],[140,229,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1867,2045],[55,140,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1763,1903,2045],[55,140,229,231,270,271,457,920,1238,1250,1313,1321,1325,1328,1330,1331,1332,1344,1442,1443,1547,1749,1867,1903,2045,2046,2149,2152,2265,2266,2267,2268,2281,2301],[140,229,231,267,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1867,1903],[55,140,229,231,267,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,2157],[55,140,229,231,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1867],[54,55,140,229,231,267,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1443,1867,2092],[140,229,230,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,1903,2272],[55,140,229,230,231,245,271,457,895,920,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1903,2278,2279],[140,229,231,269,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1763,1867,2091,2140],[54,55,56,90,140,229,230,231,267,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1423,1547,1749,1903,1945,2045,2046,2091,2092,2140,2154,2182,2282,2283,2284,2285,2286,2287],[55,140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1749,1867],[55,140,229,231,457,920,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,2045,2149],[140,229,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,2027,2094,2140,2142],[55,140,229,231,270,271,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1867,2280],[140,229,231,269,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1755,1763,1945,2027,2051,2073,2137],[229,269,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1763,2149],[55,140,229,231,267,270,271,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1749,1903,2045,2153],[55,140,229,230,231,245,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1420,1443,1519,1547,1749,1763,1903,1945,2046,2081,2149,2158,2287,2288,2289,2290,2292,2299,2300],[140,229,231,267,270,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,269,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1763,1945,2027,2073,2149],[140,229,269,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,2091,2154],[55,140,229,231,452,457,1238,1241,1250,1313,1321,1325,1328,1330,1331,1332,1344,1443,1547,1755,1867,2045],[140,229,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,2070,2071,2076],[140,229,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,2070],[140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1867,2070,2078],[140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1424,1547,1867,2070,2071,2076],[140,229,230,231,267,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1424,1437,1547,1865,1867,2070],[55,140,229,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1424,1436,1547,1945,2071,2073,2074,2075,2077,2079,2080],[140,229,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,2070,2071],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1424,2070],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2070,2071,2074,2075,2077,2078,2079,2080,2081],[229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1424,1436],[90,140,229,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1763,1867,2263],[89,140,229,231,270,467,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1767,1867,2217],[55,140,1250,1313,1321,1325,1328,1330,1331,1332,1344,2087,2088],[140,229,269,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,2315],[140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1763,1867,1903,1945,2045,2303,2304,2305,2306,2312,2314,2316,2319,2325],[55,140,229,269,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344],[140,229,231,270,457,1238,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1763,1867,2046,2149,2303,2313],[1250,1313,1321,1325,1328,1330,1331,1332,1344,2318],[140,229,269,270,457,920,1250,1313,1321,1325,1328,1330,1331,1332,1344,1749,1867,2046,2303],[131,140,229,231,269,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1763,1945,2027,2073,2137,2189,2191,2195,2307,2309,2310,2311],[140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1865,1867,2095],[140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,2095,2190],[140,229,231,235,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1865,2095,2190],[140,229,235,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867],[58,140,229,231,234,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,2027,2307,2308],[140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,2308],[140,229,269,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1749,1945,2027,2045,2046,2303],[140,229,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,2140,2142],[90,140,229,231,234,235,269,895,1250,1313,1321,1325,1328,1330,1331,1332,1344,1414,1422,1547,2087,2093,2303],[55,140,229,230,231,234,245,247,895,920,1238,1239,1243,1250,1313,1321,1325,1328,1330,1331,1332,1344,1442,1547,1903,2046,2084,2087],[231,1250,1313,1321,1325,1328,1330,1331,1332,1344,2083],[140,229,230,231,457,920,1250,1313,1321,1325,1328,1330,1331,1332,1344,1414,1417,1545,1547,1757,1867,1903,2045,2205],[89,140,229,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867],[89,140,229,231,270,467,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1767,1867,2045,2194,2197,2217,2218],[61,140,229,230,231,255,256,258,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1542,1865,1867,2182],[140,229,231,259,260,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1521,1542,1865,1867],[140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1542,1547,1865,1867,1903,2045,2202,2203],[140,229,231,232,452,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,1903,2197,2207,2208,2209,2210,2211],[140,229,231,232,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1756,1867],[140,229,231,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867],[140,229,231,232,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1755,1867],[55,140,229,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867],[140,231,452,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,1903,2332],[140,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1756,1867,2095],[140,229,234,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1756,1867,2149],[140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867],[140,229,231,234,269,452,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1414,1547,1763,1867,2027,2073,2087,2089,2187,2193,2196,2198,2199,2200],[140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,2027,2197],[140,229,230,231,245,267,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1441,1547,1867,1903,2027],[140,229,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,1903,2027,2095,2188,2189,2191,2192],[140,229,231,457,1238,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,2027,2197],[140,229,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,1903,2027,2095,2194,2195],[55,140,229,230,231,457,908,915,918,1250,1313,1321,1325,1328,1330,1331,1332,1344,1411,1547,1763,1867,1903,2045,2234,2236],[55,140,229,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1411,1547,1867],[55,140,229,230,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1428,1547,1867,1903,2182,2234,2235],[140,229,231,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867,2045],[140,229,230,231,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1433,1434,1547,1867,2045,2185],[56,140,231,235,270,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1755,1867],[229,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1867],[140,229,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1867],[55,61,140,229,230,231,270,920,1250,1313,1321,1325,1328,1330,1331,1332,1344,1542,1547,1867,1903,2045,2156,2159,2160],[55,140,229,270,452,457,1250,1313,1321,1325,1328,1330,1331,1332,1344,1418,1547,1865,2045,2158],[140,229,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1749,2094,2140,2142],[90,231,256,1250,1313,1321,1325,1328,1330,1331,1332,1344,1445,1517,1518,1520,1525,1526,1538,1539,1540,1541],[50,1250,1313,1321,1325,1328,1330,1331,1332,1344,1424,1428,1430],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1424],[231,256,257,258,895,912,1250,1313,1321,1325,1328,1330,1331,1332,1344,1523,1524,1542],[61,230,231,245,248,895,912,1250,1313,1321,1325,1328,1330,1331,1332,1344,1523,1524,1537,1542],[231,260,895,912,1250,1313,1321,1325,1328,1330,1331,1332,1344,1523,1524,1542],[55,90,230,231,245,895,920,1240,1241,1250,1313,1321,1325,1328,1330,1331,1332,1344,1435,1443,1521,1522,1523,1542],[54,231,245,894,895,908,912,918,920,1250,1313,1321,1325,1328,1330,1331,1332,1344,1406,1420,1422,1443,1542],[61,231,244,245,248,895,908,918,1250,1313,1321,1325,1328,1330,1331,1332,1344,1422,1443,1542],[55,90,894,908,912,918,920,1239,1250,1313,1321,1325,1328,1330,1331,1332,1344,1442,1443,1542,1545],[1250,1313,1321,1325,1328,1330,1331,1332,1344,1421,1422],[54,245,894,895,908,918,1250,1313,1321,1325,1328,1330,1331,1332,1344,1420,1422,1442,1542],[55,61,90,230,235,912,1240,1250,1313,1321,1325,1328,1330,1331,1332,1344,1439,1519,1542],[458,459,460,461,462,463,464,465,466,1250,1313,1321,1325,1328,1330,1331,1332,1344],[229,269,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1893],[267,1250,1313,1321,1325,1328,1330,1331,1332,1344],[245,1250,1313,1321,1325,1328,1330,1331,1332,1344],[235,271,1250,1313,1321,1325,1328,1330,1331,1332,1344],[1238,1250,1313,1321,1325,1328,1330,1331,1332,1344],[54,231,1250,1313,1321,1325,1328,1330,1331,1332,1344],[229,246,1250,1313,1321,1325,1328,1330,1331,1332,1344],[269,270,1250,1313,1321,1325,1328,1330,1331,1332,1344,1547,1749],[58,60,61,85,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,230,1250,1313,1321,1325,1328,1330,1331,1332,1344]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"175716f68ff68e4f12faef082c08321dfe2d14212bb7496af5f613d14b678972","impliedFormat":99},"1f0ae3de42dcd55aa39ca3a30b34acdfa2c7fa3503d3b1b765b2bcd7eefb8040","3ee414290dd69ddc3209e0143ff544f73401a448fa503288b5ba5925d2dd80c6",{"version":"0bb37c79ef7901db98e367e6c04f757526114811ed95d7596b6bab9ba47ce6df","affectsGlobalScope":true},{"version":"a41f8a5d20f92477529061f9c18650d9347ca85b3137a4998079bce820331d75","affectsGlobalScope":true},"6861f58158a546925bfb4da787cc8fc089408c0ae10ed546a948deb455f9438f","39d5d41996577b063c8403d8823dcc420a5fc15cdf0db929a5819c9283d6ca5b","b32cff005dc9595a4d6c68129a851680a47f203cb332d3a2f99af72f4843129a","40db12ecf6890535904fa867e8c47bb8bedf6ed5423c6dc72a4bf4763ef555f7",{"version":"1d109d94457eb7c3f72d82cb71a83a4df424dba6c9fd6a46ca1079c6568cf997","impliedFormat":1},{"version":"126714ee4ef56f902ad4c70f8d5e37a264c1479c514b00bffe10d5f227a23590","impliedFormat":1},{"version":"73a0ee6395819b063df4b148211985f2e1442945c1a057204cf4cf6281760dc3","affectsGlobalScope":true,"impliedFormat":1},{"version":"d05d8c67116dceafc62e691c47ac89f8f10cf7313cd1b2fb4fe801c2bf1bb1a7","impliedFormat":1},{"version":"3c5bb5207df7095882400323d692957e90ec17323ccff5fd5f29a1ecf3b165d0","impliedFormat":1},{"version":"e22e10700394b3260e3fa3c7034ec73023eeb10e7dfcf160133aec8f757dcc8e","impliedFormat":1},{"version":"1347f01eb7a127c59af16fc0534b865d046e2115923abd23589fc328fec7843b","impliedFormat":1},{"version":"bb4a6a55ba6a4b1615fcbfd5ac4667e1d3c15d38a0212c692fbf22ff009fe14e","impliedFormat":1},{"version":"ba9fb2ea72f27e60768b619c9aa782afe297dcd51020b0b198a5a290fe03c12b","impliedFormat":1},{"version":"4e0696e9d896584ce360d970b9a6bd0dcf020c6c917d0d5a50031c9a39788767","impliedFormat":1},{"version":"22ef8df4fb2860ba3cdd4d5bd11cf4060ec3d2154f99340c56c5e3ca4c9539b1","impliedFormat":1},{"version":"23197a030f28880e0bc15423b2cefa15771a1cf089ee125b40c7e97cce24b7d1","impliedFormat":1},{"version":"3a79cf9f4e34314aa579063f23e049a28cad44c0ad8be26f9d24a9d31c83178d","impliedFormat":1},{"version":"42988742f0d089122d7ab0f9c49320c75556a18a09123f157519b1bb0e13a903","impliedFormat":1},{"version":"e3ad49779098188f881d982170f580d658b0d2dd7c47c1b6732f317b646224ff","impliedFormat":1},{"version":"8c9f4b57bd141af987627368070836db12a88343fe7999e32ad7ddf29a807e14","impliedFormat":1},{"version":"4a9323c88c28ec64a3e9eb3e63b90c0422df07ab183c59c4bf19a02fbf96179e","impliedFormat":1},{"version":"848136ebc6512709bae1c0f0260e496ca71042a2bafddc2610955e7eb8c83252","impliedFormat":1},{"version":"5ac7fa1656cfb10b6cb88919fd930bf4e69d4f934cd7e91fe593f610dff16a8e","impliedFormat":1},{"version":"27f83fca7880e7e658590b9a998553e941d7ba87d27167dae453a2db44e2c3c4","impliedFormat":1},{"version":"cb515c2fef7956dc17b043bdd6feee4723383eccd6715c95d5a25c433878a71d","impliedFormat":1},{"version":"04c53210476e1bceb9f03ff01da4c23061125edda1e2ec0c3293994881799e82","impliedFormat":1},{"version":"f4d83e146ad3634d90e29300fb95bec39ece255aebb714e570721ae54a8cd748","impliedFormat":1},{"version":"28b55148b339a85074b0227a17d611bc0e8a45f1b2577ce03508eeb58d979c44","impliedFormat":1},{"version":"9dce7ef8e6ab92516bdd92bfb5b075f308200920b3919f31a4c82fec8e5a01be","impliedFormat":1},{"version":"543e3220d2b00f67ba9ba62fa617f3b387f15a56d7d1263aad0154f6eeb86d40","impliedFormat":1},{"version":"4715731f37b459e1e77e0c2baeb90ed1dc93f49ee301fa5e9fd4804e9f7953ca","impliedFormat":1},{"version":"6bafd1abc01d775b090b2fa062efc14bdfadd73c9ce25be33de6647fb163af0e","impliedFormat":1},{"version":"0eedf6f1be74a3098038e5121dd39c5ae151f5b8a4ec1acb592476a0764c8e3c","impliedFormat":1},{"version":"122c69be17351fb47f275c689d61a15f97b186fad09a3e2264422eff9612c089","impliedFormat":1},{"version":"a9ab6dd3d1adedbfeda993e6f202c80786236bf6dd7e0be8310e48ce5a82b117","impliedFormat":1},{"version":"15be92a9ad4c54cd9585612375693fedeff532ed0c182491fd81f2bb687abc51","impliedFormat":1},{"version":"c4ea4ff169e20644964539e51b7395fed68e7c40d996ce63c2fdfc77baeb0fb1","impliedFormat":1},"7e3ae3c3b96091cb6816dd4280f97c1f88db9f183a43f034f012c6ad74b45409",{"version":"f9800ee41019d4c7612364fd1eb3862dd535166959e139c8e54c61c421fdb799","impliedFormat":1},{"version":"f756256561f113e45196130d3be26b0fdcd76396749ba213741c2a5d0d9584f6","impliedFormat":1},{"version":"49123f65d0f1270a60f5cdb7220dea12d9fcbff320447c933511cb1f7168a11b","impliedFormat":1},{"version":"f684f2969931de8fb9a5164f8c8f51aaea4025f4eede98406a17642a605c2842","impliedFormat":1},{"version":"9fa47adb82f68bf566407e8bb04ed59a0e40cfd7eaa5a86319682839a2097cd1","impliedFormat":1},"7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","9a150ec6ece513203bb3c6771431cb8a1abcfd85c64fd41d792f142cc46cecf7","20767277db040207ba7ae96ef7884f6ff7fbeab3ebcdb15822ba257606f693ee","7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","643f69c8329e4a869990c50a5986af8179cf7b68388c74a40306033dd3d1d48a","7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","f41e4d1cf18fe34f9c94ab8509bc7fb8abc2d78919aeb7aff9f431a1933df22d","505872d65221bc816c97b4050aedc231ea6c696b3c3f78d886e4fca0384a4359","bdd83fbe2f58240a6bc9de0bcbe1e3680772580c66d1615213c476e4c1ae3745","f6d5f92b40ecd08cb769a17bed8edabcac3fbfe2ca251d86eb6e55e0a0373da6","7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","87cbee6756fc603cda0fa9e0e6cd84def30b4b8c6f1bc8c00dc12e15ce715096","88135da0542cd3fc9bb46a7844b4e708d79f11ca169c3dbc7c12fe512c08ac40","7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","9f9ab9f260ae90452195f1cc9f816ed17ed0c66e6e5f67ec285da40344c235ab","66b57ad9e6859c4c4e23607cff804ae626a2fd1a7e2e41773aae63d25829305b","f8a8ee5183b0628be8099b2e8527c7b5d95316bf07332896b1b37330ac90a388","34f66ae115826b66c86e4800890bb8c386a2cd0cacdce0e16551afc645dda97b","7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","04d0b3a38ba9cf3e01eab4442ad614cfb9c9634aab287653733fafc686945887","7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","79c149fa5d55e1728538a7ac4b9bfd805c3ac7fba6ed6841681ea90f4c2af6b4","7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","ae18ce714ae0de72609b92967d1221d362d8e70821018388e586a8978f11ccc7","7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","b992deed201d29af8b8a8a5b63a916fcdd5c8828e36090308f30b7e31f5b6114","7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","7745f82644734213ad5de157381de0182ad13870d3fe7df8f74744f481941596","ad94b453f2d39fb6f79e6ff9446e0e1a7ad435514d4071802f64c0f58b3a0046","e91ffaf7075be3d52c5020042953910c912037a06036ec9ff8697d966f105a61","efddbddbb23ca12f133c700c6d40f6bfde075212063baf19b3fb7020a0a149e0","ee3f43a99ef4e9ec5e31ed8e575eab6b55869f7cd830ce3b0942d0917fc8cb81","ee68af6334dcb5283ce083935a256a9ef4a6df3364ee732f1989f2e7605c0a72","5014dfab9bf844dd48ac6c0b8c469d7d6005cbe282d5d05edadd1bff1f2354e5",{"version":"3a909e8789a4f8b5377ef3fb8dc10d0c0a090c03f2e40aab599534727457475a","affectsGlobalScope":true,"impliedFormat":1},{"version":"fd412dd6372493eb8e3e95cae8687d35e4d34dde905a33e0ee47b74224cdd6ab","impliedFormat":1},{"version":"9d3b119c15e8eeb9a8fbeca47e0165ca7120704d90bf123b16ee5b612e2ecc9d","impliedFormat":1},{"version":"9f66eb21b8f041974625ec8f8ab3c6c36990b900b053ba962bb8b233301c8e47","impliedFormat":1},{"version":"005319c82222e57934c7b211013eb6931829e46b2a61c5d9a1c3c25f8dc3ea90","impliedFormat":1},{"version":"54ccb63049fb6d1d3635f3dc313ebfe3a8059f6b6afa8b9d670579534f6e25a6","affectsGlobalScope":true,"impliedFormat":1},{"version":"170d4db14678c68178ee8a3d5a990d5afb759ecb6ec44dbd885c50f6da6204f6","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"9dd1cf136b687969888de067d0384593097f32e9a378b187d150d9405151c6cb","impliedFormat":1},{"version":"232f660363b3b189f7be7822ed71e907195d1a85bc8d55d2b7ce3f09b2136938","impliedFormat":1},{"version":"e745388cfad9efb4e5a9a15a2c6b66d54094dd82f8d0c2551064e216f7b51526","impliedFormat":1},{"version":"d11cbcaf3a54861b1d348ba2adeeba67976ce0b33eef5ea6e4bddc023d2ac4b2","impliedFormat":1},{"version":"cf1e23408bb2e38cb90d109cf8027c829f19424ad7a611c74edf39e1f195fe22","impliedFormat":1},{"version":"8ebf448e9837fda1a368acbb575b0e28843d5b2a3fda04bce76248b64326ea49","impliedFormat":1},{"version":"91b9f6241fca7843985aa31157cfa08cc724c77d91145a4d834d27cdde099c05","impliedFormat":1},{"version":"c5dc49c81f9cb20dff16b7933b50e19ac3565430cf685bbe51bcbcdb760fc03f","impliedFormat":1},{"version":"d78d4bc8bbda13ed147b23e63ff4ac83e3dcf4f07012afadd59e8a62473b5894","impliedFormat":1},{"version":"3dfa3a6f2a62259b56fa7bcebfbacf886848dfa037298be5bed07c7a0381ee4f","impliedFormat":1},{"version":"1882680f8c88c5648d603408dd1943857ca831a815e33d3126be8368f7a69252","impliedFormat":1},{"version":"24b6109bdf5e2027f0a4366b9e1f988d648f08ea3748ebd690e76bba65115bf9","impliedFormat":1},{"version":"e7d56fa3c64c44b29fa11d840b1fe04f6d782fc2e341a1f01b987f5e59f34266","impliedFormat":1},{"version":"0f86beb951b048eb7e0a17609e934a59a8686683b2134632975baeacaf53c23d","impliedFormat":1},{"version":"e1835114d3449689778b4d41a5dde326cf82c5d13ddd902a9b71f5bf223390fb","impliedFormat":1},{"version":"16000ce3a50ff9513f802cef9ec1ce95d4b93ce251d01fd82d5c61a34e0e35bd","impliedFormat":1},{"version":"42bacb33cddecbcfe3e043ee1117ba848801749e44f947626765b3e0aec74b1c","impliedFormat":1},{"version":"4e1bfec0f44a463f25cc26528a4505bc592feef555706311a143481f69a21d6f","impliedFormat":1},{"version":"cd2156bc8e4d54d52a2817d1b6f4629a5dd3173b1d8bb0fc893ee678d6a78ecd","impliedFormat":1},{"version":"60526d9010e8ccb2a76a59821061463464c3acd5bc7a50320df6d2e4e0d6e4f7","impliedFormat":1},{"version":"35e068ea47e779f232417d5c9fd595af9a85d26b2b77f89ae6afce17343d31e7","impliedFormat":1},{"version":"623fa4efc706bb9956d0ae94b13321c6617655bf8ebdb270c9792bb398f82e44","impliedFormat":1},{"version":"70533e87167cf88facbec8ef771f9ad98021d796239c1e6f7826e0f386a725be","impliedFormat":1},{"version":"79d6871ce0da76f4c865a58daa509d5c8a10545d510b804501daa5d0626e7028","impliedFormat":1},{"version":"9054417b5760061bc5fe31f9eee5dc9bf018339b0617d3c65dd1673c8e3c0f25","impliedFormat":1},{"version":"c6b68cd2e7838e91e05ede0a686815f521024281768f338644f6c0e0ad8e63cd","impliedFormat":1},{"version":"20c7a8cb00fda35bf50333488657c20fd36b9af9acb550f8410ef3e9bef51ef0","impliedFormat":1},{"version":"c94f70562ae60797cce564c3bebbaaf1752c327d5063d6ac152aa5ca1616c267","impliedFormat":1},{"version":"2aeb5fcdfc884b16015617d263fd8d1a8513f7efe23880be4e5f0bdb3794b37c","impliedFormat":1},{"version":"b561170fbe8d4292425e1dfa52406c8d97575681f7a5e420d11d9f72f7c29e38","impliedFormat":1},{"version":"5fe94f3f6411a0f6293f16fdc8e02ee61138941847ce91d6f6800c97fac22fcd","impliedFormat":1},{"version":"7f7c0ecc3eeeef905a3678e540947f4fbbc1a9c76075419dcc5fbfc3df59cb0b","impliedFormat":1},{"version":"df3303018d45c92be73fb4a282d5a242579f96235f5e0f8981983102caf5feca","impliedFormat":1},{"version":"92c10b9a2fcc6e4e4a781c22a97a0dac735e29b9059ecb6a7fa18d5b6916983b","impliedFormat":1},{"version":"8205e62a7310ac0513747f6d84175400680cff372559bc5fbe2df707194a295d","impliedFormat":1},{"version":"084d0df6805570b6dc6c8b49c3a71d5bdfe59606901e0026c63945b68d4b080a","impliedFormat":1},{"version":"9235e7b554d1c15ea04977b69cd123c79bd10f81704479ad5145e34d0205bf07","impliedFormat":1},{"version":"0f066f9654e700a9cf79c75553c934eb14296aa80583bd2b5d07e2d582a3f4ee","impliedFormat":1},{"version":"269c5d54104033b70331343bd931c9933852a882391ed6bd98c3d8b7d6465d22","impliedFormat":1},{"version":"a56b8577aaf471d9e60582065a8193269310e8cae48c1ce4111ed03216f5f715","impliedFormat":1},{"version":"486ae83cd51b813095f6716f06cc9b2cf480ad1d6c7f8ec59674d6c858cd2407","impliedFormat":1},{"version":"039f0a1f6d67514bbfea62ffbb0822007ce35ba180853ec9034431f60f63dbe6","impliedFormat":1},{"version":"fff527e2567a24dd634a30268f1aa8a220315fed9c513d70ee872e54f67f27f3","impliedFormat":1},{"version":"5dd0ff735b3f2e642c3f16bcfb3dc4ecebb679a70e43cfb19ab5fd84d8faaeed","impliedFormat":1},{"version":"71a9a3cf1e644ec071aa3ec6417ad05aae80c7bd55ef49b011be3cf1f17b7421","impliedFormat":1},{"version":"b7d1cdc9810b334734a7d607c195daa721df6d114d99e96d595ff52db1df627b","impliedFormat":1},{"version":"79150b9d6ee93942e4e45dddf3ef823b7298b3dda0a894ac8235206cf2909587","impliedFormat":1},{"version":"77282216c61bcef9a700db98e142301d5a7d988d3076286029da63e415e98a42","impliedFormat":1},{"version":"57b242af775e000ef0e0abb946542b0094fdb761ea52affb69b59b85ad83d34f","impliedFormat":1},{"version":"75ff8ea2c0c632719c14f50849c1fc7aa2d49f42b08c54373688536b3f995ee7","impliedFormat":1},{"version":"85a915dbb768b89cb92f5e6c165d776bfebd065883c34fee4e0219c3ed321b47","impliedFormat":1},{"version":"83df2f39cb14971adea51d1c84e7d146a34e9b7f84ad118450a51bdc3138412c","impliedFormat":1},{"version":"96d6742b440a834780d550fffc57d94d0aece2e04e485bce8d817dc5fb9b05d7","impliedFormat":1},{"version":"bdb2b70c74908c92ec41d8dd8375a195cb3bb07523e4de642b2b2dfbde249ca6","impliedFormat":1},{"version":"7b329f4137a552073f504022acbf8cd90d49cc5e5529791bef508f76ff774854","impliedFormat":1},{"version":"f63bbbffcfc897d22f34cf19ae13405cd267b1783cd21ec47d8a2d02947c98c1","impliedFormat":1},{"version":"9da2649fb89af9bd08b2215621ad1cfda50f798d0acbd0d5fee2274ee940c827","impliedFormat":1},{"version":"df55b9be6ba19a6f77487e09dc7a94d7c9bf66094d35ea168dbd4bac42c46b8f","impliedFormat":1},{"version":"595125f3e088b883d104622ef10e6b7d5875ff6976bbe4d7dca090a3e2dca513","impliedFormat":1},{"version":"737fc8159cb99bf39a201c4d7097e92ad654927da76a1297ace7ffe358a2eda3","impliedFormat":1},{"version":"e0d7eed4ba363df3faadb8e617f95f9fc8adfbb00b87db7ade4a1098d6cf1e90","impliedFormat":1},{"version":"676088e53ca31e9e21e53f5a8996345d1b8a7d153737208029db964279004c3e","impliedFormat":1},{"version":"de115595321ce012c456f512a799679bfc874f0ac0a4928a8429557bb25086aa","impliedFormat":1},{"version":"896e4b676a6f55ca66d40856b63ec2ff7f4f594d6350f8ae04eaee8876da0bc5","impliedFormat":1},{"version":"0524cab11ba9048d151d93cc666d3908fda329eec6b1642e9a936093e6d79f28","impliedFormat":1},{"version":"869073d7523e75f45bd65b2072865c60002d5e0cbd3d17831e999cf011312778","impliedFormat":1},{"version":"bc7b5906a6ce6c5744a640c314e020856be6c50a693e77dc12aff2d77b12ca76","impliedFormat":1},{"version":"56503e377bc1344f155e4e3115a772cb4e59350c0b8131e3e1fb2750ac491608","impliedFormat":1},{"version":"6b579287217ee1320ee1c6cfec5f6730f3a1f91daab000f7131558ee531b2bf8","impliedFormat":1},{"version":"2586bc43511ba0f0c4d8e35dacf25ed596dde8ec50b9598ecd80194af52f992f","impliedFormat":1},{"version":"a793636667598e739a52684033037a67dc2d9db37fab727623626ef19aa5abb9","impliedFormat":1},{"version":"b15d6238a86bc0fc2368da429249b96c260debc0cec3eb7b5f838ad32587c129","impliedFormat":1},{"version":"9a9fba3a20769b0a74923e7032997451b61c1bd371c519429b29019399040d74","impliedFormat":1},{"version":"4b10e2fe52cb61035e58df3f1fdd926dd0fe9cf1a2302f92916da324332fb4e0","impliedFormat":1},{"version":"d1092ae8d6017f359f4758115f588e089848cc8fb359f7ba045b1a1cf3668a49","impliedFormat":1},{"version":"ddae9195b0da7b25a585ef43365f4dc5204a746b155fbee71e6ee1a9193fb69f","impliedFormat":1},{"version":"32dbced998ce74c5e76ce87044d0b4071857576dde36b0c6ed1d5957ce9cf5b5","impliedFormat":1},{"version":"29befd9bb08a9ed1660fd7ac0bc2ad24a56da550b75b8334ac76c2cfceda974a","impliedFormat":1},{"version":"5bc29a9918feba88816b71e32960cf11243b77b76630e9e87cad961e5e1d31d0","impliedFormat":1},{"version":"0aba767f26742d337f50e46f702a95f83ce694101fa9b8455786928a5672bb9b","impliedFormat":1},{"version":"8db57d8da0ab49e839fb2d0874cfe456553077d387f423a7730c54ef5f494318","impliedFormat":1},{"version":"ecc1b8878c8033bde0204b85e26fe1af6847805427759e5723882c848a11e134","impliedFormat":1},{"version":"cfc9c32553ad3b5be38342bc8731397438a93531118e1a226a8c79ad255b4f0c","impliedFormat":1},{"version":"16e5b5b023c2a1119c1878a51714861c56255778de0a7fe378391876a15f7433","impliedFormat":1},{"version":"52e8612d284467b4417143ca8fe54d30145fdfc3815f5b5ea9b14b677f422be5","impliedFormat":1},{"version":"a090a8a3b0ef2cceeb089acf4df95df72e7d934215896afe264ff6f734d66d15","impliedFormat":1},{"version":"151f422f08c8ca67b77c5c39d49278b4df452ef409237c8219be109ae3cdae9d","impliedFormat":1},{"version":"412a06aa68e902bc67d69f381c06f8fd52497921c5746fabddadd44f624741f5","impliedFormat":1},{"version":"c469120d20804fda2fc836f4d7007dfd5c1cef70443868858cb524fd6e54def1","impliedFormat":1},{"version":"a32cc760d7c937dde05523434e3d7036dd6ca0ba8cb69b8f4f9557ffd80028b7","impliedFormat":1},"b9856cbab0c0d47a22b4b0745e0f833e6f9db4bb3500375a47244b081f30302e","f123f71b1e61d2b32d2a5b64c179fa3491da5f334a854b0432714fd49c72914b","0c4e4eae3ea419240d2db12fa233e6f92a647d04b404c0616efe6b6658c9d614",{"version":"0f3c1c366c4fc6f6a865e54db55ec6ec3b3f15292ace3b4de6a0300051873ee4","impliedFormat":1},{"version":"1c3b5b28a07e8dfbf1f261472982fe04f2afc075cff0167a0b85b7d91f8c5bc1","impliedFormat":1},"165c5d13a14beb47fac034038b807f670917e894d3047286cf5e8a37926f2f32",{"version":"e134052a6b1ded61693b4037f615dc72f14e2881e79c1ddbff6c514c8a516b05","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"047fe6693ee2d992ddf704626c43bcff6190dae2b89d8d0bb20c708b36ce5cfe","affectsGlobalScope":true},{"version":"c326a7f65a0e49472912e4ad5dc0a84a338a4644e891580e27c58717f5e3fd3e","affectsGlobalScope":true},{"version":"047fe6693ee2d992ddf704626c43bcff6190dae2b89d8d0bb20c708b36ce5cfe","affectsGlobalScope":true},{"version":"c326a7f65a0e49472912e4ad5dc0a84a338a4644e891580e27c58717f5e3fd3e","affectsGlobalScope":true},{"version":"047fe6693ee2d992ddf704626c43bcff6190dae2b89d8d0bb20c708b36ce5cfe","affectsGlobalScope":true},{"version":"c326a7f65a0e49472912e4ad5dc0a84a338a4644e891580e27c58717f5e3fd3e","affectsGlobalScope":true},"d16752cebc5dfafc5fbeab2f3426da6908e90ffd3d47d6901ed935323eea5526","2ed198ff87ef8810b35d387baf56f1ffbd687a13e7d042fa0bef28279e870ed5","baf1fb6c144902f4241439c9528f08672943171b27fadc64ece8d4b0b9e77a29","095b084899e924f10e7c711cb20fa6a04f3e33f59efd965af15c6a90ab7fef62","f98995a6e3628be20f5403df68d4fe61c24ec9e482a4507c0a17bf74f2f08301","3c9df25b1b1ea9c69748b924458bd34928575fb9d50ab2fa9d28696b1bbc3532",{"version":"f9398e09d54a69abd3379e4164f9f0c66e214036e098ee0f9f8e4b64039efede","impliedFormat":99},{"version":"d68520185ebc62aaf75a95a669ea7ee07bfbd6ca648ceb5a68de6b787af8c740","impliedFormat":99},{"version":"12d668b38e979aaaf9b32bcbe3956bfc916f69eeefc8bbcc2b651e6d598fc8db","impliedFormat":99},{"version":"ce6145296613ed0a90962acb2cbea6d3705a6a03e51195e26f6d36ec3e501500","impliedFormat":99},{"version":"ec6112896bc0bff80582bc9dfe82e0c1c2c1c7683834deca4568df61447aa4e7","impliedFormat":99},{"version":"ed8f5e35a371cbb0a0080f7c7330c3b30462eb2a203a0e0759aed70523cde63c","impliedFormat":99},"8bd03ca8fa5b583862b97f5dca54502910b53ab868c2391cad34d7070cc54132","e1b9831989f8c3d4c97945c3b9ae5fd4217414353c948dde61ff68e747799473","f6743fbe71770c4bde824cea19637c533cd7ab972b9a058bab358cd1373dffc6","1d2abfecf0c763a3d926a9eb389e77d1b8d81dde5018299fb26f05defb5dcdc1","fe2ebc94f473be1b330d867e25778e66b017abe5727c43b9b28d625b65419ddb","54c620e48800b895a2a6c34ec24e03c56d5c0fdd1625f955ef51bbdb49726c91",{"version":"763e2a1afcbc4f503af8bd9a0e168f9e837ebb4a3ebbe521b5e7333a72534ac7","impliedFormat":99},{"version":"8eecb0fadc39ec858b6f775dab620b82ee87014423f37887b3534eaab781e71a","impliedFormat":99},{"version":"9d151f2cff555991b97edf51c8bb520a57421fd04f963e92fb780a5eab241209","impliedFormat":99},{"version":"ceec1447c0ba33a07f5a4ee0bef0abfc2668dd9aec775ba4d3f2e28e1ef5603d","affectsGlobalScope":true,"impliedFormat":99},{"version":"109cf18ade99129de29e3b730e857bcd357b8f99ebdffc1955cce396f46dff29","impliedFormat":99},{"version":"caf93a8f39d77dc8662741477b7ed311b0337e45b17566b25d08dc722d8af119","impliedFormat":99},{"version":"cac4f7c5d86426c28613732e247c2c89a4b14962a9a3799c4430b9bf91d0c774","impliedFormat":99},{"version":"baad00185559fca0e5d9916049a48f663f8856a38b7598829729030321439781","impliedFormat":99},"1244a7a3a859718c10d11976332fb8073d87e2c51131cbdad4c438eeb04dcc0c","97000343766a7e3ce0bd3577d967f92b8affe5e3f4b0e64c81c0136fd16c04a7","8c5627857e5068ff5e2cb8a0c2ce2eecf19510b368d430e285de9cef8d454f2a","dde0cbbf75291c67bcb933a7aae0fbe0161ec4e2273f8f59cf724cd45b9a004d","799b5f539059e78e18d1c76366d8a23bb1f5db8a1cf17ac3f806e422d599ca1a",{"version":"2216fc837f1e330519c670601985a257063932f2f2db54a40deda6449094be0a","impliedFormat":1},{"version":"714e51a13ddc88e4f7f736d39c616772ee59aead62bad2ed3469af44cbd227f2","impliedFormat":1},{"version":"71aa48278ab4590aabf55aec01831effd2e9be21acc6224fb2e07ab249223a30","impliedFormat":1},{"version":"3ee49e234a0b6ed82bcf2e2a76ab01aecf4bd92318ac6552c072439120d88ca0","impliedFormat":1},{"version":"23010c12d1bb431747b0cb6acf7a52ee2cf631394609cf443e5cda6b88487a61","impliedFormat":1},{"version":"83a7c3d7101550f8cec8394655258ee5ce7358422af04efccc9876749856cad7","impliedFormat":1},{"version":"c22d07075b1a35fba2437ef73252d18176ca7154e5bb83249bbfe9720a90acf2","impliedFormat":1},{"version":"0670576de71bc0fa75a57285f3d9aa385111fb91569ef14d6756d2d9ed011923","impliedFormat":1},{"version":"99e24ac5896e6db30cd5258030fa8070be0a38875962a9b3a4d6cf13e442c529","impliedFormat":1},{"version":"93d098efe9a584ac7a24ea03a059055a22c86a53be33bf256be80300dfae3d6a","impliedFormat":1},{"version":"b016ba76e42315a2c21b99bfc53249a0a0e63d4c486fc5d9033646f231b679f2","impliedFormat":1},{"version":"3824b07391e2e23c6fdc72fab211c138b079027bf8dd14ce291467653dc46d6f","impliedFormat":1},{"version":"e6cf97aa2d46800f97f333a4d08a6451c330c266d5daf1f0eadde9690374a434","impliedFormat":1},{"version":"b37c0441aad3695b403938fb1adadbf8b3590c01657522e051adea1f5d5786c1","impliedFormat":1},{"version":"16249ccbb514b016eaf7ad9b6dd9247dadc8452600aea9de06888f001c438550","impliedFormat":1},{"version":"91518c696b74751b14ec347f6d75eeb400067725bbf743c9966e97c5b668e22e","impliedFormat":1},{"version":"2cea1dcee47b5e9bb321b8bc0fb1eb237e6688bbe86e7ea685ad39f778b0323a","impliedFormat":1},{"version":"1201a56a8730b529b93f16a5a6fc3a7d463cfa32ec84f3aeb1fa3b0dc4ab8509","impliedFormat":1},{"version":"cea7c558565d023ee1b4075b7c48d872dacc5742db1dbb0cbd49266803402b91","impliedFormat":1},{"version":"11fc1fd44d26a35dc6e2dbb4e42c66106df186e1d551355fd8773d3148a58d33","impliedFormat":1},{"version":"41e5b6fee1faf463a4eabc1860e3a4298cb19788a9d5e56f2979c192b9de7928","impliedFormat":1},{"version":"bcb43bf05eebdeb592b1d52cc65965191d659f45d08274d7954fff1eff896b63","impliedFormat":1},{"version":"c4dfe7a49442612ce89f2b5d4dcff7b7cd87701d8d1264136aaa4896439f9097","impliedFormat":1},{"version":"60fad0b69e2c720cee0a8ddb5c483d0fb7ac3a96d1d67844660872d64e053a43","impliedFormat":1},{"version":"ccce146455f03a93017213866357fa507cc166949bb460ccce8e020e46f591c6","impliedFormat":1},{"version":"a22a6c263698e184ca15e94183251b8f264f9584b929f8e7dad21c847dcdcc6e","impliedFormat":1},{"version":"cd18ba72528a7d6809ba0accc2f7844af5bdc97664ba418f999f74cc41beef20","impliedFormat":1},{"version":"d15d0a35c85883917a76ff11283adefc18f1ad94ca962c0b5a28ee8f081b2c1d","impliedFormat":1},{"version":"0f445798c05cb701e9a4fb41db75eff52165670c21e3de0f42eb1c9fcaab7b49","impliedFormat":1},{"version":"94f7afa3761a2fb37add9357b6d15ad6648f756747549da96b4598ab603e1a7e","impliedFormat":1},{"version":"81286b6d3720224a3cf69b049dcaadb79020db2d1b523622e4e4566ec2d12524","impliedFormat":1},{"version":"72dbb0fcf97738ad4bef9e3a61c3a659970d5e049223e0ac14f96074cb747199","impliedFormat":1},{"version":"8bb89ec655851733bab7365aa8c8fb6de35c8e183b54af0b6c2b69b399da6d56","impliedFormat":1},{"version":"76eb7c0eff070e7f76d4477ecabad2567d7a5e2028e0728b4aecbd0ea2ff3a57","impliedFormat":1},{"version":"0dd7542698f2075a7a476fed1492c2947000a85a5aaf52000647c2eb2b0b4638","impliedFormat":1},{"version":"42e6661d72cff6e87da71bc0a9c4a485919d81e0efc06c367c70e281f530e679","impliedFormat":1},{"version":"2e8b2aa34e0d6e35b706173d57309cac1755ff664ccc11cef5c8dab9df2234a0","impliedFormat":1},{"version":"dd7f32e3c8e57aa6eee638a65874f831231e9e8f0a20f218d56098e123843914","impliedFormat":1},{"version":"fa45d2af5e197015fe2feb687ce7fee7102a26e585d1948bdd490a03001a7fda","impliedFormat":1},{"version":"2cea874ddf4181de100e5115118d1eb90846cc853c2f401c16f2887664ead1cb","impliedFormat":1},{"version":"4f6110f3159184b16e2f592deb75900e2ddc9048c1df23fdf726016bfd461259","impliedFormat":1},{"version":"20b2e68ddf8aad34f3b12e9adbdd017a234b39d16ab9da6f45130197768dd25a","impliedFormat":1},{"version":"0d00fd222197a2ad475cfe9a90d0b15bed9c0e78afd379ce44bc0ade63341cfe","impliedFormat":1},{"version":"08796a0e869aa024ec57e9d9e351417b82cccb917b077ce787574f25c4eb6906","impliedFormat":1},{"version":"f8a23c9b2d215c1a8554bc599ce533d921e4b1e012097aa4faad1ded43888c54","impliedFormat":1},{"version":"4f530177e8c82c1e88d96c7108d69fdd88684cab31669e10ee069c418513e253","impliedFormat":1},{"version":"35cb818ce1150c1fdf11b1386b00c9d83eb293b9024a81ecce5295c033cf06e5","impliedFormat":1},{"version":"f2d765b91a9f11023ae755617d980bf07da288fabb79560abc590451f61555c7","impliedFormat":1},{"version":"c18aa62db5222c7a9bdd724f8b71d89392f734af501182acac7993cca709155b","impliedFormat":1},{"version":"999e252b3de60259ff35f8e010450048f7dafea7b0546c64872364616e19d885","impliedFormat":1},{"version":"c732665ef491e2b4f1895a2773547cbb6232cf9a79748f51542260d918f0ea03","impliedFormat":1},{"version":"f9b7fd9e9291f9a91f6a752e937edf966b4b6ad2a5e076c6c60365ace72719e2","impliedFormat":1},{"version":"142e73b304f05586baccc7697e9b26170cfe5f2b99c3436b3a42c774071e3020","impliedFormat":1},{"version":"301be8a28f715dfb73a4fb302100a04a6794369dcef5b6ae6bdd41a82c3fbe07","impliedFormat":1},{"version":"5e4d38a79adf9f1a76ce2de73051a54168f2d53da1f4f6fa4913a1b4beafa99c","impliedFormat":1},{"version":"acef2fd0811735af6f3f1a9aff2833705cf7348c71b15be0e78b08456561b029","impliedFormat":1},{"version":"c4d6c18d69516745f3fce90c53d5ee992b57ccb8aa5384abeac688f8886eeb8d","impliedFormat":1},{"version":"753c39e7d783554af8bd701aa13600812b30d193f16a8209c3fb0d5128cdf08b","impliedFormat":1},{"version":"3de0755db7f40bcc27b39a87670acb0dddd5f4a045df351ab471cf925865c670","impliedFormat":1},{"version":"a92bdf3f88ac07a9fca8d0271bd4a0992988b7d4dce0caa66814926498cf8dfe","impliedFormat":1},{"version":"611c973b6206226643dbb3657de0ba31be68945aa7233fdf61a60da7a3fac180","impliedFormat":1},{"version":"2d4ad339e8394a28831c13f120ff95b75838f5975f797deb276495935d99dfcd","impliedFormat":1},{"version":"c2c30baa0b86b4a34cd51d1d0ad6f060fddfbab2a10ae1a569b5c5ced1966852","impliedFormat":1},{"version":"663e1ee9f6acb37dfbc4ad2d1d3b561f5837314aecb0a0ed0e76f96b82e43b45","impliedFormat":1},{"version":"c483d8c3ee7c81101afc9eed3a2c6c2dbe91e652f50423984793ede0b9869aa5","impliedFormat":1},{"version":"bc5545aed9f4590a14d152b8cfed6ee14a249e5a8c19c69b910bf1172d415bc9","impliedFormat":1},{"version":"8a05593a076b350b9dcc1e083a62fb7f467bc193969a2928d2e15d9901afb738","impliedFormat":1},{"version":"0f49a593986384ed9bad4e9584787907ff3aa38f8d01b99a05c4cb2d438135cd","impliedFormat":1},{"version":"c6f3d819a61cf95a168e80cc3c6fd19107cda7eef77920c14bc7a1d0c12090f4","impliedFormat":1},{"version":"1d11f16dd61b0847fd7c7650e56a79e09cb74a3a485814edcedfd5ad15aa5809","impliedFormat":1},{"version":"50d357a9ddbe5602aa6f573b429c6a0bfa88cc1e35a86384d70b670a4239ea88","impliedFormat":1},{"version":"9793c9a97aaf86f8ffb7d28ca0adf087d5761ac9d5e05c6a87d4db31e2e8e45d","impliedFormat":1},{"version":"15abd4e0d04c0449fd03e0a1b7d2013674863d8d34ad2ea5487ad494e7cf3fdb","impliedFormat":1},{"version":"8527e9475556df79857aef2b4d07e8ca8f68be9fab9516ccb382d2076ba2c6cd","impliedFormat":1},{"version":"641a1f222df51a003a1901c3ca9fa01eb3120a1fb05a49ac461a2d0557c39bec","impliedFormat":1},{"version":"c26173a15b7d53ba35ec965b77dd0a6dae6def0c724722b490d1768121a62e30","impliedFormat":1},{"version":"cfde8945338cf4b458434cb4a6ce12c25470fa9591c500bdb4ed545fc0f365a4","impliedFormat":1},{"version":"259a80acb8da2d28c1f3aaeddfb4e3e5a7481d5427b0a9dfcba7734ca5c2d175","impliedFormat":1},{"version":"a9ca60caec20eccd363cc526e7c3bbfd2ae2610fd7ee370cd26fb4f1c4682377","impliedFormat":1},{"version":"a4d5141d3cb789c9b2b621b319f2d854916c2db624a55cf7c54fba0ec4fe9812","impliedFormat":1},{"version":"beff4db31faffe2f6cb06b2e90b3b19886113c3da1489b551a81270f613a866b","impliedFormat":1},{"version":"5e82f98b10480dfaf5d78a5b404a5e5ecb31174afb8bd9d8dcd8a66a59574def","impliedFormat":1},{"version":"f85a40df37ac693401045c50d740d9138b4b550831f361279aafcff73e2b960c","impliedFormat":1},{"version":"f7b8073d3f40e076117cbeec12a10df50536d655e6714bf866fc45b4cc41c2bb","impliedFormat":1},{"version":"1bb39fd7f47a2f211e4c598c2bf38ee90e4de28d5a4ec49b49305e5e7bd2e411","impliedFormat":1},{"version":"e8d94f3e990b81d5d106539887033005d70bcf345c214773adc1f4e4e3053e53","impliedFormat":1},{"version":"c038c21d5a87990734a38298ffa82a0fed640425c5fee40ea3960822deff4975","impliedFormat":1},{"version":"154d74a74c16d21ee0e648cb5642676db8f661d1d9ada4144bd9e3e7dc6b8a7a","impliedFormat":1},{"version":"ee09088d781a1176c7bf214840737fa21e07de3ca9eb6ea57f777f2c089abbb4","impliedFormat":1},{"version":"41f41572a65407edcf64851f5444613daccd166caff3363b6e3745bbdce2aea9","impliedFormat":1},{"version":"9c468cd1f0c9f07a46910fbe1f246aec7ec29f92571ab7c3b272d8c1c0e0dd89","impliedFormat":1},{"version":"d1555af5c910c0689e80bec673474b7d191638bed9becbf0516cb720d4e16070","impliedFormat":1},{"version":"4960feed79d63fa10a53c9829c9a062f6ea45a1624d2ceacbc7bff2f19a38e1c","impliedFormat":1},{"version":"3ddd7a968e01b2fec8307e1666932e3844306dc5ada11ff0b6640f86c156723d","impliedFormat":1},{"version":"5a7e88aee4a1785f90a989e88484281f9efd4d92a16ec71cba98b18e447b6ed7","impliedFormat":1},{"version":"bc787dc8e3415e0048159c79cccd47dea791c4191747d2b583b5e353d2a36bca","impliedFormat":1},{"version":"7457b43a725de631b1fa2efabddec1f94b1c07fe50e0bdff5eba95b89a0b4b1f","impliedFormat":1},{"version":"2a338b70aa0f5d2accf3a16f901d24b14d55c5a98ff62cc7a7a3db9269e20618","impliedFormat":1},{"version":"1321277097d216075d43f5b30d3932fbc5b6ceb206219c22a805d63fd5579ea5","impliedFormat":1},{"version":"2ad48ee7f317389b51b9013b316a670cf89f1d8ea6323e7e6df3e835a13d14e6","impliedFormat":1},{"version":"35a83e323a15a4dd7b0ea121165cebdbc140cdfe9a7ffbd35d6ef2330a2eb99d","impliedFormat":1},{"version":"2fac639c35a95a92ec5470da8c9b8489eb014b8e42e1f34b0b7dcfd9b7415a04","impliedFormat":1},{"version":"2cef458ec965005564e91a3cf4de382e8b2fb99f3a4376f55499397f964b4634","impliedFormat":1},{"version":"63d51a7ccf0aada276066307efed7e2c758313988387c2c5a9c85f805c7de2b8","impliedFormat":1},{"version":"a87c2419d8b652afcf0494984a0472c5a26924b782e6afba31a1e46fd68dbbdb","impliedFormat":1},{"version":"1ff440ab1b185a4e969c0ecdac43ca1d13c9baab28742b13eb82ca53387709b1","impliedFormat":1},{"version":"3c572b03f245650f224cc7455ca179f9ec9effcca5c32f379f1f2b71ed875b2c","impliedFormat":1},{"version":"98994195b0507fbe1615d7d01e34c84ca78236926f188e499afd59dc46e27bb3","impliedFormat":1},{"version":"a58f6ca45d2106cde60f14cec027fa7ce9f239f7c9b81b8ca3b4840b62b1afaf","impliedFormat":1},{"version":"7ec2643fd6d384a8c82eccef1a7d61aa95842315698e1a5a72cd90f55b0f0f3e","impliedFormat":1},{"version":"7159d4e2bbfe8ced0a59cfd7ddfba352858c2ab5c4abd6e45877f532c70b8120","impliedFormat":1},{"version":"4114d00e7e8dc9baf4456abbe82453a0a45a8ccf39a20650c93000380d782e81","impliedFormat":1},{"version":"dd56595a5688fd6f693cbdf3b36fa9df84c4edd26ec4e4cf0007cc03a28039f1","impliedFormat":1},{"version":"cad43a67a688bcbe04fee6918cf956ef0aaeb9aab0e4acddcc4095d14a3a22f6","impliedFormat":1},{"version":"0d251f92da281368a2c23feda6f2bf7971c9c99cd40391c68fa68d72d3966b89","impliedFormat":1},{"version":"990a517ac771699d7ad051f1227a9f789c3f89d637261dd6fa7c98207d52271f","impliedFormat":1},{"version":"483234a7292888eedbc9bdac1bda9bed97d8189f2f370738ba2e19a8bab3e825","impliedFormat":99},{"version":"5c93d5b8997969ae0513521e9f43b8cacce59b23f26ac21258a9e4f836759244","impliedFormat":99},{"version":"128f8ec386a21ec637c803a074e14fab2f8f66284cc0fc67493610d5014009fc","impliedFormat":99},{"version":"29261880f5f5622868336efec551c0ca516b498b860eea7b80594bf786543af8","impliedFormat":99},{"version":"00f158bb38e70285992f45dfe83bc9b7c9160f84e20e269a37973fa54fb323cc","impliedFormat":99},{"version":"325a8188d1e55526eb6d97c791c8c3139749f5a6dcfdfaa41d2241d415833c3f","impliedFormat":99},{"version":"511670344a7a6e8c7210084ec6e411da561910cbcaabfd6a6c274175a6e9eeb7","impliedFormat":99},{"version":"f6122c3a23e2f4142b8b8d71facc2107d605cd061abdfe6d3e8d8482cff85ac5","affectsGlobalScope":true,"impliedFormat":99},{"version":"974a1a0c2df35182093ef9025b736c1064d49acd9e709a8ce24fc38d0060476b","impliedFormat":99},{"version":"bd62306e942473ab29877e62e04f45bfde19e44820d7464cefc4b1a46253a87e","impliedFormat":99},{"version":"49341848b21d6c1607226639489252e2ed99971a549ee803ad035954c51609af","impliedFormat":99},{"version":"8bed537f8911c582d45890cbe34cdb8da3789f74419a260ea1ef1b127630ef3e","impliedFormat":99},{"version":"7152ed52db99b6b5f51e2ea849befec78b1ad6fe7335a26ce095d04cf49939d3","impliedFormat":99},{"version":"e6facf92181fde42252841659156af639e5e762b526ec349fbc995caa416cab7","impliedFormat":99},{"version":"ce710d222c3199ef27088102d7d6a0625afeae75299593c87aa6e5aeb96e46d2","impliedFormat":99},{"version":"25aeae768f3412d0f5cb0174cc4d752153ca6ff8049afc6ae34472f891b4d969","impliedFormat":99},{"version":"2eb7f9042af4bfd96a6b26648371cb71610f91918a3afdab1f18d368fc382539","impliedFormat":99},{"version":"19b40effb3383bdcb30c0da1c8df23971eca7c8bfa387ed87fe86cf5eb5b8c0c","impliedFormat":99},{"version":"1052269f3f798153c82b81f848034a26d9ebaf3568e6348e2e08db54574cf44c","impliedFormat":99},{"version":"df2e9a23e3d645a98d26ba81f5523ff70dc2f3121a0591d51977be8e14bc08c9","impliedFormat":99},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"d1bc70bb451cb237221bd55225b69eb38c3d4acc124f72ce252d6ae7dd07a63a","impliedFormat":99},{"version":"dd31f6e0bd58dcedf5983c32bb4393802ade212130cb7ac0d2183ca329ae8509","impliedFormat":99},{"version":"237ba8d8e50d5dd3da1605567fce72e85900be577574f90f655530359271fbb8","impliedFormat":99},{"version":"0f98b8056b3da59651f4901ce6a5995ddff24eb736a7d7729c56a4daf330ccee","impliedFormat":99},{"version":"b02fcb0d17501cd60b28e38310efde45f52cf54f24fde5a1b5b69b8f9d94c626","impliedFormat":99},{"version":"a7c9e440caa847e5ef7ec70c1f22894d28812140d35ba9c581a0fde42703cf1b","impliedFormat":99},{"version":"5c146e5ddd9cb5560bbfb7a2eeca8fb95cb0095735729158c374f6665e546253","impliedFormat":99},{"version":"8318b0134ef3b80e1db02a8a8a4b3e51532d6ddd19ce82c5cfddcecf26b484ac","impliedFormat":99},{"version":"5a43c4538b08001d3a6ece9adb1f9495850a1bd7dc2eb65d83fd7c0e7a392650","impliedFormat":99},{"version":"18dbcddb8d9818b28cc04b43669328ed37a25072aaaef2c2f39236418786c914","impliedFormat":99},{"version":"b7403457ce3abcab1164089ab08dc51e7f25f107f782de39ce5ee581067f458c","impliedFormat":99},{"version":"61c3289a793b12125eb045405284a08e5a30944da6004ff31451fc97d255ab6a","impliedFormat":99},{"version":"aa4235a89f98c4a22736fcfb34eac83a04dc3ca3d4f16dcb08e0e724b4ed0fe6","impliedFormat":99},{"version":"9b191f34f84f51f675780d460e3278e5052d01ff0f54760b86b1ded7c7481502","impliedFormat":99},{"version":"684e66700cc36abdb023edbfce8b173bfdfbb24a83aeb323a4ff9a824c3d117c","impliedFormat":99},{"version":"00eef909fe5b147a8300b82fa23509ab703518a22ad4d7534c71db52b32e30c3","impliedFormat":99},{"version":"9966e926440d31cd9e4be247d521c0d8943cec0f1578b5fc8f2cade12b0dcfdb","impliedFormat":99},{"version":"a7af63d694ba06d02a3ab430dfad79babe64b5058e8b23feaef5f45a40d1cda3","impliedFormat":99},{"version":"4f191fb15eeff92fd00302646267f3018231a75bc1477443a694556b78cef709","impliedFormat":99},{"version":"ea6cc98a17fce8fd6511c20a7b56cf7e0a4e53bd631c3f0353ccd9b307ca64a1","impliedFormat":99},{"version":"834f06bfe2fcb6b8a3392db8b5945eea43da11c10fd48d03cf088b0ffdecc17b","impliedFormat":99},{"version":"752d49b6a6980173482ed1b402591f03976d2bd7c497b5c1dcb901f99dcf9836","impliedFormat":99},{"version":"e126d48aaf1f00de47d3f0241866cfc4558f44a22d77928ca95a88a3a24c2d19","impliedFormat":99},{"version":"4eb7db012d4e80cbec2ca05bc0495c6e3163ed03bb284f1b77dfe0851339e398","affectsGlobalScope":true,"impliedFormat":99},{"version":"7992b7f685cfba4ee50eda0b49127ab5c864a42d3c5dd7f59e58086903da0890","affectsGlobalScope":true,"impliedFormat":99},{"version":"9e74ddc0bd65f7c3ef84244a65fa1d20cd174a590a001121528bb3c976ad41a8","impliedFormat":99},{"version":"58624ad4f9c49dc3694ff5afc6697acdccf64bd9fdf49a675806afc543c364b5","impliedFormat":99},{"version":"4914aa275263fe6e732e6baa5f467d0cf6c2854fc29929807799dee8f58f5056","impliedFormat":99},{"version":"3ace5e18f4dd057733b091c6f49c938c8700701c09b65d621d037e5cb018d1a1","impliedFormat":99},{"version":"b612024cc0ca894a513a9a4601e731053423bcb67d1949c2fedbc46a7a0fea3b","impliedFormat":99},{"version":"c23be055826e6979af0c1a0b5dc5310fbcc8b85eb901ed3068924494e1cc98fd","impliedFormat":99},{"version":"d0247a162c693eab26084b4a1516ac96860caff46e8e657d9c27a540b66d9776","impliedFormat":99},{"version":"d144cd746f2a7f173c48d34d3e9f170ea5b4cd01dcb1fa108f663ef41efbbc50","impliedFormat":99},{"version":"16eecaca313db27d0412dcd15228858c5cede120c668f7850e057210cff4f0dd","impliedFormat":99},{"version":"5423d29e89a94ade9fc6b32a4b0d806fad5f398ce66b0c6cac4f0ae5c57b1c31","impliedFormat":99},{"version":"788317720a047b846f7fecc7102c0a1e48be111ca016f84f40e3b3eeb16c5dbb","impliedFormat":99},{"version":"d45e477884bb6604f81219240a7f70d833559e67f0a1ea9bd1f9e1c2f7185b21","impliedFormat":99},{"version":"a9aed4219003a14817b07d23769dafb9b1ffc7e84879ff7e28e1fd693cb78065","impliedFormat":99},{"version":"cfad32ba41ffa009cb1f5b6baad43a6d302f589f1f5e8a88d0a9900e5b01d515","impliedFormat":99},{"version":"ecf5b45b694782b67fc8ab4c17ab48a7daf3a9c55a1345de6266317ee0072cf1","impliedFormat":99},{"version":"364e927e1c8478de2e8876ec9c7a10d1632f95d802785c8da0c2b0e9232ceb11","impliedFormat":99},{"version":"f9a4468dacf205ffe66400b7116ced2f5d2c3e44f9758857d0cd8d39dfb3875f","impliedFormat":1},{"version":"c8630f6513ae04f49421f8ab32b38f7abdac88cba2ac0c9e1ff8843b5b22a340","impliedFormat":1},{"version":"ff13cacf3d52e66274714b5294beb32be2b9a0d2a6e7b04d3edaeb60d185a6d4","impliedFormat":1},{"version":"244b18983d0ee2536aee059d93cf87bb4bdbd883b2fafd8d651e16fab9bb9029","impliedFormat":1},{"version":"9c5890d05237497de3cde7137bbadee48af3d52eea3f3d981a58fea35dc2f00b","impliedFormat":1},"ba9ae2cdcac467847a8cca3cc2f644013cb922e1890b29896b4507e07cf65790","355eaada16a8f31c904098e54ebca6e51b0e01fc716e523fc8ab9e8ef11446a3","790a4f0084f994dc520aa88f1864c02cce6eb96c94fca61a5da1de9c8914bb06","f14d6c825e11a7e8aa46bdb7586d5ef1acb7d803a848e5596cfae158cde75a81","3df442e697c08a2d25898b267b931bc6266ac83bda991f7b6fcafa8c79f51bfe","d41054bd8366a161a7e857585e5cdd2b98e9bfbc07f7b290663477329fba1380","b24c1a9595b200beab809f57f84751f8457555ff4ea0d47cbc7a3279d773359d","f6da090a5466c2b62e51af3d35b62046c17efb1b9099d76adc0444565e87f82a","66724a45541fac6d036b63e86c2cec657b68525601cb3fb708d096a5df003890","5f928bfe8a0dc1a615c538ab95966dea250b32554b9059036e41aec4f4ecffd5","288ce8793fc6fdee1733614d15915d600536cafbe7548100270e5f30b57484f9",{"version":"ef3b827d142e164637e58587b777c83b43837a1f87374b69c9b59b68ba5bd359","impliedFormat":99},{"version":"6548427991be59f1a9e9b87925de47aceb8a13707bf308cb5e1835587a0ce205","impliedFormat":99},{"version":"056548669a8124af53df97ef147211bdb37ac803c72b7a787c24976520b88c31","impliedFormat":99},{"version":"53bb3a811e31388b4674c3c7774ee8154a36c486a8357a2955a165bfb12edbe9","impliedFormat":99},{"version":"f7ca3eb47bdcce6a795056ea5d234f42fe3b647e8b6622c7df97aec57c446a7d","impliedFormat":99},{"version":"fd977e663b6949407059ab43c22115ebf8f82076ff387398a5fd51638bfb4c71","impliedFormat":99},{"version":"fd9433f7c32d288a490c3e595daa08cb1047946c1210e1cf8104cb1266644b39","impliedFormat":99},{"version":"387ad53f5d09319376177a0ab2a91ce20478a387ebb0fb0020bddbecfb224766","impliedFormat":99},{"version":"db9ca69778676077b4409e1d17788f032f302c2d05560d2ef19dcda46236e5d3","impliedFormat":99},{"version":"cc82b09f9c88c662f424a5b0b9ec731a21a31fc14389275631f009f7ca715cb2","impliedFormat":99},{"version":"01ce49843885fd80c3f624622a0d4522679aaf50a26e33d35d52f1b52e9d00ba","impliedFormat":99},{"version":"18f496634b5ed007c5d02957412a349336dde2aff4b9cd06f95842f1cd55ac8b","impliedFormat":99},{"version":"b804072099a27907ae5e3c2ae8c86c6898943af825ffcfe6f1d132238577db2f","impliedFormat":99},{"version":"5fc9692150dd4698e0c8758ff18c668191525d1b7eaee1f975ec8c40cddd90fc","impliedFormat":99},{"version":"6efc201f692fb97a3edb0897a8175fd7142969984522ca55d1960be9aa7fef43","impliedFormat":99},{"version":"c0ac8d1ffc41efae3f8a2c8d498fa75e57a7c8a4ada4bae94ce521ad1dfe7a9d","impliedFormat":99},{"version":"0000a399b847466d8d126c77b05a984be5f97b00c8e837ebda2491dade2bbbf5","impliedFormat":99},{"version":"06bb766cda339a4a32002f02207d078cf5683e1705feade0c157f422318fb76e","impliedFormat":99},{"version":"5c3e8d6f1fc3c31be2546f62b8d52ad4474ddb4692d3ec9e1d266d6d7a121a60","impliedFormat":99},{"version":"42f1433421deba9dd2af18eaa606b6b343cff4938c850e59af6eea1e62735329","impliedFormat":99},{"version":"23b9904358c798c3dad75857247669bc8f9fc6c4250b592b1dea8aa22afddba9","impliedFormat":99},{"version":"3b9d3dde2ee986075859f645e82012f6eb56c49f2c138a7c184c13605d351c85","impliedFormat":99},{"version":"f8788945384c5551f1cbe4267a912e8f1566b82d5a4172f16c66151f3a368e70","impliedFormat":99},{"version":"54f168f9716c2d3eb5fc6c1150bd4490e0357308ab6cb5eeeb406829bd9dab56","impliedFormat":99},{"version":"f166d49ae1d6bdb4191a669f37b7fd7be7a8e67a14ee2edb6b78e14dda54ba89","impliedFormat":99},{"version":"aa21d322fbba97f2a23ceb482c2784ae9867ef5d695c983119446bb5fbbebbab","impliedFormat":99},{"version":"cc6aac4c8ab9c1989f9d08e90165821956b4059256a54fbc111de090ee78a69d","impliedFormat":99},{"version":"887fe4afbeee3011ae6427655203a884bb8470a539769aa719ef5fcdd0a59bc1","impliedFormat":99},{"version":"d159365182c6ad6a8633ad137f5a41b0529e834ea17e72bfcb3db4702438c09c","impliedFormat":99},{"version":"52fb2ea14b3b711313e0968fe10e20e8fed4a2ce849bdf57d8899a1bf3625a2b","impliedFormat":99},{"version":"8098bf2ea98b504b323c904204c0a3b0981c98be7221358712be038515e3b47e","impliedFormat":99},{"version":"395e9a64b2079e59a90e2551ce15ebb2f8e4605f13f3d5b1c42bbe3a1b39e600","impliedFormat":99},{"version":"fb9b3a4fdd090e6979a15ac60176fd84bfeef813c4bc0f1f2180cf78fe9af23f","impliedFormat":99},{"version":"8ab638477d6568cdafd2769650452846293a53ddfaca36f731b1f1288b81415c","impliedFormat":99},{"version":"61155cbcd621c95b58f2f42ad4e27348c24aa985c9553b3d53b09abfc2e98016","impliedFormat":99},{"version":"f9f19ffa86da9fa5a3a335226a922996e1c3b33d40e62a8492fc5db0071b7f9a","impliedFormat":99},{"version":"132d3f7714d39a8882f0a7e7de4b12f8dc1a22fd5bbef346c57f00e0eacb2bc0","impliedFormat":99},{"version":"cf20bc9c656687f99a5eeb223c04192b49384176c9ffb0f601264214d5af1261","impliedFormat":99},{"version":"7c20d03fda4c8b4516764e18355ebc6f892675982ffa93b2ebc96d943d0c2143","impliedFormat":99},{"version":"418d2d8e3e1486159113bdb4e51d77b2a81fb1ba23bdebf4313ada18b8badc31","impliedFormat":99},{"version":"ac9c0dd24709ea3b7b5d7aee91f1ff0d9721c80a8557fe0e7d1d6cc754d8f299","impliedFormat":99},{"version":"e4a9f19b00c467ac6996311012d238cbe354e55a642d6b9a2f19dd98e332fc47","impliedFormat":99},{"version":"097d47a237ebc2c24c8d79c77fa92d25596dca318cc5f999dc0ae94c2f0884d9","impliedFormat":99},{"version":"55cc568682a777f004c1b89e97baf26a1aed2b4c7b4293d6b2a6d8b537b64e82","impliedFormat":99},{"version":"5b1d8c3f44f755ce5d493b8be2eb8b498cbf2aa4308dd14231bf836250be4b96","impliedFormat":99},{"version":"9e6f2bd33104529e94f84d654d899ba5bb316eeec141c7e843ee5f8e7043d6f5","impliedFormat":99},{"version":"5c87dfb6c4731684e5a914443fe6a6690a18178e5c2ce4efc0809b9fc4b8f168","impliedFormat":99},{"version":"575c197db3dcc1dffeec862851b1676cad360e2ca318bb939f726c2f84d2c590","impliedFormat":99},{"version":"6d2f714fb4d4c80e2b0ee8089f152945af09353a1a0fdaafc993ca49a65a654e","impliedFormat":99},{"version":"f339ef7ff21bea5893bbc216bccaf612e5790a140685265b88d8b38ecaf8eecf","impliedFormat":99},{"version":"979e30a26ab50ffab989aaf27dc47b4c8ef63b3b111402ba6ed8233dffb14c6c","impliedFormat":99},{"version":"06d4dcd0b50b79ef039868289669c22bb19d877314e04dd025d32503c97e658d","impliedFormat":99},{"version":"4ab020241e5facd85e2187ac1226223ce878edbb0b87a3c1a56fb655ae0be038","impliedFormat":99},{"version":"bd2145a8cea7da96f9dc18c5cd25b44e729ed458cd388d46e8e77cb824214bed","impliedFormat":99},{"version":"888121ff81a9c9cbe1ac88ad8cf00b60ffdccfd78e96d30506bac4a80515a669","impliedFormat":99},{"version":"fb1302608d2706a2d64653f44b3f81c1e465884a80e605406056e32f3290eb3e","impliedFormat":99},{"version":"65b95a5423b865b3259f7353f30eb34bc1db4191d38a523148fc90ac9af5dc96","impliedFormat":99},{"version":"d920270b658e7bec8272e127038a18937ac1970d30eea3902595d13db0ae7f6f","impliedFormat":99},{"version":"0451a0d96f1781002bba0dbe395fab19f365375a84cd92f10e7bcd9e972b24aa","impliedFormat":99},{"version":"c5b7baced3c287f99036072fff0a24fc3fe2555d2b74890180d0f31e25cdc317","impliedFormat":99},{"version":"19a52bd68490f25976d3c3d338f765984123a258e6eb247e852fab541c9a6a07","impliedFormat":99},{"version":"3c7b139ab82ee2ab63341e773ea9bf8e90aee6edcf6866d4de78f0de1774b40f","impliedFormat":99},{"version":"c38418d70fbf9fb2f4731656371ce648fb6084ecc30e9fbfdf45eb768d1ea453","impliedFormat":99},{"version":"c8a465f5f7e21f08e7becf892f29e4308d9086cb803f9f7b759d5f26454aadb6","impliedFormat":99},{"version":"cf26fe55c4cd2ba2a3fa6350db1662298b563f4ea2d673f4c70baa711cb5420b","impliedFormat":99},{"version":"b16a00a34ce0a110770268a943d352b36a555d0eb264269b51697fe5cb11300f","impliedFormat":99},{"version":"3510a479b5809af49608a04e835bf7dffd3ba6e076a97afd01203b2e7877e4c4","impliedFormat":99},{"version":"878b44eebf6575925f8e9873eb3bda9789399a62d553ecd5e8f284ec5025af96","impliedFormat":99},{"version":"0348c1c592e0590b999a2a906664366e19e70586a11d61fa7a1758b8c3cdffb9","impliedFormat":99},{"version":"9f0c673cb9df905ac2902475cdf3e2f54f020a76af9612b5c0222a3eab70a424","impliedFormat":99},{"version":"a8f28a20c992bffc995234288b054cdebe4956e59b8d3b22e28d601ddfbf9e91","impliedFormat":99},{"version":"64efb6ea94c4b984a014bbf42afd359e3ac2e8f8e560ecdc8fe76ec8a4c96db0","impliedFormat":99},{"version":"f64e0e5cb0425fcc4a8ab39bd741b4d2fa470912114161a3c0bef4be7a946606","impliedFormat":99},{"version":"9c8d051e938048af069f9e3622443d68448841bbe90a338f0df7c1ea09d4b090","impliedFormat":99},{"version":"6937a06ce0ca216fddc6bd7b1d28ef1011d248f5162e453000f6c49ec72e2809","impliedFormat":99},{"version":"e68ce166e657444fd7b72abc38b27ec911c1bf37b04736e190b7059f0b40c5d5","impliedFormat":99},{"version":"8fbb0fc54842389550f241d79a344de70917ad7bccc057311bcd66602da7a564","impliedFormat":99},{"version":"3e57e28f71180845f9377b45ac462c9b443dd0edc83009ccd5f2e2fcdf171cc1","impliedFormat":99},{"version":"7602628dd1add9bbc7a6351499742a2f7945fcf44b8917e9db8628d503f8c0f2","impliedFormat":99},{"version":"213a88998413b5057d8a18f6cab47b28ce157f8d4853ed4008aadc5557116d79","impliedFormat":99},{"version":"60eb6f2e3c880a01c9c87aa5d3433520f5aa06e9b3e9e576bf4b116a223a47eb","impliedFormat":99},{"version":"d90a59ef8afe6b1cfad54c0b767b7f6504a18ac59584fca7619cb93727ede2b0","impliedFormat":99},{"version":"4e49f4197252788d9da2e30b4d2a99221bb34ee5cac02618b2c8d7af7a2356b2","impliedFormat":99},{"version":"567b74f4d86f4b7251fb2995d9075f144645b403722278e8fe48437fb0b13e82","impliedFormat":99},{"version":"895cfd1b2b5dbc001ab5649303d5247d10405c87d60957af579115fa78c596ed","impliedFormat":99},{"version":"e760f564f47f747f15d449002994a34cad22ea703dd794f38f49b0ab074607d9","impliedFormat":99},{"version":"61fd1aa937ad61531b8f4363508535087a585ee2abca24a6cdc12156815c9c74","impliedFormat":99},{"version":"2913927f917f24eb3acacdd7e63391525f261b56e94f8c3ead403a2fa12ec670","impliedFormat":99},{"version":"82d8f9ac21f79a3653f25e726d3366a93ed890fdf0405560d5cb9d2a89fd0a0b","impliedFormat":99},{"version":"ef428e4be74815fb31502abda54f77b4dcd3de5191dcacf01b9d88bbb99c5ce5","impliedFormat":99},{"version":"a73036a39cc57d7c5ca50beb8ad667ce9cb7fb3492a39ab42f75f5ffd4a1f70e","impliedFormat":99},{"version":"147b3bb8ed39a7a933e1b103c3ca92ad70e4195770659bd763d6f6c2ed23b4c6","impliedFormat":99},{"version":"143b9e04721c82d719e2e374841e6dc10c116f9d8e9c9097374cd30d01329e97","impliedFormat":99},{"version":"9c821dcaed43c92fdcb24da8f0c751658c65ab66763be0edf7090cd45197a5c2","impliedFormat":99},{"version":"7ebff076b1e73dcc7f2c446ac86fda88a05094d552aa1ee2873195f126dae605","impliedFormat":99},{"version":"89a620a019af8f2c4a719b6250490e2c26afa78c902937d1627215752053a301","impliedFormat":99},{"version":"c50cd236db67f276c4ee4e8053cb24ca07a605c866e6d2b6015f3f1364a95d21","impliedFormat":99},{"version":"79ee0fbe6f9810d5fe3e42cd562c60b7d9e792289f80d33dbc5f0398c43c6e18","impliedFormat":99},{"version":"b1c5066383bb1cc471df2a7bb7f3ee2cd9ba29cd4cd84259bcbf8c603dd428d5","impliedFormat":99},{"version":"dc62de38767358792fc4a943fc2f16459ca7c641b201d013c35319a7ac8a499e","impliedFormat":99},{"version":"bd7917f330b8f603383e0a5ca238e87ce557404375ed5eb1c0c3a4c124108c03","impliedFormat":99},{"version":"6d249c3942d6aad1abe4bc9d40290938183740407823318c6d3e93b4c666af69","impliedFormat":99},{"version":"d60281d78865cb7cf2d3d1146643ea5e3dc0754945867997a4851e1101a44620","impliedFormat":99},{"version":"846b79051c6c7bf200e9e3da0ab68e6f1f18a38dbf913103ce19009a7d638f28","impliedFormat":99},{"version":"d32593d549ff49f0107bb6908533f210224516ca3b30a48c6bf1b3e2a61981f8","impliedFormat":99},{"version":"cd1e2a69a41f865b817933305ee2f4c9919f05659e5405c4e2c0b0596bb8732d","impliedFormat":99},{"version":"49bae819dbfbf82774dec08663b6b8ff74d513938fc5893ca970290d63b9cc67","impliedFormat":99},{"version":"3bc0eb204a155ada13f258c31f4d8f1949d1b65d92e7b9d3f4ae4fed3c5c3c1c","impliedFormat":99},{"version":"5f65c4e35c318fc5c530bbd8dfb104d1798140ce692341389ff2a37bbbb62a31","impliedFormat":99},{"version":"5dca40695f32f83168aee614af93459d3fb0a99649a7469a79a494b2c64676b2","impliedFormat":99},{"version":"28bf5090a36c11aeddd8f7ceda109baf821c916a8a9255a63164b1927eea2179","impliedFormat":99},{"version":"74f478d7f5a871f4ef87e96fb6a970816e3a1f814f18d1f9e331e264c98fdcec","impliedFormat":99},{"version":"84183eaeff29b0fb24c8da993fd153c3dcb9143dbec803afc318c9adb2602bc6","impliedFormat":99},{"version":"1f5fa4350587a76e72a862beb92bf761098959360891c330a68553d5045753e6","impliedFormat":99},{"version":"dbec65f3ee0d5c4675d87a632f92b387c490fdfff9aeb2b730ab8000f6a34490","impliedFormat":99},{"version":"7446baf2319f74a41272174d3afe6ae9083be927e253d8efbf1dbf35acdd73b6","impliedFormat":99},{"version":"ee12c4f79edb43f20220d38db712f9f93234d5143053f7aa1e5778cee99898d3","impliedFormat":99},{"version":"f7c17106ac6ae27fa0614784f99d4d32a293717fbda8d243add915b54ba15bdc","impliedFormat":99},{"version":"9a05031d37968e9d33caf7cd522d11e242b5c6d57cc01364bd975a6dcbc23dc7","impliedFormat":99},{"version":"d6bd4db7590d2df45e45bd936573f2feb7c981402c32e88b2aa077c8bc97c6b4","impliedFormat":99},{"version":"e43b42762d010e95fe11419f4304c9354d53af49d92ac1488f283a09def82cf7","impliedFormat":99},{"version":"929bf22cf368000d7fd25dc460e7910655ea27a591eaf3a10118a31ef1e7e9f0","impliedFormat":99},{"version":"8845274cf2ee7e9a86c80dfdbd1cfc0698ffb2a1771c576a6ff59bbc2d981a33","impliedFormat":99},{"version":"354cde80a2b43250a8e5cd79897c0700b08ae93cd6944faec6362b7298761cc9","impliedFormat":99},{"version":"944bd98837cf7ef42822b16d27f9c817489ae4cdb784854f68e0bc5ee4b20617","impliedFormat":99},{"version":"771ed2b96f71bb0fe75369887cfdcffbc19ed7328b7960a1154bee5b60e0f338","impliedFormat":99},{"version":"17023783fa09e32247613150d208e8a3ed8186f927d62d7403dd49dad80a3995","impliedFormat":99},{"version":"ea2a66cffd5c42deb22006336c8bd776a9a638d6f23f9ab85dc00a414bc27af3","impliedFormat":99},{"version":"4e50b03a7be19c65eea3a0ebb30f804ce2e3f05df98484d7f260c2017d51ac86","impliedFormat":99},{"version":"62f02cadcc1ea7d29e6d280f12c49042e4ba31a77642aed8ef167dda0eafb9fd","impliedFormat":99},{"version":"990fc09cb61ada30ce71509bca9f592ef1fe0f41b2d3e8742dba8daddaf8f3ff","impliedFormat":99},{"version":"3ada6433dfede454fc3d54cd7df9fc7a66cc3b75352e2cb91c4f1a785b8c281d","impliedFormat":99},{"version":"e5e433cce0a955548f03be69f721080898a54854fbb15eaad348faf881fcfa3a","impliedFormat":99},{"version":"48b8d321906e86bf468296fa9ff78036dbe7e3fd098cd15ad11fdba076bcd31a","impliedFormat":99},{"version":"f7df68b8b4f24d71b56c91de8392c4cdd2e7fc740bcf59e687246382441f19e5","impliedFormat":99},{"version":"1737968b813436792f1c70164efbec6e03bac6623cf02f06c7d03b94344da045","impliedFormat":99},{"version":"a8d662dcca4057fa7b26dfd5abb7bd0d8dd2b1b00e88169305731e3406b778ce","impliedFormat":99},{"version":"0f5543f19043872284654715b0265c9767be6b18c28b19ffc616461155bedaed","impliedFormat":99},{"version":"32d1f88289363d1a9b3e62d30a948a8dafd321e453e86232222e41d874a4adff","impliedFormat":99},{"version":"4741a0adb723016d6ea4df41b020b81a041a3ea6f7e8db5d7ad312927f9e3673","impliedFormat":99},{"version":"8a67ff741c2d8866c34b4aad44eb9117f1cb74567e377264ab66874ea3d31a42","impliedFormat":99},{"version":"fbb60aadcfb7c62cb5003739868068b3f10c692c3c70a57430e0c21b53c8f8e0","impliedFormat":99},{"version":"c806ae29a7e758565142a7bb7a17ecdf3a4628d4c536c7e69256bb7454343055","impliedFormat":99},{"version":"43387775f0037e6b4dcf182b95ae84a8729150486ff099308b4b0dd1bcb2ad8e","impliedFormat":99},{"version":"6d710d9854d1dd67d241a4e026885d4272d8d91d5421a4631129d4e11b7b4d0d","impliedFormat":99},{"version":"5412b7e388fd383133e1bd4df188f934316de3b657566dc0e8117ec6cc36f344","impliedFormat":99},{"version":"6ce20c9bc2346a9f3d4403c208fac1bebecbf4b9227509d02aaf98712e0e9a96","impliedFormat":99},{"version":"772818065260d2912d2a6074eafa52f6d770900c312c799aab0356855c521928","impliedFormat":99},{"version":"cf64ac7517f937f64942c0d369f586bce2f05529f7558e0fdfb40760ac41a900","impliedFormat":99},{"version":"3ecbc89d34821e576cebc1f90c0b701a120fc094504b2d23938b6de555498120","impliedFormat":99},{"version":"05f48d119edbd83a07eac10fb6079a9df80bb633b4d172afd88f006606e4a3c2","impliedFormat":99},{"version":"e40f944f8661ab9e5064dcc45c5fe7a25fd9408b4f900ce74780ac8f676f7e60","impliedFormat":99},{"version":"485156edff72c636b28d100abf479d3a5a06e2875ee00112797a4cf55a537b83","impliedFormat":99},{"version":"4687f979cc4d880315ffbb836235831a43e821343276e412958272d5557c3bbd","impliedFormat":99},{"version":"cef215d974c8b6944ad651efa0a26b7893a9e3dca5be4c57c0ee12e266ce68bc","impliedFormat":99},{"version":"7edc3fd9e7cdfa1dc9a82abee80b2046e7895771d62fd0e45ebdd492b56e0a20","impliedFormat":99},{"version":"cdfbf84a437f51cd05775b3fe226823b00426eb6e0727d27dafd83467df3a23c","impliedFormat":99},{"version":"6deb5531627842af75a6e6787a98292f9011a1149334f32b3e073e7416475a0d","impliedFormat":99},{"version":"f692c1e1b478ba2876bc3a447b12dbd14f2de514acf96ce2b14a6d756c5eb96d","impliedFormat":99},{"version":"ae85cdefe1bc5a081ce023e72ee7f2e49a0887c6bf59170f0c70978095f0b885","impliedFormat":99},{"version":"96684a29c218048362a24a59e238d3d638ff35f4d39bfbc2605bb416ded35c5e","impliedFormat":99},{"version":"4b756ca804035896bf60e6bf041a7ad570849cccd7ef556bfffc182e140ba302","impliedFormat":99},{"version":"f5fc281949377a37094a70e03ca71954c5acaecd598f6ef277b6179c21fa89a4","impliedFormat":99},{"version":"5affd74120cc10a0db5dab852eb7dd4b9bd78878a1ea6605998183aaca98d0c2","impliedFormat":99},{"version":"5cc5a39e9ecfad672562bd0e0539c03c87493d37f7767aefb7eeaff838784898","impliedFormat":99},{"version":"1f99a65846bdf898510ff232de26c10ab5121652615eed66acb133ea4f1c75a6","impliedFormat":99},{"version":"dc7930292b5c6c1a6dd7d6a6442017af00dd7fd1dae01e9d9ac507e2642cdeb5","impliedFormat":99},{"version":"8d5a7829c3a596157aa64319044c60238d3fb24628e6d7009c881dd27dca2c57","impliedFormat":99},{"version":"431abb8eae688d769fdb2840851e6acbc511097f46bcb627a8559e0c9bdd8287","impliedFormat":99},{"version":"27dd0a030be0a1eb7973c195d83c380b93b4d5a6e2dcfaa5fed614e61378efc9","impliedFormat":99},{"version":"9a51fd5b49a5a1a8cafa60df9ddf08a4de98057fb9e6090d6ed63d5087752bfc","impliedFormat":99},{"version":"d0b947662b2cbbd43e11c6ae38295438563d3b9f46e5f2a40c2bbf2a6a48215e","impliedFormat":99},{"version":"6354a2a194e3ac52d3e31ed7aaa6d72e7472668990250d2ed577975c11836907","impliedFormat":99},{"version":"96f32d9bba69ab4cc8543de9ffb49ff99d8ca610b540e688de355a0286b31a7a","impliedFormat":99},{"version":"d753469e50a02ca7b5ae50f9e9741e945bad0feb30f45f8f02efcd14aa000a91","impliedFormat":99},{"version":"9b3ee523f50a0ff0268204a4e71f0806056574d6cfc3c030c583f593f90da307","impliedFormat":99},{"version":"768a528d14de142bcb24ee5f212372e46d956a73f95961841d3366016c0df5e1","impliedFormat":99},{"version":"9a3bb818efd76fe616f6faf460c23827448cdb3d6544f6a3d3e2964b9357360e","impliedFormat":99},{"version":"528488cde0f4a8739e680c0370aa1701565916d64f941d243f477d7dc23d92b2","impliedFormat":99},{"version":"8eff788c2d1a8962c67cd92de23312acf6be73f34e8981e82d9e649cef7ee6c6","impliedFormat":99},{"version":"d7144cc95056c3f17656ba0528a83a3b7dbcd8281228a22058afba7ca5374a1e","impliedFormat":99},{"version":"41161faac7427a410e7affb7f725dad06648871655612592dd82ab5bcbcb9135","impliedFormat":99},{"version":"0dea00c26a0a3b6974d17e7f372d5e065415e4737a4dee77aa3270b850b051ac","impliedFormat":99},{"version":"92b9d0ffd51342fef83855e9ab909e2df294f56deaa3e71e3d96643c57e80be2","impliedFormat":99},{"version":"cdd4641817b6193cc37456ee8b0853270928ca9cf44d664ce55334611dbfd091","impliedFormat":99},{"version":"ccc28dd7eadc3d48f4fb5b9a7a6aaa96afbfa453db4d605ce860b7c2e376b1d4","impliedFormat":99},{"version":"8e39bea3a3fe73f4a3122e01a8b5b53bae372fe4f191db122d13e20964b46e53","impliedFormat":99},{"version":"19fe17ea045e492575b7a93ff798a41f4ca79eb20cc599dbd9c0d4b0ed3032ff","impliedFormat":99},{"version":"11d691a213672e5c29d65adcf469af5765323c19614886ab201a9d0450d1d580","impliedFormat":99},{"version":"37430f9fa8356c5083510b69614747895f8e6bc8622ed5db4997c4454a368c83","impliedFormat":99},{"version":"fb61f48f111198aa36fc2c0b53090f054626c0808cfb75bcf8f0bea6c04ab967","impliedFormat":99},{"version":"fc2e5b7a766e1765c4f62d88d603ca554a4ed55a1dba837e53e1f8fc186a0eed","impliedFormat":99},{"version":"501dbcef0321dc39e6644182cdbc1c520074bc1b87732c04110f1954f555917c","impliedFormat":99},{"version":"81c8004b8002435ca85ca95596f6f429ea41314e0e41c3c8e9a3c21b8bf9ac18","impliedFormat":99},{"version":"c7e8b24ea389c9834c87176bafb3212df72f83c37206fabdb54a0cc3c4e5eb97","impliedFormat":99},{"version":"c7bf9c66bbe23e9af8c7ce4b22375542aad0aea2f9014461be3e2d42268f77d1","impliedFormat":99},{"version":"909a48b64b6648d5ac985208884af3d22c2d98f0db5b96c4796709147080ef24","impliedFormat":99},{"version":"c7cb614a30415664b97510a64033ca1fe12271d28c7a2eba863b093ffd295b9e","impliedFormat":99},{"version":"7fc9d0d8798de94531d62defb7add38247c9d74e7fcdcc2c2aeabf8f2fb6bf71","impliedFormat":99},{"version":"6e1139048a6c2737b5c48aa1652470c87607250daabe8a60202d2ef6eefa410e","impliedFormat":99},{"version":"f372ba4099e22f247c5ac8cdd8dcb336ea6be921e652ef1ce6b4bb16866aa03f","impliedFormat":99},{"version":"e1254d798521d741d001103ee25f05e6c2c8c35aa77bcabe0ea883f1fe2dc396","impliedFormat":99},{"version":"e8ecf6a4f4c29c70e5aaffbb555ba7f879f6538e4ed5013d51704b1db56e5d3d","impliedFormat":99},{"version":"8648fc65ba19658336d2adf371623ae3aa0476e0e3f40844484966c0744ad6e3","impliedFormat":99},{"version":"0b5e8df8d9306a7b6a328d7f006cdeb9a48e76a81c310bd85f2e4f1c5482934e","impliedFormat":99},{"version":"2138d8b358299b39eb15a3a66f80c9a2180ab8f0a151816722012ef667639d3a","impliedFormat":99},{"version":"0f8a46c4ac0976ec4b5336db0fd1aeb5e3c5c621b1e24ba452ab634c589a8905","impliedFormat":99},{"version":"ef840e20a0800591e2c9641ecfcfdc858f729bbc98de06ec9774f755860acb53","impliedFormat":99},{"version":"a7d38d738679d2832bfccd16dd72b22dd7feaaed39807da247a1dc3363c143dc","impliedFormat":99},{"version":"a1aea20a79d90817634e360807a37583ef9c85fada5b4d22fca39f4ef9b1cbfc","impliedFormat":99},{"version":"6a1b90d95953c0a6d784c219c376da5f97f4c3e9e107fd16240746c9eae24458","impliedFormat":99},{"version":"eb5575906d42e0c6d3390dccb07a6ffacf2a8228303077e17ad93e78288c0457","impliedFormat":99},{"version":"5c021b1f362a525b29180d60e304a02f2efbb03f3d919c809453d5549d03c262","impliedFormat":99},{"version":"2c914292c54b51e39f9b38db3a8867befcd513eae7cc56382408134bf0f09637","impliedFormat":99},{"version":"c1bbdee70a4e8bf70ae222d766a32ef2d46cd05d45c8b0ea505e364c00402af4","impliedFormat":99},{"version":"d30606a057e1daaf834152afc4e7071be01a0d6fdc5158b6286213a7cbb6fc72","impliedFormat":99},{"version":"5a6e0b5f4f019c88c54066dccced44670ffd2bc53cc0002f02ea088efaf0d371","impliedFormat":99},{"version":"8bbe129e5211810b9d8a988cf77e30d9cfd99f73a8f6c4f1414ad3ffab0e3a25","impliedFormat":99},{"version":"240f280d0ac7bceb9d8e00e2ef70e17e970b22d09c52d5de97794de1f910e864","impliedFormat":99},{"version":"a42b2ec1256092b2173833c3790b435277a717ff20e3de9a5519c75565ef980c","impliedFormat":99},{"version":"01653b53208b75dc66da6edaffe5e9997b5e3026e6f32fab83287d47dba6b2ee","impliedFormat":99},{"version":"555ddbb1c628ff42bc14f2bddc8080083ddb39ea2ff50cfdd2b3f536289aa4be","impliedFormat":99},{"version":"309d3fa28b1e606886c9eafa062868a8f66c58ee0321a07b8e1f45b68d58a787","impliedFormat":99},{"version":"ce93623d553e62ad7d68e33d1600b63ff45af9992f39524605628d4c009040db","impliedFormat":99},{"version":"5c1d31cdf67886fbc6a802f6404e0edfb2cc84db99dffd9cb22570c74d85ac96","impliedFormat":99},{"version":"3e9fed9ebeef1f6a0a281e32f8916a4f5c2bb7949a1b0c083ce2f79d64831321","impliedFormat":99},{"version":"0b28f4d4b177efd916a33cd8f3b8130c47d2f986c005b5045fe5e489cc120148","impliedFormat":99},{"version":"8e71c3b4acff6d1bd0e5f32615f12cf2e769aab29baaf69e3eb52f412a8b2493","impliedFormat":99},{"version":"57e7282b9432db3300945703a15bedbd010a13e16bf6015ef3419f92f5ef39fc","impliedFormat":99},{"version":"4e141f8e0f5d5a522269eb1dba88648b9196faa3e8775642dc65b3e2d9db0f4f","impliedFormat":99},{"version":"f55c6c9be692ae2cebc87bd89f0aa78a6dd6435134368ce9ed6310bb29a164e3","impliedFormat":99},{"version":"da8edef43572e4b96a6d236fc9ad0232ead1a8cc2e45f382b3a282ab28264564","impliedFormat":99},{"version":"f92a3d35d50c9294c8590b92501891dc46b740f8ccc87f062bc6c26396a167a6","impliedFormat":99},{"version":"decf7692a684ff95fa78b141adadad5e17d45674ccfc3c8acce62d75bdc09236","impliedFormat":99},{"version":"985494fe7f7aa21adc81382e8ef1a485b2d5ba73dcb5d67acadb6fc3114cfcc9","impliedFormat":99},{"version":"774aaacce77eca677e43e61712b8aeb42eae38f6505166d2af1520c3f42e53a1","impliedFormat":99},{"version":"ce615a9d5489e7be41c23da22c859710407717122cbb08b70e920f0ec2ad8224","impliedFormat":99},{"version":"35ed6e178abf609f84f8fb91ba6eeb6cdb84e3af5f5daa7d0be93715d40730aa","impliedFormat":99},{"version":"ae87ae1de03d4932f114234d28367fe6e7493ec07583c7ef2a6ce36baec3f6ec","impliedFormat":99},{"version":"ee586d6ca4e172e2a7809342fc988607f584041ea190495d0fa7273c9d377665","impliedFormat":99},{"version":"d6a296ffff2b3d8bb43cb7cf440466144247e214ece2b158540cf9c241fc414a","impliedFormat":99},{"version":"92a0210b5fb2d7446b1870a627278b6f1af429aca75815a248ab5e43a3d7fe5d","impliedFormat":99},{"version":"35fd7833b2679babb123365e2070cfd507ed09383d8a6d9859e1057dad082721","impliedFormat":99},{"version":"5fa41b8118266851e99eb3bc55da87c5637f753c1c984c72cf85ccf42d00923e","impliedFormat":99},{"version":"1235c2a22868a8403a0dac18e079c1fb1283a0e6096874f2c4121496c6028f07","impliedFormat":99},{"version":"ad1a154e84dbe5f6edc3bdf9da37dc6b65a5a81884b1a07cd4c10ad357ccfe4f","impliedFormat":99},{"version":"531df43ba635c00149a8798e18d30010673b55a60fb9bfc80f3e335858b1925b","impliedFormat":99},{"version":"704852198bb5e2ea5be7e4ad047b46f8e6b8865c26438a0a433918212c5efab3","impliedFormat":99},{"version":"0ca7274836a9b81616499a3381086f82ef43dfd0e85737699b2f2f625a9ba116","impliedFormat":99},{"version":"46b2899a134650e9298ca1b5135ea6f84b9abcecec900403c0385897d249949c","impliedFormat":99},{"version":"6aed0be88154da7647a94e9e7d7ee5b3d80368d5f1e45ed07877861e94f475cc","impliedFormat":99},{"version":"41144ca9abc4df33612d75b5ff04c8270f8be6dc34bb041a14da7bfd3cc285d4","impliedFormat":99},{"version":"72be0d8ceabfabd4be3edc5d070e7aa24bfd6d8e3b4d209ad4c8df0d4ba8ad6f","impliedFormat":99},{"version":"1d2bfbf94aef782520c86d03255144c3e12e46f3ab495637c63da0e5dfdf5082","impliedFormat":99},{"version":"f365f232f337510cd1fa5b3049057b37da48c42f1b5b9b61a8f656019ada9d6a","impliedFormat":99},{"version":"c13daaa17768f6f9a43d506da39f27e7927532be920a9b28d2bc852e025ed0ea","impliedFormat":99},{"version":"ed3c521c87d6d43ddc6978ce7b8646cb698947b48790ed2557f97346154f1d43","impliedFormat":99},{"version":"111d37d2a38eb93101e9faf650fa070ada634dd87d2c76d2bc29e9c4ac8c624d","impliedFormat":99},{"version":"bac6c340d8fc911d121cdad32f6decb9cef533527dd3c5095c458f5fb82af355","impliedFormat":99},{"version":"933f35fecb3950cc3404531e18a0e11147431183d2f37289f1ea76863c770e58","impliedFormat":99},{"version":"1d8329570ecb9d80b36877d49f485f26b96ec0dd64a1f8bc287f1c27729326b0","impliedFormat":99},{"version":"f5178c684c14672956e8e95224f09284a771121e21c2ffc2cf7eacf8867c2c4b","impliedFormat":99},{"version":"cd21e09ed2854ee1937c0048fc7a9c9186bd72893bcb873b88e0a86cfd688925","impliedFormat":99},{"version":"5a99aa699a2e5a397692bbe47d181cb302a54af785d0d7048b4527fd5bcfd5f2","impliedFormat":99},{"version":"06151a29072e6f5af0c5173713c8d6ed7185056f57228a2cdea65fe9f4af2cb6","impliedFormat":99},{"version":"5880a6c580d46fb3045e53c425d9bfae910173fabb0495f41289f4317df63228","impliedFormat":99},{"version":"da6817a95523f2c02ed0efd1af69cf692a22ea08623046cc6d47fbc0f433d222","impliedFormat":99},{"version":"5d99a8614e20d6cf864f8ead53af82fc0c369b1414b3045723c1da6c8cba8db7","impliedFormat":99},{"version":"0cb1b72c10f1127b2516082f640a6aa59cbb2811e8016b1b27068728d3048f9d","impliedFormat":99},{"version":"420707c563aa1869917810fe446947a5afbf3fa9296afd8cff3e9c0abc0ad7cf","impliedFormat":99},{"version":"4e56f2becaa1a5182afd52a6431728d7ddffd30d60e09c914862752db8564b14","impliedFormat":99},{"version":"902396e34bf752720d7e51482d457f701badde1a447fcc6a2704e540b6e8f648","impliedFormat":99},{"version":"0617212390cdb27999e1aaeaeedca724059045b00aea9de6efa3a95455784e8e","impliedFormat":99},{"version":"11915ab9d51b19fe9ad0ca7f85ed7fa4ac2212ff9e158b1f0853de9ffb31d3f8","impliedFormat":99},{"version":"c88c3f91675bd36c4a0a63c2569b9078c249d973e2dc335c44cc7f886f920524","impliedFormat":99},{"version":"145c292f607f7c346cf28782574c3416335b78f2a3aa63647e577cbe61b229ef","impliedFormat":99},{"version":"f3d220031a6be5c27ffe9733480647456acd5785a586c064403b2c4630f44109","impliedFormat":99},{"version":"7b545e2b17993c031302c840f3972618b377d0348c812feac791b3710d6a52e5","impliedFormat":99},{"version":"80c39f130f281307474e6eb5ee27f3db7bb9c166ccbbdd6bcabc9a655c835118","impliedFormat":99},{"version":"71c3d3de8006951d060b4e98b3fb735041d9b41da839229256b2aec31dac2dfa","impliedFormat":99},{"version":"4ab058a2a2554b612cd45ea9f6fb7c6d28f87ba8b6f29b7ab997a475eba04947","impliedFormat":99},{"version":"d00e87c4a9c84e81c58d63644df9d9fc2fadf9438666349169bab8119af4b76f","impliedFormat":99},{"version":"55538c4d71453284f401458ada818ac59aa23635c06e61724c991bfb0d12b09e","impliedFormat":99},{"version":"da2ffee514e131297049f9325d3540f86c57c33ac9e6eadb058d5157cd6d0f28","impliedFormat":99},{"version":"d5ff2e0473b949236dcb1de04ec65ec8bb8bab047f3cf5360d9ed09d440a076e","impliedFormat":99},{"version":"328ffdf6ff15a82d4da430a5cd83583d4823077f4d7967b4435b8e94f01c24ae","impliedFormat":99},{"version":"6ae844af3d21c8aa979a7787f9f29c8b49fbeb9b551d3053ca174c7a7935ee27","impliedFormat":99},{"version":"9de9f046428da61ef9401644fde7cc0ec3b078f8ac4ba46ca11e092d7c9113c5","impliedFormat":99},{"version":"1b75adc95b1ee5063e097350d2dc52c60866a96ec9758d45f568c90bbba96a0a","impliedFormat":99},{"version":"6174664f54ee2d69c173bb59039ed2ed5933fdbacb1c65a68dec2378a07a5b53","impliedFormat":99},{"version":"084b9ec7442ec67c069151bfc2f96fb52c15147522be30a344fb832e894ac5cf","impliedFormat":99},{"version":"27e4d9779d008185b9fc6af4aa23020abd8d60e750862c291109f63cbb7dacd0","impliedFormat":99},{"version":"7ced3f2ce26b4873e57e21eeb66ee38d50dbcdd85a3b3e2ecb59c032f90d121b","impliedFormat":99},{"version":"5a7b2587e3eedf789f4cf53a70ca14b50e9544c6375e5bee8ed437199bce4369","impliedFormat":99},{"version":"2b8ef63805106107c24d2541f7c6a1c646ee3fa4b40462f744596062532da8a0","impliedFormat":99},{"version":"579eff0ce54aa9af3193c2fdff6d56eaadcdf5eb9445a0e7d223d4207d3ebb8e","impliedFormat":99},{"version":"900f927b8241a8d2fe0059b865cdf4a1ec697eeb8db5a9e0a0bc8ba4bd41e9f9","impliedFormat":99},{"version":"80326e6394074ec22bf5904bcf65125efdb76618f6e8bfa5134c313946d25af7","impliedFormat":99},{"version":"3eff7f088701b5fa0e1c0a7659da4c39f8c963a8cefb906eae896fe737803f58","impliedFormat":99},{"version":"dabbe6f60de2ae16eeb00938f313feccad30f4008de64beac7941e1b06e48f8a","impliedFormat":99},{"version":"b3d4517bfc2fd940adf5479130823547a2669a75db07980b5121986767a5f25d","impliedFormat":99},{"version":"1a8ebffde00061665dd2642882e8ccc078038f9c4b89427799a17e58b5769727","impliedFormat":99},{"version":"c2704bf35d33087174d347cd24a419884ec2d2b5b947fc853129c1bba3025f94","impliedFormat":99},{"version":"cccfe875486da7a0a603ad28dfe4c8c93cf1991c8d535e997f932623fa7ff56a","impliedFormat":99},{"version":"d5fb33c5874bc48e3c8e07f08d55579617846ee01264fb6c74dad8f6ebcbc115","impliedFormat":99},{"version":"2c0e0ccacb0d466bec5610164adf6d5acccf3641bb637132e3cb581701f46afa","impliedFormat":99},{"version":"112907ab30b2f8464dbe58936a531f5ecb3d9f52b2229cb008b576f79f6c1d81","impliedFormat":99},{"version":"ba72dace2d410f8f01daa3edcd34a8f670990877ff1fc591a397a9da3784684c","impliedFormat":99},{"version":"8528cc2cb84fcb47afebffd843ccec24f4cf0870fa01516b7b9412e1388c0193","impliedFormat":99},{"version":"57f588f45f2e9ab533600cfb6e4a616b85333d86dc674d643b6066196b70e98f","impliedFormat":99},{"version":"11c1ca978bbce58eddb5ce4249cdfa00f00a786bdf1d7c67b9f92b849bf96f4f","impliedFormat":99},{"version":"87650701e43e24cbb2c9a3528ba546a37b1d9090b5daab4119b81234bfd275b3","impliedFormat":99},{"version":"2d799db76b3fab467a0e5f3a08ac77c459159aab7560ed9135934798093f2952","impliedFormat":99},{"version":"ae1db2c109441eef070c094e96a4f596a63aa3366ec95cbd4d96c6c84ce14142","impliedFormat":99},{"version":"02e579ed47c50ddff81c192793b0d48edf9a5d2fff2d14f72c05ac074ce51f7c","impliedFormat":99},{"version":"a301ad127437df84c9b6c4abdebf2351ba3968a2d16a2fd39a9e0133f541ebcd","impliedFormat":99},{"version":"6e03b20fc4e616122c482cd9d2cca55df1ef9391c6d0076b818a833b76447eeb","impliedFormat":99},{"version":"5b60c282b2526e10825ef9132f04153d11f3b0b0bd561d58bcec5ef54e5e3152","impliedFormat":99},{"version":"80e15b33a5675817835b1a3a375a29a7a51bf80c1cecd4029cad89afd702ce4d","impliedFormat":99},{"version":"6a2159f263a83da0549660067cea7cfb7e8a750f8d064bee44c4d524566558bf","impliedFormat":99},{"version":"d4e4ea6e19feb0d2d766a62998b9be84f1fc8954401d75056a9b149cbff0237f","impliedFormat":99},{"version":"a14496a6b93e019ccbb14793bcbabeb7ea0413118bca92d4b9546519c168b1ec","impliedFormat":99},{"version":"e0c7169f2c627ede162448c6ea687e98b3fb08b811fe23638e2fe603ba69b18b","impliedFormat":99},{"version":"f8eb6cefc4a7b16e721f7470e71f0a6607f8a0f6e8a0c6b82d50b3885fadc5e4","impliedFormat":99},{"version":"619bb4ca85deeaa62b42ff87b1e574f2855a8c0300c94ba181da55fcaed139ba","impliedFormat":99},{"version":"953c4dafd6149d6fd4753bfabec65742cf6b322eeda2c19cde880dea0d984d4a","impliedFormat":99},{"version":"12006d07311fd0652f43ca80d9c4581e215536efc347773004fb641fa2d609c4","impliedFormat":99},{"version":"3240939c7d9c20d547f4b4870feb53a39ebd766c31fcf08605364d8136cc1c25","impliedFormat":99},{"version":"c6f802636d25b76864d0e33a6e2567ef3e69b3d0f8c8261bcb22f2c4329fc832","impliedFormat":99},{"version":"93d6ffb9316fbf79e9f08b41f54c153c0a945d788fffcefe8d96bf4f601ffc3d","impliedFormat":99},{"version":"19dbdd6bbc146165d37157fa07b57c93595f0de6fffc37830882e8a9ef0ded8f","impliedFormat":99},{"version":"903dc8d5da85086cfcda4cf954a54f635457f02a8c49725999495be361bb840c","impliedFormat":99},{"version":"fc1f65f787606d06c4237a5b2bc2dfedbd14996b6b1e0f5c74cc80b2614221af","impliedFormat":99},{"version":"8d26da673490f949decba5cd82d8dd048bf27cad69c944c7450cf4265d5d728a","impliedFormat":99},{"version":"fb496885d76f300d8fc1630f8942f9268561999f3f53182ae9c66b16422edd3b","impliedFormat":99},{"version":"b02d35115de961f7172559bed1bdc2a77ad98e43f355113d07b35c043823ce8b","impliedFormat":99},{"version":"3017feddddb20031cc1906497d7aec2410fc4b09a9435218ea79f27b6b263747","impliedFormat":99},{"version":"0ea0b18d40b47d1b119802a7d4ca9a6c499fde684990f81a15537db607ef266e","impliedFormat":99},{"version":"73a1763b3514b7f4642d41321de8eb44981907cb545679f0df24da76c3478998","impliedFormat":99},{"version":"7608c417b1f705610e9dd4b1ba5d454646f45874367099d704b6898032b209ca","impliedFormat":99},{"version":"2b84a76f720a51de0168dc1d7b9eb155877b917490ca9f090bfb91ba9ad08c6a","impliedFormat":99},{"version":"7739b19042dac28c594a9b7976337455ba76d8c9bb1d76726409b64ccce11e50","impliedFormat":99},{"version":"4a39a7d7cfab5add57fce547a24fa22c98f057fe3f5f735977be1b8b5de9b270","impliedFormat":99},{"version":"43ae8254ecdc2cf8ab3b88f7b578ed5dcbcbae4b91920e5b9ce86df56f44dc7e","impliedFormat":99},{"version":"0a7b14be670b5c76d0d8af6c145b666cb01eb35e847f8ce7d26c8fe929a5ac49","impliedFormat":99},{"version":"f86736bfd283fd8c8bf74ee7a54ae522de003856e94f1be02d9a3a62df106e7c","impliedFormat":99},{"version":"dfb7c2ae42ccdd9bc44368a6d88b6b67d6dadbbf7418918a4d800df1362ee40f","impliedFormat":99},{"version":"e368a3eb356ace34eb714de3d8d0525274c0ddde81ceba99887352e03af5f320","impliedFormat":99},{"version":"03b36aefa6f11ee2be780a47093568f63c4891926b415b5f257b15a7a8992fa4","impliedFormat":99},{"version":"9cb5f193ec1bc65aede731da43683c066b0b7f5c7e3a08b1d023c51776fa80f4","impliedFormat":99},{"version":"ba900c9eeda74457abcc5bb202e0bcdf85236c1fd14502c66723835c259d3b33","impliedFormat":99},{"version":"85aabe45e408643843dd6a1a5c21660cb1d8d92db857ea101fc9391aba24c5dc","impliedFormat":99},{"version":"69e2c220d72693f356d236ea7e5785a3b0c43bd170b21debc8e38cbaa1924474","impliedFormat":99},{"version":"ac5100599d1de573f7cdcff0dd4589a3fd931eceeef51162eb91a9e00fe39a94","impliedFormat":99},{"version":"71914cd32d708f41a67816d281630ce050907b924e85f6d108f88cbd254f15f8","impliedFormat":99},{"version":"bc60e6c797e54b3694a0df72db1fdf827e354b42d23c4a741946474b3e143201","impliedFormat":99},{"version":"d230f6c710bf20fd4cb767596cb84619a8558cf607b6c8b119f941732fbfb6e6","impliedFormat":99},{"version":"61f81f04c7420f8e981b5a55b9831b6ebd628d4f8262fd316274b0c6394064ca","impliedFormat":99},{"version":"a8294a7b4247219d8edf61cda4b0d5bdd4f16547a4757988002b5c95270fa1f7","impliedFormat":99},{"version":"9ffc5abbd23dbc8c89491ac0d0497bf330fc0803eca2f07d7ae279803bfdeaad","impliedFormat":99},{"version":"60dde1f903f263168e122d6b9dface9703d2dca0a0d93cfe86349ef2b14c1985","impliedFormat":99},{"version":"4ddcb66616f1592c0fdcecd9fe5fd3e1bed3bbd3fa2d2420eab9303e6b3e6af8","impliedFormat":99},{"version":"ef35ad3e78c706e69ccfb3c2ecbb401f11c7ac84f42414057457ec77c1aaa2fd","impliedFormat":99},{"version":"c5869b678b87fa2cb20b032f9a31485f6224c9608a3e18f2226d76b580b21583","impliedFormat":99},{"version":"fd6044a1defb17c9c2823c4fb4c355ce909e8900783c039ae362ccc83f7b81be","impliedFormat":99},{"version":"02c0bf2f8bcb637efb66cc4c7b47bc4c8f11dad44562c3f120d10521111b1a9b","impliedFormat":99},{"version":"e48e6f5b37b10d8ac049165416761c874275ca4dc48b0b3f162a4150dfaeff02","impliedFormat":99},{"version":"1b7b2e76f87e8fe15729c9fceada944d9a989f3be1b94c0dbf958c511c3ab635","impliedFormat":99},{"version":"dfc440599a5c757a9811c12426ed0105331a8e1a102cfa47457fc0c0fd67ed0d","impliedFormat":99},{"version":"a2dffb7c768597bd76cf792b59e6c2d5b32caa0dc463ca4cbf461ca5a7b97071","impliedFormat":99},{"version":"fd64860136ee75fdc604e6c5d10d11faaefe63a587a8619688d80c13d5fa9f27","impliedFormat":99},{"version":"73dc87388d39a7bf3a5571f400573395e5d6335d5d8ffe505d9272ecba33e762","impliedFormat":99},{"version":"51e0a0df7b660da59c04454eefedebcdbec88360cb7a563252e63b63d71935a8","impliedFormat":99},{"version":"f3f90b80c87f61cd970da421f48828081cb6542c0e4efcececc6759696b1ac52","impliedFormat":99},{"version":"ee48c4e4c52150481dce12d364b2e95e838eba5d7e32f7baf948029193ad01b5","impliedFormat":99},{"version":"155d13de15c2d189dd00191f09e74ac108f85d5a5bcc1761c4380ce376e5c89f","impliedFormat":99},{"version":"b989c158627404a3587fa624b46d894ed7b25f52d826fede0f569f4d7f1fb5ec","impliedFormat":99},{"version":"cda26b6d5e64c0ba65822c4bb15099490821a74f3140d95f4b562ed0040abffa","impliedFormat":99},{"version":"b32bf41fcbda086fa4f1172599d568f371c68597fba5197fd1f5a60db5c42ee0","impliedFormat":99},{"version":"ef0a4378730dcd1f298e36b9bf4bbbfb859fef99fa13e25e63deb4bd63a47a73","impliedFormat":99},{"version":"f3c0082857ae01aeab270c8322df513159dcb1cc35a6d501a581dad62f267a93","impliedFormat":99},{"version":"cf990447acbb287192c22067e2cfb28ca4e685d1782519720149de9d4b6ed19d","impliedFormat":99},{"version":"d161c835716d83b8845d11a83848933111141b34218b7b0f32b76703478c39a0","impliedFormat":99},{"version":"27e785e6d23af7dc4d0666eb469d4b8b8306c85b349ebdd3829cc5120406ee08","impliedFormat":99},{"version":"5f4ca5547e7c4f66270b7f2ec1ba7101e248ee195cfdc758291965a929d45663","impliedFormat":99},{"version":"29ac9b0db437cf7064a42579e787ebcfe2585f1c8cb0e411e44f5f1a3a2ca9ee","impliedFormat":99},{"version":"d1ad943e1211fe78a0ba99a015674207fb2147f292d4ad69691f925491fba7c6","impliedFormat":99},{"version":"d7c28b0f31f917357fd7ac64e1ab89a251e5e3206295546ec25b889a8419bf32","impliedFormat":99},{"version":"4a3e3b8422d63c712523bbfcf90381ebc283b6a02677d5fafc1b899bbd6c37ef","impliedFormat":99},{"version":"2abf211010168e7ecead68a76d3be5ea5a5bddf7fac893a9e36b711e5be2d957","impliedFormat":99},{"version":"e8420b7685cbacecd94a9e783260df6ac512337fc7c2b5c133a8137df9f5aabe","impliedFormat":99},{"version":"c1c2a8a08c29d1971ea15d2c07385636f508b75de41fb03aabe2eaf32c359ee2","impliedFormat":99},{"version":"54ff0fcee78cf8a8c49536207a9f17d558a31e7b4d816d92087d726b4b928a75","impliedFormat":99},{"version":"731f23bc7237c6e244a040bee0146de7b4c8e326ada953bd284236c126f39c17","impliedFormat":99},{"version":"7d75d200016c8bdf6a8d43d99976cdfe98baa7f00c11d93887aa1b4ebd73adf4","impliedFormat":99},{"version":"634bb0c12f4d77e7e05fed885de75e45fb7eec41b92d499d47cbc076d8701156","impliedFormat":99},{"version":"e33084151e553dcfe5d0fb5a393872988deed2fc79ccef00104b184ada1aad53","impliedFormat":99},{"version":"1055788f50a01297982c85fa7fd3559d8a6bc92668c9dcdbf6ff1ac438fb1067","impliedFormat":99},{"version":"cb5454fb39234a6198960f62f75ed5df73b8346f9a5d880439fb0a131752b07e","impliedFormat":99},{"version":"f02ddb781aa9d288cf9be73e646383eff0a22320b5e37bb6a85f3203edc8265b","impliedFormat":99},{"version":"ad14bc14e81409b598c62ff0674590d4dae26704b8f0581f76c078f218ceae3a","impliedFormat":99},{"version":"f762357e85caa57f68b7891196d826b9dd229aa47d41400fd6cb1767f1aea408","impliedFormat":99},{"version":"c55915885a5e4979533ae33f4f6b69def0af109a6cacc6b8973f62ad3be87c56","impliedFormat":99},{"version":"55abfc315df795bd61c0bc3e512e354af890a8d5e15bebaad4638472ecfb8b7c","impliedFormat":99},{"version":"7faa9726c687f53dc41efe4fd8b0f4051b464a2ec1e8240d44864f8262a994fe","impliedFormat":99},{"version":"a88c13504552db98058dc3ac72c66b9b5311453214cc8b1ad917bc7e54d7fc89","impliedFormat":99},{"version":"f266c182bb42509a0cec63cb94e8f378ccf38ac951b8cbf5ffeae109f8f0be0a","impliedFormat":99},{"version":"31bade8607b70948668fc63979f6c715b4a9273e5890cb2e98734bdc7809bccb","impliedFormat":99},{"version":"8aa0143434da7de0e19cc204e07885c227e7128fcf5bac3103374141c60aa4b4","impliedFormat":99},{"version":"3eb1aae366611498c0e14972fc9a9b5acca56ffe34978b953c3fed68d1a94a43","impliedFormat":99},{"version":"1c9c03dc2fa288c3529c22cce3fd1a4711c6ae207857d0e59217a2358606d2c1","impliedFormat":99},{"version":"bf2a6d968a97ce8f1c781b633455e17ebaea246a2c60c45d2ac3f201e4c9650d","impliedFormat":99},{"version":"fdb0bb204040c2a62d029b9dfc41ff9838e13a1d77a36ececcf28388e78598d6","impliedFormat":99},{"version":"f374c7f42510f46e7ac93352807c9146a40978f18a65d4cbb60d79acef6dff5c","impliedFormat":99},{"version":"af747ccd00d1efb4c70e4646963bc683595ba6bd91c56bc624aa9993dddc33ea","impliedFormat":99},{"version":"4d636ee4ebe8cf660ba792e80151d77a59e73d6268b713416467a2d4fd95b5de","impliedFormat":99},{"version":"794469770e7cf424c4eac7a75666e1ddcd003580c6849579a3a5be2db293f67a","impliedFormat":99},{"version":"e971fff8ac6b8d2188b6e2af434107f7a387c874ef1eebcce79613d1b54a5f0a","impliedFormat":99},{"version":"ebc96da5e1b0eb6915035700a7f8cc46506926ade97c1964aa8cc244d4275c34","impliedFormat":99},{"version":"966c21e27152330e2c8fee39363a9e5b78327979107e695a0c0af560dc0582e5","impliedFormat":99},{"version":"cfc48fd633b3e4329f553b694feb164ddd8821e2c9534cb2fea40e7d4f79fe75","impliedFormat":99},{"version":"7a333c6917aaa3ab73d47662cdb849f44ba66d7a1d1e6701fec37c2067841880","impliedFormat":99},{"version":"9d64858df7ae34ea53dd2ae019183f01f7374e70c48f220a98d40285e7a7e2b5","impliedFormat":99},{"version":"afe7bfdc642978a70377ae72bfe7c2913989a93328f62030f3852756cefa6331","impliedFormat":99},{"version":"f056d9c42d94f84b87c0c5a7aec951c7242b4c06c045070da74ee98e4b59f036","impliedFormat":99},{"version":"96777ee3fed26a42e8a0a1e797d0a848ee52af4108c1286c5bba6c44f75d52af","impliedFormat":99},"781bf53a20d580df1cc83046e19ab1bddef9b91caf98fd52c88705cf777d618e",{"version":"87762f3c65c7da1e53b40b10f8acaf44c44ba6163537c1864414460cbe7ea14a","impliedFormat":99},{"version":"d601209b9dbc6ea5328bb86db4fcc878e18f5164905593116299dd77367cd1ef","affectsGlobalScope":true,"impliedFormat":99},{"version":"0a20a7d6ccca33343a4ef04ec5198e33147fd98c70ef2fae85848ed6336e9552","impliedFormat":99},{"version":"3fd76c770ad7b87bfb6ddda4b9b9b5ddf57235825a1a3609855bb9ccb6479f71","impliedFormat":99},{"version":"4efaaa494170ba9198ecd58e0d0b0b17c8b69ea3fe71701606c1421e5074816e","impliedFormat":99},{"version":"dae4d3c4358dfd6daf7226ed01da2fc17bfaef90b6d8c7f77bbe888a1c3178ff","impliedFormat":99},{"version":"71df853e50957199921c5eaf5f0fec68b68067902cbf8a6dcea8c6f6d60e6f51","impliedFormat":99},"2b29ed2d8bda1e060c8a94e947c01feecc7395608cd736a38d312515319f3202","e6e38ba1516390d621a443b8a3e12dfd6441962b7ebdb9724f4dfaa6778d139f","e21a2477d588fe5b25e1579ccf1b614bc68c3c753f0831107dec1024abdefc61","6e340b74a08e4fbb74fef9c6b6bb7c1529784c91980856e48893be33a295c5ab","6276ab3fb3ed507ec80c5ec77c17b65b5d43d1d94fadcf5fd3da50bf1161549a","43b575d1050fada93378d88228da48218f1e038d3c067be403d8fdfda51ad6f5",{"version":"9b70d22b933a2231a9d8e4c2afb1d2708ba17bf3115016b663b4b5b88f5ff37f","impliedFormat":99},"0891015ddec4afa39d510ba3c339bb4462d5f70cba592b03a8ca3b41a56bdb75","f74a70dd4d1eecc973834f1b22d07335b965950a0875642f0d2c142a8229b664","2b19d405298181dd350ace3935148e578a011899c6772ad1a6ac725a4ff20f8d","a0353c8928a3ea589827229593705223d8c55e0997ff1b8bbac267bb1bdeb38f","8ae855cb1b377e41ad38fbe287f7cd5f8fce93a88ebf91ebc29eba73645db05c","f726ebe837a84fb9ff01862ef78b67b52dbd77647ccd60391e462c325f65113e","cef84fbfcbfdffe4c64afc01d879ede79f272ecc65c466dd7a1f0dabf2514392","db8be55179ac1375af26b8c27de8712e54860ed41db53e911e7ea7e952083fc2","b91fcaa4192c933c9ef8cbf8443a0f98dfc7085dfae12ceb7b99e6bd9502d803","dee768bcbba9055096ed06b4fe508dc652c717cafd3ad2f7ab43d655c2b2e68c","ba348cfa1911b7c35175a28b8f7f77f797050a1acb1c6514b420db3d394bc1de",{"version":"380b919bfa0516118edaf25b99e45f855e7bc3fd75ce4163a1cfe4a666388804","impliedFormat":1},{"version":"0d89e5c4ce6e3096e64504e1fa45a8ddccf488cb5fdc1980ea09db2a451f0b91","impliedFormat":1},{"version":"fcf79300e5257a23ed3bacaa6861d7c645139c6f7ece134d15e6669447e5e6db","impliedFormat":1},{"version":"187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","impliedFormat":1},{"version":"aa2c18a1b5a086bbcaae10a4efba409cc95ba7287d8cf8f2591b53704fea3dea","impliedFormat":1},{"version":"5a0b15210129310cee9fa6af9200714bb4b12af4a04d890e15f34dbea1cf1852","impliedFormat":1},{"version":"0244119dbcbcf34faf3ffdae72dab1e9bc2bc9efc3c477b2240ffa94af3bca56","impliedFormat":1},{"version":"00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","impliedFormat":1},{"version":"a873c50d3e47c21aa09fbe1e2023d9a44efb07cc0cb8c72f418bf301b0771fd3","impliedFormat":1},{"version":"7c14ccd2eaa82619fffc1bfa877eb68a012e9fb723d07ee98db451fadb618906","impliedFormat":1},{"version":"49c36529ee09ea9ce19525af5bb84985ea8e782cb7ee8c493d9e36d027a3d019","impliedFormat":1},{"version":"df996e25faa505f85aeb294d15ebe61b399cf1d1e49959cdfaf2cc0815c203f9","impliedFormat":1},{"version":"4f6a12044ee6f458db11964153830abbc499e73d065c51c329ec97407f4b13dd","impliedFormat":1},{"version":"cf93e7b09b66e142429611c27ba2cbf330826057e3c793e1e2861e976fae3940","impliedFormat":99},{"version":"90e727d145feb03695693fdc9f165a4dc10684713ee5f6aa81e97a6086faa0f8","impliedFormat":99},{"version":"ee2c6ec73c636c9da5ab4ce9227e5197f55a57241d66ea5828f94b69a4a09a2d","impliedFormat":99},{"version":"afaf64477630c7297e3733765046c95640ab1c63f0dfb3c624691c8445bc3b08","impliedFormat":99},{"version":"5aa03223a53ad03171988820b81a6cae9647eabcebcb987d1284799de978d8e3","impliedFormat":99},{"version":"7f50c8914983009c2b940923d891e621db624ba32968a51db46e0bf480e4e1cb","impliedFormat":99},{"version":"90fc18234b7d2e19d18ac026361aaf2f49d27c98dc30d9f01e033a9c2b01c765","impliedFormat":99},{"version":"a980e4d46239f344eb4d5442b69dcf1d46bd2acac8d908574b5a507181f7e2a1","impliedFormat":99},{"version":"bbbfa4c51cdaa6e2ef7f7be3ae199b319de6b31e3b5afa7e5a2229c14bb2568a","impliedFormat":99},{"version":"bc7bfe8f48fa3067deb3b37d4b511588b01831ba123a785ea81320fe74dd9540","impliedFormat":99},{"version":"fd60c0aaf7c52115f0e7f367d794657ac18dbb257255777406829ab65ca85746","impliedFormat":99},{"version":"15c17866d58a19f4a01a125f3f511567bd1c22235b4fd77bf90c793bf28388c3","impliedFormat":99},{"version":"51301a76264b1e1b4046f803bda44307fba403183bc274fe9e7227252d7315cb","impliedFormat":99},{"version":"ddef23e8ace6c2b2ddf8d8092d30b1dd313743f7ff47b2cbb43f36c395896008","impliedFormat":99},{"version":"9e42df47111429042b5e22561849a512ad5871668097664b8fb06a11640140ac","impliedFormat":99},{"version":"391fcc749c6f94c6c4b7f017c6a6f63296c1c9ae03fa639f99337dddb9cc33fe","impliedFormat":99},{"version":"ac4706eb1fb167b19f336a93989763ab175cd7cc6227b0dcbfa6a7824c6ba59a","impliedFormat":99},{"version":"633220dc1e1a5d0ccf11d3c3e8cadc9124daf80fef468f2ff8186a2775229de3","impliedFormat":99},{"version":"6de22ad73e332e513454f0292275155d6cb77f2f695b73f0744928c4ebb3a128","impliedFormat":99},{"version":"ebe0e3c77f5114b656d857213698fade968cff1b3a681d1868f3cfdd09d63b75","impliedFormat":99},{"version":"22c27a87488a0625657b52b9750122814c2f5582cac971484cda0dcd7a46dc3b","impliedFormat":99},{"version":"7e7a817c8ec57035b2b74df8d5dbcc376a4a60ad870b27ec35463536158e1156","impliedFormat":99},{"version":"0e2061f86ca739f34feae42fd7cce27cc171788d251a587215b33eaec456e786","impliedFormat":99},{"version":"91659b2b090cadffdb593736210910508fc5b77046d4ce180b52580b14b075ec","impliedFormat":99},{"version":"d0f6c657c45faaf576ca1a1dc64484534a8dc74ada36fd57008edc1aab65a02b","impliedFormat":99},{"version":"ce0c52b1ebc023b71d3c1fe974804a2422cf1d85d4af74bb1bced36ff3bff8b5","impliedFormat":99},{"version":"9c6acb4a388887f9a5552eda68987ee5d607152163d72f123193a984c48157c9","impliedFormat":99},{"version":"90d0a9968cbb7048015736299f96a0cceb01cf583fd2e9a9edbc632ac4c81b01","impliedFormat":99},{"version":"49abec0571c941ab6f095885a76828d50498511c03bb326eec62a852e58000c5","impliedFormat":99},{"version":"8eeb4a4ff94460051173d561749539bca870422a6400108903af2fb7a1ffe3d7","impliedFormat":99},{"version":"49e39b284b87452fed1e27ac0748ba698f5a27debe05084bc5066b3ecf4ed762","impliedFormat":99},{"version":"59dcf835762f8df90fba5a3f8ba87941467604041cf127fb456543c793b71456","impliedFormat":99},{"version":"33e0c4c683dcaeb66bedf5bb6cc35798d00ac58d7f3bc82aadb50fa475781d60","impliedFormat":99},{"version":"605839abb6d150b0d83ed3712e1b3ffbeb309e382770e7754085d36bc2d84a4c","impliedFormat":99},{"version":"a862dcb740371257e3dae1ab379b0859edcb5119484f8359a5e6fb405db9e12e","impliedFormat":99},{"version":"0f0a16a0e8037c17e28f537028215e87db047eba52281bd33484d5395402f3c1","impliedFormat":99},{"version":"cf533aed4c455b526ddccbb10dae7cc77e9269c3d7862f9e5cedbd4f5c92e05e","impliedFormat":99},{"version":"f8a60ca31702a0209ef217f8f3b4b32f498813927df2304787ac968c78d8560d","impliedFormat":99},{"version":"530192961885d3ddad87bf9c4390e12689fa29ff515df57f17a57c9125fc77c3","impliedFormat":99},{"version":"165ba9e775dd769749e2177c383d24578e3b212e4774b0a72ad0f6faee103b68","impliedFormat":99},{"version":"61448f238fdfa94e5ccce1f43a7cced5e548b1ea2d957bec5259a6e719378381","impliedFormat":99},{"version":"69fa523e48131ced0a52ab1af36c3a922c5fd7a25e474d82117329fe051f5b85","impliedFormat":99},{"version":"fa10b79cd06f5dd03435e184fb05cc5f0d02713bfb4ee9d343db527501be334c","impliedFormat":99},{"version":"c6fb591e363ee4dea2b102bb721c0921485459df23a2d2171af8354cacef4bce","impliedFormat":99},{"version":"ea7e1f1097c2e61ed6e56fa04a9d7beae9d276d87ac6edb0cd39a3ee649cddfe","impliedFormat":99},{"version":"e8cf2659d87462aae9c7647e2a256ac7dcaf2a565a9681bfb49328a8a52861e8","impliedFormat":99},{"version":"7e374cb98b705d35369b3c15444ef2ff5ff983bd2fbb77a287f7e3240abf208c","impliedFormat":99},{"version":"ca75ba1519f9a426b8c512046ebbad58231d8627678d054008c93c51bc0f3fa5","impliedFormat":99},{"version":"ff63760147d7a60dcfc4ac16e40aa2696d016b9ffe27e296b43655dfa869d66b","impliedFormat":99},{"version":"4d434123b16f46b290982907a4d24675442eb651ca95a5e98e4c274be16f1220","impliedFormat":99},{"version":"57263d6ba38046e85f499f3c0ab518cfaf0a5f5d4f53bdae896d045209ab4aff","impliedFormat":99},{"version":"d3a535f2cd5d17f12b1abf0b19a64e816b90c8c10a030b58f308c0f7f2acfe2c","impliedFormat":99},{"version":"be26d49bb713c13bd737d00ae8a61aa394f0b76bc2d5a1c93c74f59402eb8db3","impliedFormat":99},{"version":"c7012003ac0c9e6c9d3a6418128ddebf6219d904095180d4502b19c42f46a186","impliedFormat":99},{"version":"d58c55750756bcf73f474344e6b4a9376e5381e4ba7d834dc352264b491423b6","impliedFormat":99},{"version":"01e2aabfabe22b4bf6d715fc54d72d32fa860a3bd1faa8974e0d672c4b565dfe","impliedFormat":99},{"version":"ba2c489bb2566c16d28f0500b3d98013917e471c40a4417c03991460cb248e88","impliedFormat":99},{"version":"39f94b619f0844c454a6f912e5d6868d0beb32752587b134c3c858b10ecd7056","impliedFormat":99},{"version":"0d2d8b0477b1cf16b34088e786e9745c3e8145bc8eea5919b700ad054e70a095","impliedFormat":99},{"version":"2a5e963b2b8f33a50bb516215ba54a20801cb379a8e9b1ae0b311e900dc7254c","impliedFormat":99},{"version":"d8307f62b55feeb5858529314761089746dce957d2b8fd919673a4985fa4342a","impliedFormat":99},{"version":"bf449ec80fc692b2703ad03e64ae007b3513ecd507dc2ab77f39be6f578e6f5c","impliedFormat":99},{"version":"f780213dd78998daf2511385dd51abf72905f709c839a9457b6ba2a55df57be7","impliedFormat":99},{"version":"2b7843e8a9a50bdf511de24350b6d429a3ee28430f5e8af7d3599b1e9aa7057f","impliedFormat":99},{"version":"05d95be6e25b4118c2eb28667e784f0b25882f6a8486147788df675c85391ab7","impliedFormat":99},{"version":"62d2721e9f2c9197c3e2e5cffeb2f76c6412121ae155153179049890011eb785","impliedFormat":99},{"version":"ff5668fb7594c02aca5e7ba7be6c238676226e450681ca96b457f4a84898b2d9","impliedFormat":99},{"version":"59fd37ea08657fef36c55ddea879eae550ffe21d7e3a1f8699314a85a30d8ae9","impliedFormat":99},{"version":"84e23663776e080e18b25052eb3459b1a0486b5b19f674d59b96347c0cb7312a","impliedFormat":99},{"version":"43e5934c7355731eec20c5a2aa7a859086f19f60a4e5fcd80e6684228f6fb767","impliedFormat":99},{"version":"a49c210c136c518a7c08325f6058fc648f59f911c41c93de2026db692bba0e47","impliedFormat":99},{"version":"1a92f93597ebc451e9ef4b158653c8d31902de5e6c8a574470ecb6da64932df4","impliedFormat":99},{"version":"256513ad066ac9898a70ca01e6fbdb3898a4e0fe408fbf70608fdc28ac1af224","impliedFormat":99},{"version":"d9835850b6cc05c21e8d85692a8071ebcf167a4382e5e39bf700c4a1e816437e","impliedFormat":99},{"version":"e5ab7190f818442e958d0322191c24c2447ddceae393c4e811e79cda6bd49836","impliedFormat":99},{"version":"91b4b77ef81466ce894f1aade7d35d3589ddd5c9981109d1dea11f55a4b807a0","impliedFormat":99},{"version":"03abb209bed94c8c893d9872639e3789f0282061c7aa6917888965e4047a8b5f","impliedFormat":99},{"version":"e97a07901de562219f5cba545b0945a1540d9663bd9abce66495721af3903eec","impliedFormat":99},{"version":"bf39ed1fdf29bc8178055ec4ff32be6725c1de9f29c252e31bdc71baf5c227e6","impliedFormat":99},{"version":"985eabf06dac7288fc355435b18641282f86107e48334a83605739a1fe82ac15","impliedFormat":99},{"version":"6112d33bcf51e3e6f6a81e419f29580e2f8e773529d53958c7c1c99728d4fb2e","impliedFormat":99},{"version":"89e9f7e87a573504acc2e7e5ad727a110b960330657d1b9a6d3526e77c83d8be","impliedFormat":99},{"version":"44bbb88abe9958c7c417e8687abf65820385191685009cc4b739c2d270cb02e9","impliedFormat":99},{"version":"ab4b506b53d2c4aec4cc00452740c540a0e6abe7778063e95c81a5cd557c19eb","impliedFormat":99},{"version":"858757bde6d615d0d1ee474c972131c6d79c37b0b61897da7fbd7110beb8af12","impliedFormat":99},{"version":"60b9dea33807b086a1b4b4b89f72d5da27ad0dd36d6436a6e306600c47438ac4","impliedFormat":99},{"version":"409c963b1166d0c1d49fdad1dfeb4de27fd2d6662d699009857de9baf43ca7c3","impliedFormat":99},{"version":"b7674ecfeb5753e965404f7b3d31eec8450857d1a23770cb867c82f264f546ab","impliedFormat":99},{"version":"c9800b9a9ad7fcdf74ed8972a5928b66f0e4ff674d55fd038a3b1c076911dcbe","impliedFormat":99},{"version":"99864433e35b24c61f8790d2224428e3b920624c01a6d26ea8b27ee1f62836bb","impliedFormat":99},{"version":"c391317b9ff8f87d28c6bfe4e50ed92e8f8bfab1bb8a03cd1fe104ff13186f83","impliedFormat":99},{"version":"42bdc3c98446fdd528e2591213f71ce6f7008fb9bb12413bd57df60d892a3fb5","impliedFormat":99},{"version":"542d2d689b58c25d39a76312ccaea2fcd10a45fb27b890e18015399c8032e2d9","impliedFormat":99},{"version":"97d1656f0a563dbb361d22b3d7c2487427b0998f347123abd1c69a4991326c96","impliedFormat":99},{"version":"d4f53ed7960c9fba8378af3fa28e3cc483d6c0b48e4a152a83ff0973d507307d","impliedFormat":99},{"version":"0665de5280d65ec32776dc55fb37128e259e60f389cde5b9803cf9e81ad23ce0","impliedFormat":99},{"version":"b6dc8fd1c6092da86725c338ca6c263d1c6dd3073046d3ec4eb2d68515062da2","impliedFormat":99},{"version":"d9198a0f01f00870653347560e10494efeca0bfa2de0988bd5d883a9d2c47edb","impliedFormat":99},{"version":"d4279865b926d7e2cfe8863b2eae270c4c035b6e923af8f9d7e6462d68679e07","impliedFormat":99},{"version":"73b6945448bb3425b764cfe7b1c4b0b56c010cc66e5f438ef320c53e469797eb","impliedFormat":99},{"version":"cf72fd8ffa5395f4f1a26be60246ec79c5a9ad201579c9ba63fd2607b5daf184","impliedFormat":99},{"version":"301a458744666096f84580a78cc3f6e8411f8bab92608cdaa33707546ca2906f","impliedFormat":99},{"version":"711e70c0916ff5f821ea208043ecd3e67ed09434b8a31d5616286802b58ebebe","impliedFormat":99},{"version":"e1f2fd9f88dd0e40c358fbf8c8f992211ab00a699e7d6823579b615b874a8453","impliedFormat":99},{"version":"17db3a9dcb2e1689ff7ace9c94fa110c88da64d69f01dc2f3cec698e4fc7e29e","impliedFormat":99},{"version":"73fb07305106bb18c2230890fcacf910fd1a7a77d93ac12ec40bc04c49ee5b8e","impliedFormat":99},{"version":"2c5f341625a45530b040d59a4bc2bc83824d258985ede10c67005be72d3e21d0","impliedFormat":99},{"version":"c4a262730d4277ecaaf6f6553dabecc84dcca8decaebbf2e16f1df8bbd996397","impliedFormat":99},{"version":"c23c533d85518f3358c55a7f19ab1a05aad290251e8bba0947bd19ea3c259467","impliedFormat":99},{"version":"5d0322a0b8cdc67b8c71e4ccaa30286b0c8453211d4c955a217ac2d3590e911f","impliedFormat":99},{"version":"f5e4032b6e4e116e7fec5b2620a2a35d0b6b8b4a1cc9b94a8e5ee76190153110","impliedFormat":99},{"version":"9ab26cb62a0e86ab7f669c311eb0c4d665457eb70a103508aa39da6ccee663da","impliedFormat":99},{"version":"5f64d1a11d8d4ce2c7ee3b72471df76b82d178a48964a14cdfdc7c5ef7276d70","impliedFormat":99},{"version":"24e2fbc48f65814e691d9377399807b9ec22cd54b51d631ba9e48ee18c5939dd","impliedFormat":99},{"version":"bfa2648b2ee90268c6b6f19e84da3176b4d46329c9ec0555d470e647d0568dfb","impliedFormat":99},{"version":"75ef3cb4e7b3583ba268a094c1bd16ce31023f2c3d1ac36e75ca65aca9721534","impliedFormat":99},{"version":"3be6b3304a81d0301838860fd3b4536c2b93390e785808a1f1a30e4135501514","impliedFormat":99},{"version":"da66c1b3e50ef9908e31ce7a281b137b2db41423c2b143c62524f97a536a53d9","impliedFormat":99},{"version":"3ada1b216e45bb9e32e30d8179a0a95870576fe949c33d9767823ccf4f4f4c97","impliedFormat":99},{"version":"1ace2885dffab849f7c98bffe3d1233260fbf07ee62cb58130167fd67a376a65","impliedFormat":99},{"version":"2126e5989c0ca5194d883cf9e9c10fe3e5224fbd3e4a4a6267677544e8be0aae","impliedFormat":99},{"version":"41a6738cf3c756af74753c5033e95c5b33dfc1f6e1287fa769a1ac4027335bf5","impliedFormat":99},{"version":"6e8630be5b0166cbc9f359b9f9e42801626d64ff1702dcb691af811149766154","impliedFormat":99},{"version":"e36b77c04e00b4a0bb4e1364f2646618a54910c27f6dc3fc558ca2ced8ca5bc5","impliedFormat":99},{"version":"2c4ea7e9f95a558f46c89726d1fedcb525ef649eb755a3d7d5055e22b80c2904","impliedFormat":99},{"version":"4875d65190e789fad05e73abd178297b386806b88b624328222d82e455c0f2e7","impliedFormat":99},{"version":"bf5302ecfaacee37c2316e33703723d62e66590093738c8921773ee30f2ecc38","impliedFormat":99},{"version":"62684064fe034d54b87f62ad416f41b98a405dee4146d0ec03b198c3634ea93c","impliedFormat":99},{"version":"be02cbdb1688c8387f8a76a9c6ed9d75d8bb794ec5b9b1d2ba3339a952a00614","impliedFormat":99},{"version":"cefaff060473a5dbf4939ee1b52eb900f215f8d6249dc7c058d6b869d599983c","impliedFormat":99},{"version":"b2797235a4c1a7442a6f326f28ffb966226c3419399dbb33634b8159af2c712f","impliedFormat":99},{"version":"164d633bbd4329794d329219fc173c3de85d5ad866d44e5b5f0fb60c140e98f2","impliedFormat":99},{"version":"b74300dd0a52eaf564b3757c07d07e1d92def4e3b8708f12eedb40033e4cafe9","impliedFormat":99},{"version":"a792f80b1e265b06dce1783992dbee2b45815a7bdc030782464b8cf982337cf2","impliedFormat":99},{"version":"8816b4b3a87d9b77f0355e616b38ed5054f993cc4c141101297f1914976a94b1","impliedFormat":99},{"version":"0f35e4da974793534c4ca1cdd9491eab6993f8cf47103dadfc048b899ed9b511","impliedFormat":99},{"version":"0ccdfcaebf297ec7b9dde20bbbc8539d5951a3d8aaa40665ca469da27f5a86e1","impliedFormat":99},{"version":"7fcb05c8ce81f05499c7b0488ae02a0a1ac6aebc78c01e9f8c42d98f7ba68140","impliedFormat":99},{"version":"81c376c9e4d227a4629c7fca9dde3bbdfa44bd5bd281aee0ed03801182368dc5","impliedFormat":99},{"version":"0f2448f95110c3714797e4c043bbc539368e9c4c33586d03ecda166aa9908843","impliedFormat":99},{"version":"b2f1a443f7f3982d7325775906b51665fe875c82a62be3528a36184852faa0bb","impliedFormat":99},{"version":"7568ff1f23363d7ee349105eb936e156d61aea8864187a4c5d85c60594b44a25","impliedFormat":99},{"version":"8c4d1d9a4eba4eac69e6da0f599a424b2689aee55a455f0b5a7f27a807e064db","impliedFormat":99},{"version":"e1beb9077c100bdd0fc8e727615f5dae2c6e1207de224569421907072f4ec885","impliedFormat":99},{"version":"3dda13836320ec71b95a68cd3d91a27118b34c05a2bfda3e7e51f1d8ca9b960b","impliedFormat":99},{"version":"fedc79cb91f2b3a14e832d7a8e3d58eb02b5d5411c843fcbdc79e35041316b36","impliedFormat":99},{"version":"99f395322ffae908dcdfbaa2624cc7a2a2cb7b0fbf1a1274aca506f7b57ebcb5","impliedFormat":99},{"version":"5e1f7c43e8d45f2222a5c61cbc88b074f4aaf1ca4b118ac6d6123c858efdcd71","impliedFormat":99},{"version":"7388273ab71cb8f22b3f25ffd8d44a37d5740077c4d87023da25575204d57872","impliedFormat":99},{"version":"0a48ceb01a0fdfc506aa20dfd8a3563edbdeaa53a8333ddf261d2ee87669ea7b","impliedFormat":99},{"version":"3182d06b874f31e8e55f91ea706c85d5f207f16273480f46438781d0bd2a46a1","impliedFormat":99},{"version":"ccd47cab635e8f71693fa4e2bbb7969f559972dae97bd5dbd1bbfee77a63b410","impliedFormat":99},{"version":"89770fa14c037f3dc3882e6c56be1c01bb495c81dec96fa29f868185d9555a5d","impliedFormat":99},{"version":"7048c397f08c54099c52e6b9d90623dc9dc6811ea142f8af3200e40d66a972e1","impliedFormat":99},{"version":"512120cd6f026ce1d3cf686c6ab5da80caa40ef92aa47466ec60ba61a48b5551","impliedFormat":99},{"version":"6cd0cb7f999f221e984157a7640e7871960131f6b221d67e4fdc2a53937c6770","impliedFormat":99},{"version":"f48b84a0884776f1bc5bf0fcf3f69832e97b97dc55d79d7557f344de900d259b","impliedFormat":99},{"version":"dca490d986411644b0f9edf6ea701016836558e8677c150dca8ad315178ec735","impliedFormat":99},{"version":"a028a04948cf98c1233166b48887dad324e8fe424a4be368a287c706d9ccd491","impliedFormat":99},{"version":"3046ed22c701f24272534b293c10cfd17b0f6a89c2ec6014c9a44a90963dfa06","impliedFormat":99},{"version":"394da10397d272f19a324c95bea7492faadf2263da157831e02ae1107bd410f5","impliedFormat":99},{"version":"0580595a99248b2d30d03f2307c50f14eb21716a55beb84dd09d240b1b087a42","impliedFormat":99},{"version":"a7da9510150f36a9bea61513b107b59a423fdff54429ad38547c7475cd390e95","impliedFormat":99},{"version":"659615f96e64361af7127645bb91f287f7b46c5d03bea7371e6e02099226d818","impliedFormat":99},{"version":"1f2a42974920476ce46bb666cd9b3c1b82b2072b66ccd0d775aa960532d78176","impliedFormat":99},{"version":"500b3ae6095cbab92d81de0b40c9129f5524d10ad955643f81fc07d726c5a667","impliedFormat":99},{"version":"a957ad4bd562be0662fb99599dbcf0e16d1631f857e5e1a83a3f3afb6c226059","impliedFormat":99},{"version":"e57a4915266a6a751c6c172e8f30f6df44a495608613e1f1c410196207da9641","impliedFormat":99},{"version":"7a12e57143b7bc5a52a41a8c4e6283a8f8d59a5e302478185fb623a7157fff5e","impliedFormat":99},{"version":"17b3426162e1d9cb0a843e8d04212aabe461d53548e671236de957ed3ae9471b","impliedFormat":99},{"version":"f38e86eb00398d63180210c5090ef6ed065004474361146573f98b3c8a96477d","impliedFormat":99},{"version":"231d9e32382d3971f58325e5a85ba283a2021243651cb650f82f87a1bf62d649","impliedFormat":99},{"version":"6532e3e87b87c95f0771611afce929b5bad9d2c94855b19b29b3246937c9840b","impliedFormat":99},{"version":"65704bbb8f0b55c73871335edd3c9cead7c9f0d4b21f64f5d22d0987c45687f0","impliedFormat":99},{"version":"787232f574af2253ac860f22a445c755d57c73a69a402823ae81ba0dfdd1ce23","impliedFormat":99},{"version":"5e63903cd5ebce02486b91647d951d61a16ad80d65f9c56581cd624f39a66007","impliedFormat":99},{"version":"bcc89a120d8f3c02411f4df6b1d989143c01369314e9b0e04794441e6b078d22","impliedFormat":99},{"version":"d17531ef42b7c76d953f63bd5c5cd927c4723e62a7e0b2badf812d5f35f784eb","impliedFormat":99},{"version":"6d4ee1a8e3a97168ea4c4cc1c68bb61a3fd77134f15c71bb9f3f63df3d26b54c","impliedFormat":99},{"version":"1eb04fea6b47b16922ed79625d90431a8b2fc7ba9d5768b255e62df0c96f1e3a","impliedFormat":99},{"version":"de0c2eece83bd81b8682f4496f558beb728263e17e74cbc4910e5c9ce7bef689","impliedFormat":99},{"version":"98866542d45306dab48ecc3ddd98ee54fa983353bc3139dfbc619df882f54d90","impliedFormat":99},{"version":"9e04c7708917af428c165f1e38536ddb2e8ecd576f55ed11a97442dc34b6b010","impliedFormat":99},{"version":"31fe6f6d02b53c1a7c34b8d8f8c87ee9b6dd4b67f158cbfff3034b4f3f69c409","impliedFormat":99},{"version":"2e1d853f84188e8e002361f4bfdd892ac31c68acaeac426a63cd4ff7abf150d0","impliedFormat":99},{"version":"666b5289ec8a01c4cc0977c62e3fd32e89a8e3fd9e97c8d8fd646f632e63c055","impliedFormat":99},{"version":"a1107bbb2b10982dba1f7958a6a5cf841e1a19d6976d0ecdc4c43269c7b0eaf2","impliedFormat":99},{"version":"07fa6122f7495331f39167ec9e4ebd990146a20f99c16c17bc0a98aa81f63b27","impliedFormat":99},{"version":"39c1483481b35c2123eaab5094a8b548a0c3f1e483ab7338102c3291f1ab18bf","impliedFormat":99},{"version":"b73e6242c13796e7d5fba225bf1c07c8ee66d31b7bb65f45be14226a9ae492d2","impliedFormat":99},{"version":"f2931608d541145d189390d6cfb74e1b1e88f73c0b9a80c4356a4daa7fa5e005","impliedFormat":99},{"version":"8684656fe3bf1425a91bd62b8b455a1c7ec18b074fd695793cfae44ae02e381a","impliedFormat":99},{"version":"ccf0b9057dd65c7fb5e237de34f706966ebc30c6d3669715ed05e76225f54fbd","impliedFormat":99},{"version":"d930f077da575e8ea761e3d644d4c6279e2d847bae2b3ea893bbd572315acc21","impliedFormat":99},{"version":"19b0616946cb615abde72c6d69049f136cc4821b784634771c1d73bec8005f73","impliedFormat":99},{"version":"553312560ad0ef97b344b653931935d6e80840c2de6ab90b8be43cbacf0d04cf","impliedFormat":99},{"version":"1225cf1910667bfd52b4daa9974197c3485f21fe631c3ce9db3b733334199faa","impliedFormat":99},{"version":"f7cb9e46bd6ab9d620d68257b525dbbbbc9b0b148adf500b819d756ebc339de0","impliedFormat":99},{"version":"e46d6c3120aca07ae8ec3189edf518c667d027478810ca67a62431a0fa545434","impliedFormat":99},{"version":"9d234b7d2f662a135d430d3190fc21074325f296273125244b2bf8328b5839a0","impliedFormat":99},{"version":"0554ef14d10acea403348c53436b1dd8d61e7c73ef5872e2fe69cc1c433b02f8","impliedFormat":99},{"version":"2f6ae5538090db60514336bd1441ca208a8fab13108cfa4b311e61eaca5ff716","impliedFormat":99},{"version":"17bf4ce505a4cff88fb56177a8f7eb48aa55c22ccc4cce3e49cc5c8ddc54b07d","impliedFormat":99},{"version":"3d735f493d7da48156b79b4d8a406bf2bbf7e3fe379210d8f7c085028143ee40","impliedFormat":99},{"version":"41de1b3ddd71bd0d9ed7ac217ca1b15b177dd731d5251cde094945c20a715d03","impliedFormat":99},{"version":"17d9c562a46c6a25bc2f317c9b06dd4e8e0368cbe9bdf89be6117aeafd577b36","impliedFormat":99},{"version":"ded799031fe18a0bb5e78be38a6ae168458ff41b6c6542392b009d2abe6a6f32","impliedFormat":99},{"version":"ed48d467a7b25ee1a2769adebc198b647a820e242c96a5f96c1e6c27a40ab131","impliedFormat":99},{"version":"b914114df05f286897a1ae85d2df39cfd98ed8da68754d73cf830159e85ddd15","impliedFormat":99},{"version":"73881e647da3c226f21e0b80e216feaf14a5541a861494c744e9fbe1c3b3a6af","impliedFormat":99},{"version":"d79e1d31b939fa99694f2d6fbdd19870147401dbb3f42214e84c011e7ec359ab","impliedFormat":99},{"version":"4f71097eae7aa37941bab39beb2e53e624321fd341c12cc1d400eb7a805691ff","impliedFormat":99},{"version":"58ebb4f21f3a90dda31a01764462aa617849fdb1b592f3a8d875c85019956aff","impliedFormat":99},{"version":"a8e8d0e6efff70f3c28d3e384f9d64530c7a7596a201e4879a7fd75c7d55cbb5","impliedFormat":99},{"version":"df5cbb80d8353bf0511a4047cc7b8434b0be12e280b6cf3de919d5a3380912c0","impliedFormat":99},{"version":"256eb0520e822b56f720962edd7807ed36abdf7ea23bcadf4a25929a3317c8cf","impliedFormat":99},{"version":"9cf2cbc9ceb5f718c1705f37ce5454f14d3b89f690d9864394963567673c1b5c","impliedFormat":99},{"version":"07d3dd790cf1e66bb6fc9806d014dd40bb2055f8d6ca3811cf0e12f92ba4cb9a","impliedFormat":99},{"version":"1f99fd62e9cff9b50c36f368caf3b9fb79fc6f6c75ca5d3c2ec4afaea08d9109","impliedFormat":99},{"version":"6558faaacba5622ef7f1fdfb843cd967af2c105469b9ff5c18a81ce85178fca7","impliedFormat":99},{"version":"34e7f17ae9395b0269cd3f2f0af10709e6dc975c5b44a36b6b70442dc5e25a38","impliedFormat":99},{"version":"a4295111b54f84c02c27e46b0855b02fad3421ae1d2d7e67ecf16cb49538280a","impliedFormat":99},{"version":"ce9746b2ceae2388b7be9fe1f009dcecbc65f0bdbc16f40c0027fab0fb848c3b","impliedFormat":99},{"version":"35ce823a59f397f0e85295387778f51467cea137d787df385be57a2099752bfb","impliedFormat":99},{"version":"2e5acd3ec67bc309e4f679a70c894f809863c33b9572a8da0b78db403edfa106","impliedFormat":99},{"version":"1872f3fcea0643d5e03b19a19d777704320f857d1be0eb4ee372681357e20c88","impliedFormat":99},{"version":"9689628941205e40dcbb2706d1833bd00ce7510d333b2ef08be24ecbf3eb1a37","impliedFormat":99},{"version":"0317a72a0b63094781476cf1d2d27585d00eb2b0ca62b5287124735912f3d048","impliedFormat":99},{"version":"6ce4c0ab3450a4fff25d60a058a25039cffd03141549589689f5a17055ad0545","impliedFormat":99},{"version":"9153ec7b0577ae77349d2c5e8c5dd57163f41853b80c4fb5ce342c7a431cbe1e","impliedFormat":99},{"version":"f490dfa4619e48edd594a36079950c9fca1230efb3a82aaf325047262ba07379","impliedFormat":99},{"version":"674f00085caff46d2cbc76fc74740fd31f49d53396804558573421e138be0c12","impliedFormat":99},{"version":"41d029194c4811f09b350a1e858143c191073007a9ee836061090ed0143ad94f","impliedFormat":99},{"version":"44a6259ffd6febd8510b9a9b13a700e1d022530d8b33663f0735dbb3bee67b3d","impliedFormat":99},{"version":"6f4322500aff8676d9b8eef7711c7166708d4a0686b792aa4b158e276ed946a7","impliedFormat":99},{"version":"e829ff9ecffa3510d3a4d2c3e4e9b54d4a4ccfef004bacbb1d6919ce3ccca01f","impliedFormat":99},{"version":"62e6fec9dbd012460b47af7e727ec4cd34345b6e4311e781f040e6b640d7f93e","impliedFormat":99},{"version":"4d180dd4d0785f2cd140bc069d56285d0121d95b53e4348feb4f62db2d7035d3","impliedFormat":99},{"version":"f1142cbba31d7f492d2e7c91d82211a8334e6642efe52b71d9a82cb95ba4e8ae","impliedFormat":99},{"version":"279cac827be5d48c0f69fe319dc38c876fdd076b66995d9779c43558552d8a50","impliedFormat":99},{"version":"a70ff3c65dc0e7213bfe0d81c072951db9f5b1e640eb66c1eaed0737879c797b","impliedFormat":99},{"version":"f75d3303c1750f4fdacd23354657eca09aae16122c344e65b8c14c570ff67df5","impliedFormat":99},{"version":"3ebae6a418229d4b303f8e0fdb14de83f39fba9f57b39d5f213398bca72137c7","impliedFormat":99},{"version":"21ba07e33265f59d52dece5ac44f933b2b464059514587e64ad5182ddf34a9b0","impliedFormat":99},{"version":"2d3d96efba00493059c460fd55e6206b0667fc2e73215c4f1a9eb559b550021f","impliedFormat":99},{"version":"d23d4a57fff5cec5607521ba3b72f372e3d735d0f6b11a4681655b0bdd0505f4","impliedFormat":99},{"version":"395c1f3da7e9c87097c8095acbb361541480bf5fd7fa92523985019fef7761dd","impliedFormat":99},{"version":"d61f3d719293c2f92a04ba73d08536940805938ecab89ac35ceabc8a48ccb648","impliedFormat":99},{"version":"ca693235a1242bcd97254f43a17592aa84af66ccb7497333ccfea54842fde648","impliedFormat":99},{"version":"cd41cf040b2e368382f2382ec9145824777233730e3965e9a7ba4523a6a4698e","impliedFormat":99},{"version":"2e7a9dba6512b0310c037a28d27330520904cf5063ca19f034b74ad280dbfe71","impliedFormat":99},{"version":"9f2a38baf702e6cb98e0392fa39d25a64c41457a827b935b366c5e0980a6a667","impliedFormat":99},{"version":"c1dc37f0e7252928f73d03b0d6b46feb26dea3d8737a531ca4c0ec4105e33120","impliedFormat":99},{"version":"25126b80243fb499517e94fc5afe5c9c5df3a0105618e33581fb5b2f2622f342","impliedFormat":99},{"version":"d332c2ddcb64012290eb14753c1b49fe3eee9ca067204efba1cf31c1ce1ee020","impliedFormat":99},{"version":"1be8da453470021f6fe936ba19ee0bfebc7cfa2406953fa56e78940467c90769","impliedFormat":99},{"version":"7c9f2d62d83f1292a183a44fb7fb1f16eb9037deb05691d307d4017ac8af850a","impliedFormat":99},{"version":"d0163ab7b0de6e23b8562af8b5b4adea4182884ca7543488f7ac2a3478f3ae6e","impliedFormat":99},{"version":"05224e15c6e51c4c6cd08c65f0766723f6b39165534b67546076c226661db691","impliedFormat":99},{"version":"a5f7158823c7700dd9fc1843a94b9edc309180c969fbfa6d591aeb0b33d3b514","impliedFormat":99},{"version":"7d30937f8cf9bb0d4b2c2a8fb56a415d7ef393f6252b24e4863f3d7b84285724","impliedFormat":99},{"version":"e04d074584483dc9c59341f9f36c7220f16eed09f7af1fa3ef9c64c26095faec","impliedFormat":99},{"version":"619697e06cbc2c77edda949a83a62047e777efacde1433e895b904fe4877c650","impliedFormat":99},{"version":"88d9a8593d2e6aee67f7b15a25bda62652c77be72b79afbee52bea61d5ffb39e","impliedFormat":99},{"version":"044d7acfc9bd1af21951e32252cf8f3a11c8b35a704169115ddcbde9fd717de2","impliedFormat":99},{"version":"a4ca8f13a91bd80e6d7a4f013b8a9e156fbf579bbec981fe724dad38719cfe01","impliedFormat":99},{"version":"5a216426a68418e37e55c7a4366bc50efc99bda9dc361eae94d7e336da96c027","impliedFormat":99},{"version":"13b65b640306755096d304e76d4a237d21103de88b474634f7ae13a2fac722d5","impliedFormat":99},{"version":"7478bd43e449d3ce4e94f3ed1105c65007b21f078b3a791ea5d2c47b30ea6962","impliedFormat":99},{"version":"601d3e8e71b7d6a24fc003aca9989a6c25fa2b3755df196fd0aaee709d190303","impliedFormat":99},{"version":"168e0850fcc94011e4477e31eca81a8a8a71e1aed66d056b7b50196b877e86c8","impliedFormat":99},{"version":"37ba82d63f5f8c6b4fc9b756f24902e47f62ea66aae07e89ace445a54190a86e","impliedFormat":99},{"version":"f5b66b855f0496bc05f1cd9ba51a6a9de3d989b24aa36f6017257f01c8b65a9f","impliedFormat":99},{"version":"823b16d378e8456fcc5503d6253c8b13659be44435151c6b9f140c4a38ec98c1","impliedFormat":99},{"version":"b58b254bf1b586222844c04b3cdec396e16c811463bf187615bb0a1584beb100","impliedFormat":99},{"version":"a367c2ccfb2460e222c5d10d304e980bd172dd668bcc02f6c2ff626e71e90d75","impliedFormat":99},{"version":"0718623262ac94b016cb0cfd8d54e4d5b7b1d3941c01d85cf95c25ec1ba5ed8d","impliedFormat":99},{"version":"d4f3c9a0bd129e9c7cbfac02b6647e34718a2b81a414d914e8bd6b76341172e0","impliedFormat":99},{"version":"824306df6196f1e0222ff775c8023d399091ada2f10f2995ce53f5e3d4aff7a4","impliedFormat":99},{"version":"84ca07a8d57f1a6ba8c0cf264180d681f7afae995631c6ca9f2b85ec6ee06c0f","impliedFormat":99},{"version":"35755e61e9f4ec82d059efdbe3d1abcccc97a8a839f1dbf2e73ac1965f266847","impliedFormat":99},{"version":"64a918a5aa97a37400ec085ffeea12a14211aa799cd34e5dc828beb1806e95bb","impliedFormat":99},{"version":"0c8f5489ba6af02a4b1d5ba280e7badd58f30dc8eb716113b679e9d7c31185e5","impliedFormat":99},{"version":"7b574ca9ae0417203cdfa621ab1585de5b90c4bc6eea77a465b2eb8b92aa5380","impliedFormat":99},{"version":"3334c03c15102700973e3e334954ac1dffb7be7704c67cc272822d5895215c93","impliedFormat":99},{"version":"aabcb169451df7f78eb43567fab877a74d134a0a6d9850aa58b38321374ab7c0","impliedFormat":99},{"version":"1b5effdd8b4e8d9897fc34ab4cd708a446bf79db4cb9a3467e4a30d55b502e14","impliedFormat":99},{"version":"d772776a7aea246fd72c5818de72c3654f556b2cf0d73b90930c9c187cc055fc","impliedFormat":99},{"version":"dbd4bd62f433f14a419e4c6130075199eb15f2812d2d8e7c9e1f297f4daac788","impliedFormat":99},{"version":"427df949f5f10c73bcc77b2999893bc66c17579ad073ee5f5270a2b30651c873","impliedFormat":99},{"version":"c4c1a5565b9b85abfa1d663ca386d959d55361e801e8d49155a14dd6ca41abe1","impliedFormat":99},{"version":"7a45a45c277686aaff716db75a8157d0458a0d854bacf072c47fee3d499d7a99","impliedFormat":99},{"version":"57005b72bce2dc26293e8924f9c6be7ee3a2c1b71028a680f329762fa4439354","impliedFormat":99},{"version":"8f53b1f97c53c3573c16d0225ee3187d22f14f01421e3c6da1a26a1aace32356","impliedFormat":99},{"version":"810fdc0e554ed7315c723b91f6fa6ef3a6859b943b4cd82879641563b0e6c390","impliedFormat":99},{"version":"87a36b177b04d23214aa4502a0011cd65079e208cd60654aefc47d0d65da68ea","impliedFormat":99},{"version":"28a1c17fcbb9e66d7193caca68bbd12115518f186d90fc729a71869f96e2c07b","impliedFormat":99},{"version":"cc2d2abbb1cc7d6453c6fee760b04a516aa425187d65e296a8aacff66a49598a","impliedFormat":99},{"version":"d2413645bc4ab9c3f3688c5281232e6538684e84b49a57d8a1a8b2e5cf9f2041","impliedFormat":99},{"version":"4e6e21a0f9718282d342e66c83b2cd9aa7cd777dfcf2abd93552da694103b3dc","impliedFormat":99},{"version":"9006cc15c3a35e49508598a51664aa34ae59fc7ab32d6cc6ea2ec68d1c39448e","impliedFormat":99},{"version":"74467b184eadee6186a17cac579938d62eceb6d89c923ae67d058e2bcded254e","impliedFormat":99},{"version":"4169b96bb6309a2619f16d17307da341758da2917ff40c615568217b14357f5e","impliedFormat":99},{"version":"4a94d6146b38050de0830019a1c6a7820c2e2b90eba1a5ee4e4ab3bc30a72036","impliedFormat":99},{"version":"48a35ece156203abf19864daa984475055bbed4dc9049d07f4462100363f1e85","impliedFormat":99},"adfada2825506f38cd229c77d632498d83a9073e8551b58b6be1da33d174d99c","9887686cf51666e50c2d36bdb7ad2c6ab7654a1ac310ce1f013e74cea5224c4e","854e1005a4f49ba91d961dbfe6e869b5e36fa3c669e4e98258446b3090c11300","e22e2bb05c1e6ed17087d04572352fef92f5d7606fdacfc4ee429c4fb35b8a25","f1bc7f8967ccf4049f2a74559fb26f9b4d047f8eab1dcd0d50d790cd01c4e225","2d8b6d6d8c3813ba4eb1729e189e88f95d02d5f753a82a782f5482b9f6a54e42","98d935eebcc2ad814090300c2cdb80abaacb0b15d15235ab695c4228af14b96f",{"version":"08816fd68670b0818422d25ffcba5e8a3843e081d69cc9de60fcb91c19f873e0","impliedFormat":99},{"version":"463c7557566cc442687e3eb01902a3b96370f897eac5b7631aaf6c0ffa539aac","impliedFormat":99},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"438b41419b1df9f1fbe33b5e1b18f5853432be205991d1b19f5b7f351675541e","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"487b694c3de27ddf4ad107d4007ad304d29effccf9800c8ae23c2093638d906a","impliedFormat":1},{"version":"3a80bc85f38526ca3b08007ee80712e7bb0601df178b23fbf0bf87036fce40ce","impliedFormat":1},{"version":"ccf4552357ce3c159ef75f0f0114e80401702228f1898bdc9402214c9499e8c0","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"2931540c47ee0ff8a62860e61782eb17b155615db61e36986e54645ec67f67c2","impliedFormat":1},{"version":"ccab02f3920fc75c01174c47fcf67882a11daf16baf9e81701d0a94636e94556","impliedFormat":1},{"version":"f6faf5f74e4c4cc309a6c6a6c4da02dbb840be5d3e92905a23dcd7b2b0bd1986","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"33e981bf6376e939f99bd7f89abec757c64897d33c005036b9a10d9587d80187","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"b41767d372275c154c7ea6c9d5449d9a741b8ce080f640155cc88ba1763e35b3","impliedFormat":1},{"version":"3bacf516d686d08682751a3bd2519ea3b8041a164bfb4f1d35728993e70a2426","impliedFormat":1},{"version":"7fb266686238369442bd1719bc0d7edd0199da4fb8540354e1ff7f16669b4323","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"54c3e2371e3d016469ad959697fd257e5621e16296fa67082c2575d0bf8eced0","impliedFormat":1},{"version":"beb8233b2c220cfa0feea31fbe9218d89fa02faa81ef744be8dce5acb89bb1fd","impliedFormat":1},{"version":"c183b931b68ad184bc8e8372bf663f3d33304772fb482f29fb91b3c391031f3e","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"48cc3ec153b50985fb95153258a710782b25975b10dd4ac8a4f3920632d10790","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"e1528ca65ac90f6fa0e4a247eb656b4263c470bb22d9033e466463e13395e599","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"866078923a56d026e39243b4392e282c1c63159723996fa89243140e1388a98d","impliedFormat":1},{"version":"c3f5289820990ab66b70c7fb5b63cb674001009ff84b13de40619619a9c8175f","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"435b3711465425770ed2ee2f1cf00ce071835265e0851a7dc4600ab4b007550e","impliedFormat":1},{"version":"7e49f52a159435fc8df4de9dc377ef5860732ca2dc9efec1640531d3cf5da7a3","impliedFormat":1},{"version":"dd4bde4bdc2e5394aed6855e98cf135dfdf5dd6468cad842e03116d31bbcc9bc","impliedFormat":1},{"version":"4d4e879009a84a47c05350b8dca823036ba3a29a3038efed1be76c9f81e45edf","affectsGlobalScope":true,"impliedFormat":1},{"version":"237ba5ac2a95702a114a309e39c53a5bddff5f6333b325db9764df9b34f3502b","impliedFormat":1},{"version":"9ba13b47cb450a438e3076c4a3f6afb9dc85e17eae50f26d4b2d72c0688c9251","impliedFormat":1},{"version":"b64cd4401633ea4ecadfd700ddc8323a13b63b106ac7127c1d2726f32424622c","impliedFormat":1},{"version":"37c6e5fe5715814412b43cc9b50b24c67a63c4e04e753e0d1305970d65417a60","impliedFormat":1},{"version":"1d024184fb57c58c5c91823f9d10b4915a4867b7934e89115fd0d861a9df27c8","impliedFormat":1},{"version":"ee0e4946247f842c6dd483cbb60a5e6b484fee07996e3a7bc7343dfb68a04c5d","impliedFormat":1},{"version":"ef051f42b7e0ef5ca04552f54c4552eac84099d64b6c5ad0ef4033574b6035b8","impliedFormat":1},{"version":"853a43154f1d01b0173d9cbd74063507ece57170bad7a3b68f3fa1229ad0a92f","impliedFormat":1},{"version":"56231e3c39a031bfb0afb797690b20ed4537670c93c0318b72d5180833d98b72","impliedFormat":1},{"version":"5cc7c39031bfd8b00ad58f32143d59eb6ffc24f5d41a20931269011dccd36c5e","impliedFormat":1},{"version":"b0b69c61b0f0ec8ca15db4c8c41f6e77f4cacb784d42bca948f42dea33e8757e","affectsGlobalScope":true,"impliedFormat":1},{"version":"f96a48183254c00d24575401f1a761b4ce4927d927407e7862a83e06ce5d6964","impliedFormat":1},{"version":"cc25940cfb27aa538e60d465f98bb5068d4d7d33131861ace43f04fe6947d68f","impliedFormat":1},{"version":"f83fb2b1338afbb3f9d733c7d6e8b135826c41b0518867df0c0ace18ae1aa270","impliedFormat":1},{"version":"01ff95aa1443e3f7248974e5a771f513cb2ac158c8898f470a1792f817bee497","impliedFormat":1},{"version":"757227c8b345c57d76f7f0e3bbad7a91ffca23f1b2547cbed9e10025816c9cb7","impliedFormat":1},{"version":"42a05d8f239f74587d4926aba8cc54792eed8e8a442c7adc9b38b516642aadfe","impliedFormat":1},{"version":"5d21b58d60383cc6ab9ad3d3e265d7d25af24a2c9b506247e0e50b0a884920be","impliedFormat":1},{"version":"101f482fd48cb4c7c0468dcc6d62c843d842977aea6235644b1edd05e81fbf22","impliedFormat":1},{"version":"ae6757460f37078884b1571a3de3ebaf724d827d7e1d53626c02b3c2a408ac63","affectsGlobalScope":true,"impliedFormat":1},{"version":"9451a46a89ed209e2e08329e6cac59f89356eae79a7230f916d8cc38725407c7","impliedFormat":1},{"version":"3ef397f12387eff17f550bc484ea7c27d21d43816bbe609d495107f44b97e933","impliedFormat":1},{"version":"1023282e2ba810bc07905d3668349fbd37a26411f0c8f94a70ef3c05fe523fcf","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"e236b5eba291f51bdf32c231673e6cab81b5410850e61f51a7a524dddadc0f95","impliedFormat":1},{"version":"f7ba0e839daa0702e3ff1a1a871c0d8ea2d586ce684dd8a72c786c36a680b1d9","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f2c62938251b45715fd2a9887060ec4fbc8724727029d1cbce373747252bdd7","impliedFormat":1},{"version":"e3ace08b6bbd84655d41e244677b474fd995923ffef7149ddb68af8848b60b05","impliedFormat":1},{"version":"132580b0e86c48fab152bab850fc57a4b74fe915c8958d2ccb052b809a44b61c","impliedFormat":1},{"version":"af4ab0aa8908fc9a655bb833d3bc28e117c4f0e1038c5a891546158beb25accb","impliedFormat":1},{"version":"69c9a5a9392e8564bd81116e1ed93b13205201fb44cb35a7fde8c9f9e21c4b23","impliedFormat":1},{"version":"5f8fc37f8434691ffac1bfd8fc2634647da2c0e84253ab5d2dd19a7718915b35","impliedFormat":1},{"version":"5981c2340fd8b076cae8efbae818d42c11ffc615994cb060b1cd390795f1be2b","impliedFormat":1},{"version":"f64deb26664af64dc274637343bde8d82f930c77af05a412c7d310b77207a448","impliedFormat":1},{"version":"ed4f674fc8c0c993cc7e145069ac44129e03519b910c62be206a0cc777bdc60b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0250da3eb85c99624f974e77ef355cdf86f43980251bc371475c2b397ba55bcd","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"3d3a5f27ffbc06c885dd4d5f9ee20de61faf877fe2c3a7051c4825903d9a7fdc","impliedFormat":1},{"version":"12806f9f085598ef930edaf2467a5fa1789a878fba077cd27e85dc5851e11834","impliedFormat":1},{"version":"bce309f4d9b67c18d4eeff5bba6cf3e67b2b0aead9f03f75d6060c553974d7ba","impliedFormat":1},{"version":"a43fe41c33d0a192a0ecaf9b92e87bef3709c9972e6d53c42c49251ccb962d69","impliedFormat":1},{"version":"a177959203c017fad3ecc4f3d96c8757a840957a4959a3ae00dab9d35961ca6c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6fc727ccf9b36e257ff982ea0badeffbfc2c151802f741bddff00c6af3b784cf","impliedFormat":1},{"version":"2a00d005e3af99cd1cfa75220e60c61b04bfb6be7ca7453bfe2ef6cca37cc03c","impliedFormat":1},{"version":"4844a4c9b4b1e812b257676ed8a80b3f3be0e29bf05e742cc2ea9c3c6865e6c6","impliedFormat":1},{"version":"064878a60367e0407c42fb7ba02a2ea4d83257357dc20088e549bd4d89433e9c","impliedFormat":1},{"version":"14d4bd22d1b05824971b98f7e91b2484c90f1a684805c330476641417c3d9735","impliedFormat":1},{"version":"c3877fef8a43cd434f9728f25a97575b0eb73d92f38b5c87c840daccc3e21d97","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"1dbd83860e7634f9c236647f45dbc5d3c4f9eba8827d87209d6e9826fdf4dbd5","impliedFormat":1},{"version":"41ef7992c555671a8fe54db302788adefa191ded810a50329b79d20a6772d14c","impliedFormat":1},{"version":"041a7781b9127ab568d2cdcce62c58fdea7c7407f40b8c50045d7866a2727130","impliedFormat":1},{"version":"b37f83e7deea729aa9ce5593f78905afb45b7532fdff63041d374f60059e7852","impliedFormat":1},{"version":"e1cb68f3ef3a8dd7b2a9dfb3de482ed6c0f1586ba0db4e7d73c1d2147b6ffc51","impliedFormat":1},{"version":"55cdbeebe76a1fa18bbd7e7bf73350a2173926bd3085bb050cf5a5397025ee4e","impliedFormat":1},{"version":"2556e7e8bb7e6f0bb3fe25f3da990d1812cb91f8c9b389354b6a0c8a6d687590","impliedFormat":99},{"version":"ad1c91ca536e0962dcbfcdff40073e3dd18da839e0baad3fe990cf0d10c93065","impliedFormat":99},{"version":"19cf605ba2a4e8fba017edebdddbbc45aea897ddc58b4aae4c55f382b570ff53","impliedFormat":99},{"version":"f1cb3052f76b6d3a0bbe97e87a7e8ffa15661ac8ff496079daef778a60acf9ce","impliedFormat":99},{"version":"18852bc9e6c3dfe183573ab1e15f983d8172213969e7c1f51fa5f277ed41dab6","impliedFormat":99},{"version":"7618d2cb769e2093acd4623d645b683ab9fea78c262b3aa354aba9f5afdcaaee","impliedFormat":99},{"version":"029f1ce606891c3f57f4c0c60b8a46c8ced53e719d27a7c9693817f2fe37690b","impliedFormat":99},{"version":"83596c963e276a9c5911412fba37ae7c1fe280f2d77329928828eed5a3bfa9a6","impliedFormat":99},{"version":"81acfd3a01767770e559bc57d32684756989475be6ea32e2fe6255472c3ea116","impliedFormat":99},{"version":"88d0c3eae81868b4749ba5b88f9b6d564ee748321ce19a2f4269a4e9dd46020a","impliedFormat":99},{"version":"8266b39a828bfb2695cabfa403e7c1226d7d94599f21bea9f760e35f4ca7a576","impliedFormat":99},{"version":"c1c1e740195c882a776cf084acbaf963907785ee39e723c6375fec9a59bf2387","impliedFormat":99},{"version":"137f96b78e477e08876f6372072c3b6f1767672bf182013f84f8ae53d987ff86","impliedFormat":99},{"version":"29896c61d09880ff39f8a86873bf72ce4deb910158d3a496122781e29904c615","impliedFormat":99},{"version":"dc1d7cc525fd825a3172b066489eaa2048e8e40ce2a56a6f1372ad05236bc049","impliedFormat":99},{"version":"ed9ce8e6dd5b2d00ab95efc44e4ad9d0eba77362e01619cb21dedfdedbad51b8","impliedFormat":1},{"version":"5520611f997f2b8e62a6e191da45b07813ac2e758304690606604a64ac0ca976","impliedFormat":1},{"version":"00b469cba48c9d772a4555216d21ba41cdb5a732af797ccb57267344f4fc6c3d","impliedFormat":1},{"version":"2766bf77766c85c25ec31586823fefb48344e64556faad7e75a3363e517814f6","impliedFormat":1},{"version":"b7d1eaffd8003e8dc0ec275e58bd24c7b9a4dbae2a2d0d83cf248c88237262ce","impliedFormat":99},{"version":"7a8b08c0521c3a9e1db3c8b14f37e59d838fdc32389f1193b96630b435a8e64e","impliedFormat":99},{"version":"2e54848617fae9eb73654d9cf4295d99dab4b9c759934e5b82e2e57e6aaaef20","impliedFormat":99},{"version":"ae056b7c3f727d492166d4c1169d5905ddd194128a014b5d2d621248ed94b49c","impliedFormat":99},{"version":"edc5d99a04130f066f6e8d31c7c3f9ba4749496356470279408833b4faee3554","impliedFormat":99},{"version":"2f502ac2473a2bbf0d6217f9660e9d5bf40165a2f91067596323898c53dab87c","impliedFormat":99},{"version":"21f27a0c8bc8d9a4e2cf6d9c60140f8b071d0e1ffddb4b7dcf6bbf74d0e8d470","impliedFormat":99},{"version":"deb3f73972ef3525308c943cfe417840e64ccfc3a3e3cebaaed4ad51c241e6b4","impliedFormat":99},{"version":"09f1b5d09fd74c119863dd4fea0c13cac164a5b35d9efa4f0ee6c407310fc1e6","impliedFormat":99},{"version":"49ef40d7a022a3c9060581d2d1783e9a0b6eb398330cf950cf4713214892c5a5","impliedFormat":99},{"version":"5256f5cf585954c773ee01a0272df9e13e0fec1d32ae196619c9a14dd4dcfdc3","impliedFormat":99},{"version":"9cbca8447baaa98288175320c3eaa02135d5370881ee2ca2a1c91cf549b34d81","impliedFormat":99},{"version":"1d6ad75caac5c783a41789d1f9ece0da982b4af600d2ae6a7f2dd025d12aa212","impliedFormat":99},{"version":"7cb7ca9e74d896aa6f51557df37c249605ce93cf855c075a91fabaac331d4a80","impliedFormat":99},{"version":"4274ed938e85b119581cd6c65c7242555567eb55906af839a931f0acf6023982","impliedFormat":99},{"version":"8151f274499e464ac8459cbbaae63e2537d112ca41761f5067a05fb0e98e9291","impliedFormat":99},{"version":"825103c182891d61d14191b0bf64b0666663d4fd1b1468a30c203208297f253a","impliedFormat":99},{"version":"5889044020ca262dfc82a80357d75d715a0b9aa6dc3673f58220aefa36818f87","impliedFormat":99},{"version":"884aab8c07224434c034b49e88de0511f21536aa83ee88f1285160ba6d3fb77a","impliedFormat":99},{"version":"130b39b18c99e5678635f383ef57efaa507196838ddabb47cb104064e2ce4cd3","impliedFormat":99},{"version":"81ce540acef0d6972b0b163331583181be3603300f618dcd6a6a3138954ff30c","impliedFormat":99},{"version":"dae790c423f8fb7b4c58e8872b2d5146a1441d0816e50e0d9bb54e2f633c3970","impliedFormat":99},"193f1373eb80a23d94389c2aaeaa0eec97526e59540eb35274b0aef7dac0c149",{"version":"389f590e8caf51cdcb9f8c5a9456b2432aa618221cfcfb104545b1053e55f0fe","impliedFormat":1},{"version":"bf3096a4833a5aee6db86392731cd2220c26d2c2a1289c99926ed08b190c2204","impliedFormat":1},{"version":"11c8c7fa6125ae624e1db191f9a8e5c32928e6f9a716d7369fec1f60de1ac7a9","impliedFormat":1},"c5738e1105a87a17fcfb71edd69a9a55d49827f30766cf74b0336299ac2ce23c","47b4af2dcc5d77ed9be31183d8f7b537dbf5429c374debdd535e0ec2ce87e775","461e5ecc4ab9b5d779c022e1ac8a7ff8becd10d4ac45a1fc1863ff2bd4d2c32b","062736177634112f408574bd159eb66193121151e494ede31c8b60da6f721e59",{"version":"116b961153d86b304e788884c4a05630fe98423bcfc14c7a7ea8d542092aac10","impliedFormat":1},"913ced5d1f05334d27d74f8a6b18c2a0920019f08953bcadece79abe24501646","4e51a60aea318b0f12e28bcf8bb9d3c8b99455ae82b6ae56faa4e696ee9a1ab4","e02c48a648106c1998cb6238cfbe8cce1b7d4213ff3ffa48b655757430398a57","0ee690c92e0ac77f28f84e3a75b113686926c214fde5d6923894bba5045a766c","bb0eee26532ad247af379c81ae0ae0ce80f8b55231890884871b7c1b78f5e1a6",{"version":"727db096c15f151fecb4adbb96f90a9f529bdfb5b893f1e897ba15b229184aa6","impliedFormat":1},{"version":"04b92e17fdf13a1dcf3242311f41c52067e0c3d29f1e14cec161d23fcae243b6","impliedFormat":1},{"version":"1683152f2b8997fec98d9afa15ad28ca4f195196063ac60934af1c3f59d384c3","impliedFormat":1},{"version":"ed01339129f5bfe460f75b41e04b0bba1350302a36308ad6d6ea5aa2147c0a93","impliedFormat":1},{"version":"c3d7f5d7352d6c3d486588e8e937a3a1afe44fdf19df7724d6f06834659b0625","impliedFormat":1},{"version":"270da28fbc7947772168b4414052ad854bf7b9b6fc957a10af787f97c7a9355d","impliedFormat":1},"456d8e144a9c781b94e3a0df4ad470d9e868896dad004c687c10700d7ed8b1c6","4c2e9808ca7e82f178ddc3a417c7d99b04a25e3a9c736121fc1a4987229d3cf2","48494eb40511f198db4ab9af0174716f73113995a898f43606dfb95f35440525","1103f2f421f6c7fe8fa6a1de080cdf2aba4c7f67e6f35bfeadd32bbf54612950","a0717af32e098b0d97726db6672abbea6a732fc99b06e4668852184b30bf10db","c984586e98453e84ca5d51265cc54dea8d7fbb12cb86257e6131553f6ea31329","9bd783ffabfdfe6ed5e1453b096dfa3c15c4fa6a535bb7dcfa1010ca7b0ca62a","cca620c504b78edcd417e066feebaad1ce09858b7ed8f2bb3f406a5e6097a4c5","d95ed326c4484b678f2834f388ef6697b011cf8c2174efea56b19126d66c8332",{"version":"ffb58968a72b1821c0801bc6ab880a36a54c886e2cb258af248576ed53e0158d","impliedFormat":1},{"version":"be93e53a9f315349d82d175ac09f15f2bbef2460c558ee90a143abb8a135b7b8","impliedFormat":1},"147570e7d5ffa0791df4525e2f01e5452b8f1182c6aa4019d662563f1d9ccbda","7d9760583b5fe842c07a6f134418404a77e378b05784f6bc08e5b63ace387355",{"version":"b80c780c52524beb13488942543972c8b0e54400e8b59cee0169f38d0fabb968","impliedFormat":1},{"version":"38d04fd787d8830303f151af250273cdccd41cdfdf47c2535f60d07d0684129c","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"6837f70d2a1d87fd5cb4a3c85c6e905db377685b9ee2824cdbd74d1f3e594900","impliedFormat":1},{"version":"c3a3486ee72fa25eb598eeec016a7bec4134bdb63a1a3099f67ae5fe2b57fb00","impliedFormat":1},{"version":"c92274ec844d06c4e8db04735006d2f91592f614a63346f49bc853a3fa8a67fe","impliedFormat":1},{"version":"f3a79a2395060f72f051e4a7b18cf48d4e71a1044f43e7f127c87cbdcfd2f0b9","impliedFormat":1},{"version":"f45eaa9cb1f6eccb5cad43d88a1f84eece777bf5f603a86568ce119cc600f0ba","affectsGlobalScope":true,"impliedFormat":1},{"version":"aac318e3b06c3e243395a7dff4c0dc82bcd4c86eb9eee8b53706e991cdc3d0bc","impliedFormat":1},{"version":"dd8d234595489ffe667bb0b735b7c6954e2b617efdc9bfb16e77cebdbd024abb","impliedFormat":1},{"version":"9f75ec6de3cb3438845a0f26e9161ba9f5a67c1ccc27457ecab2713ba4766860","impliedFormat":1},{"version":"bc15736a69ed22f24c5c39a8dd0500a75a6f9e9223e2fe809f42a3880876d2e4","impliedFormat":1},{"version":"53dfc50db0c47e550b499b25e2cbd3a74e0409f386696f059238654e35ba6be0","impliedFormat":1},{"version":"7a9debaed1ee41bff25f7b0128310519d593c01aee5b1bc2f602f45fe476551b","impliedFormat":1},{"version":"29d6f94d764b5786b2e0c359b41de3eb5aebc263eaebe447ddce28fef1705dc3","impliedFormat":1},{"version":"28ca0fc70fc8e069339e45f878116be759a09be7f66770991ea4a672be42e254","impliedFormat":1},{"version":"62c0424b25acba7640aa3d20dc1e31f3f844b3b11b8dc76ba79a13353d98e8d4","impliedFormat":1},{"version":"50fb3d1729d49e6ef696ed6312487089d7550c50392ba5150c11b3cfe84f6c52","impliedFormat":1},{"version":"05593588e67c08c3ff4f99dd83ff8616c4a9ba2a90fefebe5083226d52e96fde","impliedFormat":1},{"version":"060ebd0900e6cb2814d16824222bb4a2345bff5460751c69fcbf90a5fa110b5d","impliedFormat":1},{"version":"79a0e67415639006805e13ed876b9f017dae5b5ac24c2589f08c833de8e22683","impliedFormat":1},{"version":"9f6d7b3a0ed3c8d40bb5cd79103fb3545d70b079bdc7caf948c13357101d2bdc","impliedFormat":1},{"version":"0612268087e02a769d95c192500e6850485c51380ac494fd270925da60915bd4","impliedFormat":1},{"version":"ad2090ed8c1e68ae4dc0fca17ab39b4c89ef52d8364f07251b64c7caeb6d719b","impliedFormat":1},{"version":"f82f0e10f6968b24bc86a7abb74f958dd271eafbd7f34867763412285ad3193b","impliedFormat":1},{"version":"544c9a8125a2b0e86bf084c9e4ab736239e4fb821a12f056c15b0c9b0c88843b","impliedFormat":1},{"version":"e82e8d2ac7b4a18748dfc8a2637574168750a4a9d38aae21896b0cd75ff94dcb","impliedFormat":1},{"version":"0ef816c1aab08988f4707564f8911b3b94a8a42175dcb4ffa5f29c3c893e3a48","impliedFormat":1},{"version":"3f94be1d455ecbb2c0029d08a47a56bedaeff595f702cae58967496e2597debf","impliedFormat":1},{"version":"ee7f235104115fb3735949db7b079935332451d1072316ae104017b6390f6827","impliedFormat":1},{"version":"2a784cc7eaec3ea5715cf6e9ede36c1c2801f9597a53baac9451c3a3952210a1","impliedFormat":1},{"version":"30cd8a844330b0859c8e0f120455d1b1f5e78d66906d57a8d43445b8ab7775ed","impliedFormat":1},{"version":"2af1c4f4a76909d59d15d2cfe86b4491c7770675f17bc80bc291d996e1bed46d","impliedFormat":1},{"version":"34546ff608e09e6f1a6bddd294a06b64ae0cf81d1d5258d1faf80991b17a440e","impliedFormat":1},{"version":"de747079c47668cff56cac5d5ddb90912e8f39b10a1e33e23aa746c66bafcda6","impliedFormat":1},{"version":"b6b102e986078b9807500a78b54ce96a935cc8849981b5bbe26adfc6f531137f","impliedFormat":1},{"version":"d0c2a38cd974120f2adfacefa47e7f65b909b9ac8a0e529f2de1e81abcef4d92","impliedFormat":1},{"version":"7b7d0fbaca498b438f4c32282fd19127586b47fe900ed27faa1d2a963a840aad","impliedFormat":1},{"version":"22c2767a738f821fffb3466eba2e68480f64ec4d8caf78884131ca3f195082e0","impliedFormat":1},{"version":"dd9e38f76f1448060672c6039826b9ac690956a15430987893b22511ca1a1c89","impliedFormat":1},{"version":"98f0a4d51265b12b8ef49da7e8686b7c831b5995ba5bbe71d2d0b0ed295fde01","impliedFormat":1},{"version":"57c362cae391f43cc61dff77571e3fb988159ae88cc26b9b52f6f27aaa5b92e0","impliedFormat":1},{"version":"f82a2e7aa1543af5ec43b21a03d8a3c70083132ccf439739c34b1b0e8258a3a3","impliedFormat":1},{"version":"a42f4b4154bd5fa8badd5fc184a9ca8147de0cdf80292d941575e06efcc8307e","impliedFormat":1},{"version":"d6cb70789adca8918953a560bce5018431cf40194f0f39ab9c58924a4febfc91","impliedFormat":1},{"version":"255c338a227348b19f0ca28de8030d2cea854dad8819fd8dca0f7319311dd940","impliedFormat":1},{"version":"7a935e0f360be6cbca9514451e72aebf62fa4e6964de8981b9b0db28ac277831","impliedFormat":1},{"version":"29ba395fa2fb6caed0f341a53f56ec4f4fe354629fd9e00ff23ae0030b11b651","impliedFormat":1},{"version":"2baca5854bbd67d326a643f0b14d180d0f47ea46b8e08299d188eda3d6529551","impliedFormat":1},{"version":"86e25a96633098fa9a9dd17e6f20b4d32f026bb6a0f3c9629c77c3d471320194","impliedFormat":1},{"version":"26a282d4da2a99ab9d4040a1f226ac2c2ac9e43fbee5a4a4adacde0f27cacc97","impliedFormat":1},{"version":"00280230822d605f2459097c1fa7b024a93126898c65e1b18f655317ff183e04","impliedFormat":1},{"version":"cf3ae13c39f68d103180888c912ca3657bf375c097e82736a5e446fc19044f80","impliedFormat":1},{"version":"00cccf1375c6c1de0a0de003d49e0d9092c6992dee7c4be0d10d32188a0d0c30","impliedFormat":1},{"version":"053eff08de8b31f1debf5d7d91b5b2ef8a0d0de919a4a18f9f8a437c38698c66","impliedFormat":1},{"version":"896783def1d521cacb715936c5b056449b7cb3bdb6c784385bcbd1db19561085","impliedFormat":1},{"version":"3d067c2d12092a387397300c995a4060fd2acd2ecdf04500d8fb871f827b1fa5","impliedFormat":1},{"version":"95314b5ce9706246b5591ec80687cf5574f34418d8816c5a6f08ce37f976c3b3","impliedFormat":1},{"version":"90ccc9a4c6a5bb45b29b199d88298351197a66477b20ee462139ef4afd0886b4","impliedFormat":1},{"version":"d705613403ea7619388fdfc165b222d71cc174870b50640bea9e75ebe9860b0e","impliedFormat":1},{"version":"aca88d12a037a4dab6fc5f7d15df977945a1c7057635046eacc24f802939f568","impliedFormat":1},{"version":"1041745726524f58228a1fd2443864372dfd93312df368741c06e3f3d848420d","impliedFormat":1},{"version":"2e2997f2bb7f56498363b11ed03dd5d0e110a5fb7d6e2476cc6c88675e07073d","impliedFormat":1},{"version":"8dc75cb9862f1f8fb18a7c039519a66080501f49435951d85add444fd9126e99","impliedFormat":1},{"version":"4c751fce5f7773554b0c7064e0f85cc4a383a0a1beb03e95274a518c2cda8caa","impliedFormat":1},{"version":"b56e323d7b1e047edfdad27891d52246c6a29ef8461746157134b5d41beb4a0c","impliedFormat":1},{"version":"60dfb3197110135a03e898690d93a6e2e051d1f1ab7a6f72a5f2362846c66b7a","impliedFormat":1},{"version":"098506e0bd163320798f0f3125523cb83032bc57d5d3a4cc642948788dcfbb91","impliedFormat":1},{"version":"8d55c2456d0a005daef933aaf7b40dcfb71f4529582ab3b00ae9e7bbae35a450","impliedFormat":1},{"version":"93dd84cdb2504703d6a6da417ecf69502c1fcdc9884b16502c0aa951f73dcdaa","impliedFormat":1},{"version":"635eb4ee8873c8ccc693055136448e965ba57a3964fbb7661cfa38f04b0ec8ac","impliedFormat":1},{"version":"0fb7af5591e97c4489663ec906a1fc1d2efefa3a334db215974280a9e195afaa","impliedFormat":1},{"version":"5f1d76380791fff3e6b1add69ff3e5ee99afea5144e079c9c4afa1c2ed2103a2","impliedFormat":1},"9ea766f62a05e41bbb13ee048bbd5744ab2d2c71ea75eaffb17265a435ca4659","eb90cb84825dbeaa6a4a94419df5b77ea7264d80085c806d0a12c96bd16f7298","f8e0279b433280f369bdf14a36b2cc81ea0b7f1fc0add64dde0b62232031572c","d73aac070f5354aa7eb9f9238ef1b3c247c5dc6fe4d60432e08b64a052264acf","f255ac5d8b49b2c84ac34805f0948d55b4a33c1e5f75e681f49932813b965a92","8367215079c31e4c5da17408e284adfb559f6e502dc88b4b74c2eda8a0bdcf35","490452b0b17f5cb01b5438fa28d4143b557fecd847d356d1bdf28947b20c9bf8","102c7bb6a9d05ebe1a745f58eb62afcfc73a0f3b2cdfb2d050c8456449e57e45","011004817ddcd91fbc11dfa0ff2c5a47a115c8a1214d139e9179ee83096f5ef5",{"version":"9859047560c0c113d5a68030c2f5aac009c7a3b7e28e0a2f6c4fa1bd3a607fa2","impliedFormat":1},{"version":"1e7881b7155cbe71a30ede9d4e3339d9de60c72d97147847bd9bef85077e72f2","impliedFormat":1},{"version":"7d2ff56ae2357110addd96ac7aa7510224f31e2eb829486bdf5a4af7e2314c9f","impliedFormat":1},{"version":"729df80470b858cc2e4c770f00f4b945e8d073d2595cccdf190d5b8c2173ada4","impliedFormat":1},{"version":"9da28b505b75310e36af6ba2eb5bdbc28480fcda1fa3901586668e00c322f10d","impliedFormat":1},{"version":"13565d74ceb7b9aa57cf122e7daa3267742ab21f2318bf82e64e346ec3fdbce1","impliedFormat":1},{"version":"4ac2b0331721cdd9ae3915d6cc80f3790de5c6caf7024d87ad7665b7b7db0dea","impliedFormat":1},{"version":"45455d6b12f14c6a213cf09930dd83bfdd56104b354a938d7fe495e8965dd58e","impliedFormat":1},{"version":"dc68c2de54f9a748a354fec3d3f4a1b9b946561b59763bb22069e0756b60a18a","impliedFormat":1},{"version":"151df51648654bbf8922ad3243494410da97c8a9c00b50d8bce4c7ae3701f20c","impliedFormat":1},{"version":"7c48c73f8beab7282443f9303550863dbbf10654e75d338de50a15774522dda6","impliedFormat":1},"52f0abd821736bd5f8bd1179e140e791cda7cf4b8ca26e192b2d89528a3a3202","e86805390d3c6da087ba5ff7a73e7330d8642a4485ada70d27e71b489787e984","26da34c1c5e6378780f5a90a2ca1081f9300571abeed4b57303965877e376178","5897c92ef992dee9f0c9b362506d5aede35710498287c2d06a5b44206483b65f","a8b485511eedd3723b794dd782ea31f1923a0bcd6875ee0ecb197263bb25f473","9f82829a4bc4d013bae76f5842c253acef2337794dbe779a62de66db99e98f5d","30b4e2d87978e8164ceaca8eeaac090094548ed8675e23733dfca5e06054f291","e28d0991d4b004aed683cd2a9a52bdc790c52d6d40df067505a26e8ee131cb37","06fe7c4bd5735f0c267f3ac1994162888950c70c4826369d1cac0d63928c09cc","0c8a55da3c97f6dc60190b49c57e43c84e365b9d40e68c76a8a165bcbcf22e44","f646c2f278c68bf86bbc1b8cb19898bf60ffa7def582f0dbc35bc3993107a3ac","de47cd61c92d9bf7ea6051de11fc48a8807f753a815bf9a0e2d5ec02a07d2e4c","2ec92b51babb56791bdec12345befe04875d40ecaa263d7bc0f80f3f84882a7e","bce5573c4dd874b18c3e833bd03406d06f2e08e09b22fe5169b7b84b7199d9f1","247773be4a1db02114abdbbc0ac1e3451061eae4acd0915da775d9996b3e31ed","a3d40bd9803ad2de1de3cab1d1d00f24ed61b9b419487aade31e0bcee8e4bdee",{"version":"be362a31dff85020118be44789e37329db7ceda72245145122234b3c7377dc54","affectsGlobalScope":true,"impliedFormat":1},{"version":"76292c6ef47b769a72de772e800eb08427c75cab39398fa0741d9f8b1fac5865","impliedFormat":1},{"version":"4cfa4ff43365d4be2e7e30b10fdb87b640b70937eba6c55dd3c8d167883c445d","impliedFormat":1},{"version":"5cfcb3a022da06031a63ca0c1a12b63d3b2b52d16309a856eb9de37ad2741182","impliedFormat":1},{"version":"f5e7925f0ce3a1671a7a2809ed2ec12312badb2834d8034a9e906c99546a7932","impliedFormat":1},{"version":"7a262b150b6f170ac6c555fd26909bc8e5ad0bb962537c5edbd1e10042b1b224","impliedFormat":1},{"version":"6a42594a49d1ab90a79287c9b1f52770dfe25c66a11e3730e02476ea4e3af322","impliedFormat":1},{"version":"b1aa6957cc9be4f2e9d8e2f3aab92978bd8854837771fda706b8eb4b1e2bbc96","impliedFormat":1},{"version":"cac1fd8a544e2f3f5fd047981156da014ca88503df42a8c4384581cf5a58b11c","impliedFormat":1},{"version":"1858da773f5eaa3f3c8dc07b401e85d3057ff01771c84c905c11a34472487742","impliedFormat":1},{"version":"19c3e56faf9a401b93ec37507d97572d295f4bb00a775d0455bc920d58e58250","affectsGlobalScope":true,"impliedFormat":1},{"version":"01ea032563dc4d47643b8e88ec094bfe5b2b274fcfd59f446369b18c963ade93","impliedFormat":1},{"version":"6697273e52441af8e34ecd87f018e292a75c3c5c022d4b4dfe93874751314a25","impliedFormat":1},{"version":"c179a885e989742ad1f1b58024a0674810bbca1a771ad82f543dc68d5468d396","impliedFormat":1},{"version":"64730ab9b396c96b2a01e3307d1ce8a9eb2a4c8e8ac82dd0eb4d6e7ddd3081a8","impliedFormat":1},{"version":"0f4e6fabcde74a53922bdb5d18fb99f2d69b92e01e9afa1816ef0680a9d5fed6","impliedFormat":1},{"version":"719ad5dd3a1e287cb54b4377967f99813e8144c61e1c954c585a644df3d7e87f","impliedFormat":1},{"version":"488fc0ca71cc34d5d6e674a722397298bd734216ad0275826df0a82cfa0a03bf","impliedFormat":1},{"version":"76d66649fffda5bc10184fbc693349e2660e6aeb91a30ba34e8281c3474cfaa2","impliedFormat":1},{"version":"173b33acde20c6612a11582a959e6f41e708947986cc5e13ed8999151d37a481","impliedFormat":1},{"version":"2e9b7ad4e7f1a9b3ccaab7ac7604fa43996d11ed09eb562bf38cfe1bb0b13483","impliedFormat":1},{"version":"f40b2b971585a200e416ba520cea15bbeb51705a54623f615c8e41425d43be36","impliedFormat":1},{"version":"ec834b82517e87c79085f2113554378a3d5cbf8eb3e898e80fdf12acc20c708b","impliedFormat":1},{"version":"09a55b7ca68a2365748bdaeab629b5534193802b2863e1457b40c8ff3eefc984","impliedFormat":1},{"version":"0385edc4f8972548a3f66458c0729b415e43a631c048c9d23ccefcda318a1f2e","impliedFormat":1},{"version":"42377ae0566eed293c98f52f72591eb9fcca2f06ea2eaec0455cdbe403ba1bb8","impliedFormat":1},{"version":"c1acb8b36a8f7386992c041ad140c0c24a2fce4441b8423251644611f65f9ed3","impliedFormat":1},{"version":"bdf2d2e5c28070e9329cc40ca536ae87250392333bd2d635b247847966c158e7","impliedFormat":1},{"version":"8616e6f003a26e4aaa4be5cc6d5a141ec5ad5f1e8c0c9bce105f8ae4723cf344","impliedFormat":1},{"version":"1dba1d968f3b4e6e0006853ec7b316a5b8f8386d3bb2849d72abdcdbe4269e19","impliedFormat":1},{"version":"4330111fed7b7d4d3fa2f8da44fe991e1e4d2b5a2b46809ef09c03d4b97c138f","impliedFormat":1},{"version":"7c6c76e829081ac329abcd59c189f25d2eee3fa21c9525db26d970d4b22400ec","impliedFormat":1},{"version":"a2d050a52412f0852654dcfa5984400b6b0d114dc88e619f5a6d94ef66e2969e","impliedFormat":1},{"version":"64cffcbc9792bf9dba611e6fbac1f82be478ca6f2df45d9a4bd92c96f88a34df","impliedFormat":1},{"version":"791a2e500643d44583d78c622e55b9e473e76dcef0249205d775368ef1e8c03b","impliedFormat":1},{"version":"0cc29c72e67ed4f91194f0b7c64fbb3e1448260269d72aac4f20e175c49df3f4","impliedFormat":1},{"version":"b572cdd4092362eda885c5739565f9958debb945a2ba04455730f7ea83e2c63c","impliedFormat":1},{"version":"e3aef2d7cc285aa3f16a7c8576e0c92a9ce041ad82343444245974337f99d284","impliedFormat":1},{"version":"4447d3229f5f11ed9c21a577415c634d6f0f88c20f39d4a416ac6d359f5568d9","impliedFormat":1},{"version":"65d78c142572b4f26777d8dfc9cc224ace704171a86b8598fa30ea7d63362b61","impliedFormat":1},{"version":"6a827ea24bdf7f3e8255dd676452a8ef6d1e6d1f8603156cb0c7f9c0f813dbc9","impliedFormat":1},{"version":"a2663c0878d107a5fe897e53a842fc487907741173cb9ff329988a295fa15f84","impliedFormat":1},{"version":"cbdc36d10a56cb4f8e90fd6c1969981f000c1bd2908aea5e5015ec7e71140d44","impliedFormat":1},{"version":"1852dc4bc8fa5de32b6e9902078138ef53d4a9e0c1feee7544c607d72a5ea6f9","impliedFormat":1},{"version":"ec50dbe52edac6e58a904159891b2129c2e55369b3ee73b5b91a4ec96f5b52c9","impliedFormat":1},{"version":"b93550d2b60509ff48c4aa237d76736971bc28bc2a044bc1c443bdf7a1314c1c","impliedFormat":1},{"version":"54b6a7f3dee8f6b3be09c83c7981fad5b748e996e50dfb1ee18a9e1156d38254","impliedFormat":1},{"version":"073066006ee7f63c193b14616d2980299b5506d394e367016db33862b2bbeeae","impliedFormat":1},{"version":"4efe5bf668103e063f470bfb29456f92bb1ea45587393781e1976a42f4e7d111","impliedFormat":1},{"version":"db7d16ef1835aa16a51befa5a68cd7f260597f775de2f443c2e20b1bd17dd3c7","impliedFormat":1},{"version":"c093d945ff065b5256e6c3ba7033fb7372ce5158b7bb34ac4f0bf4071604afa2","impliedFormat":1},{"version":"b00375bf3294048fa0d37c1b00713f8a679a8b0801802c79bdad335fe112a14e","impliedFormat":1},{"version":"83b4a79b75090e1b35bafd68ab0fc1fa9678719d3bf9eab05b1376e4ace701c5","impliedFormat":1},{"version":"7c3c8fef31b5badb5c01645e1ed4313efef1a2f61c31792a182d59272c29d43e","impliedFormat":1},{"version":"d30146c76542db9811d76be1473e17386f444f206b92fb3e504dbd4a293d9781","impliedFormat":1},{"version":"37a299a6f7425a624b13c14326b712654495647424c0683e38ff5ff89043bdfc","impliedFormat":1},{"version":"51741ad2e093a68359030f1b37d11cae828be5fbad7da1d9497664299b36a099","impliedFormat":1},{"version":"e9edbba023c30a46cb5e20066418427780310ac9da314a589889db00f1f3f89d","impliedFormat":1},{"version":"8f6c40eff2221bbf8e156e502b612480090256eada3671fdcbd92581a4a719d3","impliedFormat":1},{"version":"e4248b0a728dfd3c9ce2b25b19394b02709c1d5e7f0036e290974c5e8a71a2f7","impliedFormat":1},{"version":"43a4a8768d59987d33f80a60c6f51cd922d0367e18a4c0f7a21e10a22d201243","impliedFormat":1},{"version":"ef67fb59776bede370e8b78a9554ccb6a1863b21fdcf41730919afbed00d0ddc","impliedFormat":1},{"version":"39746082f882a595a185e65a63b3c530c90d9a38a02723004261a9e297129c9e","impliedFormat":1},{"version":"aaa5654ffca4c560d37c5ad236df82f70810c2cca081a1849a9447abf5539ddf","impliedFormat":1},{"version":"2d5e8a00a806fa1536c4d5f314756ffdfe4f91037ac3401b44e6643c074e19d7","impliedFormat":1},{"version":"0d12963e824879f33ce26b5379aa1281413d89e86a5f1dd3a5db81c0a2fe9c4c","impliedFormat":1},{"version":"8c6713c6e4e87c4d29b1354d49675a7b4f94183b4d03358d21b7a2d8145ecdbe","impliedFormat":1},{"version":"fae1240010a374142711478e4bb4cb8c5c3833f59cce5680d3eae591ada4ae5f","impliedFormat":1},{"version":"962886aac4d1668b030cfb02cd8b4e3c7477b050a0fb363558b570cc1847a558","impliedFormat":1},{"version":"99bc8d6863512a9431df6577c5c2fe3862cb1bee8799f3d27867e93edc0dd519","impliedFormat":1},{"version":"d5d9eb73098ffd2b47702a63f6702d0a75e47b9287230eb1cb36763a947ed41c","impliedFormat":1},{"version":"3ceab852a52fb3969014e5a117018ffb1ef09bbf1c645657d2d253961bd8f49b","impliedFormat":1},{"version":"5cad2832a7a1b526ee8c879e10d9cad0069a81bedbd6ecd3a6b238bde1cafd75","impliedFormat":1},{"version":"d8647fce24990acc111b75e4375ef8bc6fc5feca1ca9752b9960e780b616f006","impliedFormat":1},{"version":"63453746f73c11a8f55dd437623b998cfb0369ade4b5bd0f2722a1a7e87a5962","impliedFormat":1},{"version":"145f6416d156ee0a4ca0d32b73a0068db1b1565f3aa5776a215f35e66abe6d3f","impliedFormat":1},{"version":"012841c703f070be20484a69c5a6b90a757a123b1b005fe8196c951217067f78","impliedFormat":1},{"version":"161e3ba2ac76d6cc324d254ede6ac484d2e9ed425348cfa87d189ea69945821f","impliedFormat":1},{"version":"0039fd95c84be02289e15fa2c4bb6c35eb934d761564bfe703a61ab14b1ee014","impliedFormat":1},{"version":"260ded48dd3435416a98aff8292889ef4aa40b1930ab94031a528ebe9ef49972","impliedFormat":1},{"version":"658cae7f6e6fecab96f7bd1dcb533777d3a037c0e4669aadd2cba3ad308a5f01","impliedFormat":1},{"version":"3cad82b241cdf2d7223e22d4ade169ec7d0062515ab78793700409bb8be09995","impliedFormat":1},{"version":"09c59b63947e1f70cdbb70fd31b7d231200ea1ab5c0d3cea7782c6bbff502eb4","impliedFormat":1},{"version":"45e8c5bbeb35cf833f6010cc55243bcfa0d13a2034bced78c39f7b6b27462c10","impliedFormat":1},{"version":"fab6f1fbb462e32e6a85236b711f7e40fe44ae594b3a86eca7117e1334bebcfa","impliedFormat":1},{"version":"a36f021977924f8ba2fe2f195098e2a3bf9ae243bcddbc929b9159e03926d79a","impliedFormat":1},{"version":"79424f24f5acd14066ae056b484e5ca7c274acfd27d65839f646be67c95fe82e","impliedFormat":1},{"version":"9d9aa6351477667c7d5ef6cd0ad975b3e2283a7aac7ce58d25b858e08eda3efe","impliedFormat":1},{"version":"9f6eb1f32a51bfe805ece0bb44d1865bcf9963e88e6c0ea1e39ecaee02fb8dfb","impliedFormat":1},{"version":"211a79196daa7d14777cac295f6172275ba6545a6b387fc822d1e5959ef6ae65","impliedFormat":1},{"version":"ba57f6882bfe7ca9d8e12319c10175ab2e0d6a548231d8c04d4b5f40650d3bb7","impliedFormat":1},{"version":"f18b4bea2d3930ce7379e9a008384d55200985a74c45a52956cf5358021eeefb","impliedFormat":1},{"version":"b736875b02254de4533c2e30b6925b29b1e36d3a044b13b869341e55e7db23ea","impliedFormat":1},{"version":"122d853aef74183269bef94f6eb8c6b26e53d43247c54b3d8ecd8fc97dd1b3d7","impliedFormat":1},{"version":"28150e454e690d820a291e3f43b2b9edd555fab29c75361f43717f8fb3128500","impliedFormat":1},{"version":"8027f19e88b2f8f37b128e643286233e8ee6d87bec59359459d84e216116d7fb","impliedFormat":1},{"version":"3c4b44c5f88256d11bcadc47eae39af72f7ec13d623ddeb2ccc4a29c3dd22da5","impliedFormat":1},{"version":"ea9086d9114c2d2dd77f9ff734010f2ffc9916dc1b3ab9ecaafb6770aa68d8c5","impliedFormat":1},{"version":"adab54c16578685abbb9d0f12d29f328dde6040c1d858b698778c63944d8b019","impliedFormat":1},{"version":"635bc99642d72c8444c5937fb6e8d8a449cef4f73824ecb3c9ff5bcd96d9d023","impliedFormat":1},{"version":"9a8118dbaed0a95b33528f4fcb1cf21c569dfde81425f72d3ecf9feab28d7720","impliedFormat":1},{"version":"2c797623016f76f4d97a445ec1a1f091e91c5e0bbde9d17f6e8bc8136270c9a5","impliedFormat":1},{"version":"0115e4e6f08b8384d2f356c8d392e1a8237cfd9105081eeecb9f0393de22e628","impliedFormat":1},{"version":"fb35442fa2fb13cb73e7b6a42f2d1db3540b7cf893078d42dbaf6cd48dd120c3","impliedFormat":1},{"version":"57810fb8b19f3f7244192a6b2f8a648e0acf36d277c026c9f842f0ab21d30eb3","impliedFormat":1},{"version":"2d7925113e1f6e495aa5b4d346d9903f0578829f6b96a44f934cd8fc139d785f","impliedFormat":1},{"version":"a9ee8731b5bec4a3497ca3f0b93b61f98243d24bc510c2612543c6294e6ed707","impliedFormat":1},{"version":"6135cf12655d3b70b93338842da056d658b5977907b4b92714f7452f8e953beb","impliedFormat":1},{"version":"b55f09e270ed9f51094592273fdfd689730023afc670679b27136aac411c31af","impliedFormat":1},{"version":"c625fc7d1c4cdedf1b79a59a93dbec5832902e3853fc374bf147174b378fd0f1","impliedFormat":1},{"version":"2a8bc121d549734f6a74fdfa4c752097ee1c7fdc4877612c696486368356c11c","impliedFormat":1},{"version":"63f0452683148deb6d2ad4db4921aee4eaf53287bf6989782cf71b7d5b63efbd","impliedFormat":1},{"version":"9b095e7439ec6d17683c4b8cdc16a87db2499bde75b7d81cf07e643c77f2d67f","impliedFormat":1},{"version":"d697569469dd141d7433b90af8e7ad0c6edc729e560bb6e5a1db96a050b844fd","impliedFormat":1},{"version":"aef0a8a66f490966476e0f7728cde03c4e748db29f3429219dc793b069fe3215","impliedFormat":1},{"version":"216f7b739e42cce623bb8ba2fea296d6489f6bb54239d9fe2f4b00ee6a7a2007","impliedFormat":1},{"version":"4e5aee348b26d529601354035cf2625d329301fc44b2d0eb5a1f6b833732f5fb","impliedFormat":1},{"version":"4648f637303911189851831c3686938af70072c2fee6b7c7280c1a8b9065cd92","affectsGlobalScope":true,"impliedFormat":1},{"version":"5f729d0146ff542673fd712f5e280434a9c38c92b991f6b9a92cecee73095094","impliedFormat":1},{"version":"ba3e9e48b52de2e8fdc050162f0099c32c5c3bd601f8718c847c59829e285a42","impliedFormat":1},{"version":"68cd0b9e1afca90bc8c736851374357e8495b9616ee33a1ed11663090e358a2f","impliedFormat":1},{"version":"6b3e4459d162395fbfba21c863b7911ced06fac343d263e1683b461545733b4c","impliedFormat":1},{"version":"93d423cd58a0e6ac7a3ba49f2a047fae456466c0f395df7631e8b9622dd16356","impliedFormat":1},{"version":"bcb325fdabb860f23cfdddf9e9ffb78f5e9f507843bebdfb7f944ba5f62f5e8d","impliedFormat":1},{"version":"b8a6e4f1147e4c30099fef56b72974674657d64cf12f2266aeb80ac1f15337b6","impliedFormat":1},{"version":"23067de9b81e897d2c68a6d7612192d997378cfe4d2e93a452dbae2e98f5c4d3","impliedFormat":1},{"version":"7f55cb3505ff27a690976effa7f8f53b52bd950933009a50851c8f06bb0771c3","impliedFormat":1},{"version":"64ab0e3cd827f4a357d6c460a490d6c2c22300b3f2b5cdfa656c2078b527f25c","impliedFormat":1},{"version":"9b721d33825ffd9481eb823168a1a52d3c41d9d234e5b1cca4ee42c8628597d9","impliedFormat":1},{"version":"b8bc044da2e581bf30b13cd9f24a6a6dca7e6f26ffad5778ace0f6aa4e1f56e8","impliedFormat":1},{"version":"c6303e5e7521fee29c0ca0136b910ebd7195946951f8cad148723903c2c171b7","impliedFormat":1},{"version":"6698be6dcb2077ebfc3947bfca08c15deca04ab9a6968afb5f8f03b285f384f2","impliedFormat":1},{"version":"2b3d174c8ec514f94090f99d55cee8363c7e35c269ec22efb40a8475f77efe2c","impliedFormat":1},{"version":"fc35623e8bf53237f55218f80d42594188b6af7b62cd8b2888c4f2d7a02611fd","impliedFormat":1},{"version":"f3d53b9e8d49c7f8c4e263e98f95eb533ff3f66c41dfbefef5c9b54ca98a1c3a","impliedFormat":1},{"version":"6479ed26ec9727adca52957a18b6bb92f266104adc8e43025c382d76ba81060f","impliedFormat":1},{"version":"c5541ed4e56a3cc7ab181a4bf4ba91763fc462d94b71687da4b4657f38af207f","impliedFormat":1},{"version":"00bea5e1b1e25e3f247d9625f33f23b87a299aa9244332948031243a5e8e500b","impliedFormat":1},{"version":"644a3153fad384d1916117edcaf79f754c7a128f2b790b9b3d1c6aadb9370e28","impliedFormat":1},{"version":"858c632d088f3e404d32dad5029ded59bdc5280c1e8a90db02c34b57d393a81a","impliedFormat":1},{"version":"713e89bd552ba627a8c9c684fed2a52331644d7fc683738443ee734cafd95ee7","impliedFormat":1},{"version":"fcca8a506d32ccfa31521a6bd84bab9c508b175c0a544b56f7dc7401e171f8c3","impliedFormat":1},{"version":"841c360a904137ecff72e776545dd961d379efb564662975a18ab58f5ab6f04b","impliedFormat":1},{"version":"0aa68223f0e7f0d77372b1cdded3f7477dcbc0e46612ed3fc7928d41f2b660e5","impliedFormat":1},{"version":"efe6b247e53a10da680ae881037adc08e107c0da75dd7e95aa964f8265b7ba85","impliedFormat":1},{"version":"2f99bcd69f6272fe5673ebfa4a98de7a68a4e11920a7068bab1245b1321f3158","impliedFormat":1},{"version":"95827cd2c7e5bffb2cbcc104bd3035cc74c5e9d61857142f397042a092502daa","impliedFormat":1},{"version":"148a178507eaff8afad8ecac9118580a637a18bad730e3824ae8d8c328374632","impliedFormat":1},{"version":"cb83da5b102a0a7b8bb6138ca90b0497707c7659dabc60a77b99d1f13bdf9fa2","impliedFormat":1},{"version":"a92458efbc9a8017e36614181faf85334f20315bff7e84ec3e25edfb7575b4b2","impliedFormat":1},{"version":"aa10e87dd89789375e9043ca12f3a43dc6fbf6a01d9dfaaa05be545380035211","impliedFormat":1},{"version":"a3bab9e5e0cbb1a5567b3518ffa2862589172f39551afc15b942138c1bbe6d54","impliedFormat":1},{"version":"e117e2338388fac73a2d8317db2c8b24d57ef98601deca94288956a8fe4e7f8e","impliedFormat":1},{"version":"3a07224f5c15ff2d9ea61c396379b644896a12436235cb223b2e050b23c4925e","impliedFormat":1},{"version":"8e58eba9304f25a63c67ca6213b758a24fc8d79ec0084f5296d3d3f389af5be1","impliedFormat":1},{"version":"816f4676a7f0521b45751530feb1da99a3553fac1dfceb44b099046b4f241544","impliedFormat":1},{"version":"e7cea9972cca905d58890f759b558b84111bdaa1694dd8f04173bb32e0fc6609","impliedFormat":1},{"version":"8e75753120195cce058c27a4fc1d1bd364c98188895ce0de18d72ec74259019c","impliedFormat":1},{"version":"29d877024e36f24df56056f7f9447ff3300f89375f83f99c067d6ee3bb61a73b","impliedFormat":1},{"version":"84737d32ba8b544fe0a9bbc7abb81225c348bdbf60a3484e439f5ccf150e6a21","impliedFormat":1},{"version":"00791a99dcf184e20342294940bd6c1347bef14fa212ca5205b674fda8e6e77f","impliedFormat":1},{"version":"19ab703c28eaa2916f416a57b7c3858b5fbbc48c02a78fb18c76ca0e561e25a1","impliedFormat":1},{"version":"de751db9f0aa22ab3c2ed5e3e5b041af7f5e849ccf1322c14feae5a3fa041e24","impliedFormat":1},{"version":"5506f93fed799ae0ab9b969d2802aec981864ae5a438fde411bbb947f5b7cb25","impliedFormat":1},{"version":"de3d741197565b49b8f82925ae19c85e5a33c6225013cb905bd7c81c8ad8843c","impliedFormat":1},{"version":"5f42b1318f1e3a30751839ec9dc8bff750a4996d9757083b8aa73c277437859b","impliedFormat":1},{"version":"a4ecb443bde398167e2708f94ecde85c474680135471a8744f315fbf40dc307c","impliedFormat":1},{"version":"698cf1f3dd82d97b63ccecdf1e03f966b0280514b97cf31774bca8b9afb19e39","impliedFormat":1},{"version":"fea565679a5fa428e926c7a9e58f09d30fc5a44b1da880647efbcee6eba1b87a","impliedFormat":1},{"version":"1207a20a196612f076c33d5d7439c6c0726b4ce57584759db1494bf10fd456ab","impliedFormat":1},{"version":"9e8d53296c9a5a416551494f9a02d18d75e0f782251aa86332dee4207d3b33fc","impliedFormat":1},{"version":"dfc9bdabd76caf74530773482374213b815ed3487bd9ee8c346da3a3f217bf3a","impliedFormat":1},{"version":"09f1a6bd37278453af46c6d90f31175bc861d85ac6f431e6eb6058b5d945446d","impliedFormat":1},{"version":"a57571c89df6ac15c7f142ccc273fb1c233de18199a9224472877edad5090de1","impliedFormat":1},{"version":"28bba2ebe72280267f47043ae770fb44c0b9c14abc71a15950bfefec753c6e3f","impliedFormat":1},{"version":"a985b356fe365e36c5549397bd2600bd92010c182ec4daf26057a9361eb19053","impliedFormat":1},{"version":"c884d330029844c2ee0d40393b00eb5184f897939e24ea8ca89cfcd37567a29f","impliedFormat":1},{"version":"f8a22ea1b15c4aa8b8eb71ae28ec3de8bcf9a5f1cae28eb5ce7035c980018c15","impliedFormat":1},{"version":"efd65b92bc63a8c2b7cc8ab116d9c4b673656d056d480cb975da96a4cfb753ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"3888580e5b0f064070ab8d713b8243861b215608c31080cacc428574854206b2","impliedFormat":1},{"version":"bf800f80818dcc4380151ad1c0fd7fdd3e6cd37be49287c712fe7b6506c572c4","impliedFormat":1},{"version":"21d601090c0135850fda0e6d5e3166c4523b041969a1f97bbc1aba1e968f084d","impliedFormat":1},{"version":"9de99571f4468d112ee8453462e13335672e4b72102d9f96596c6fe1c218318b","impliedFormat":1},{"version":"2057ab1ed9ed4aea453d7f48d3d995d54fd9e6c54b765e1b1a929ff8b5a75e17","impliedFormat":1},{"version":"212e4b57441198c05ea4eca4e5ebe1f0d1d30a88fe4277c63b1a17ba08e920a3","impliedFormat":1},{"version":"dc274bd65b77188d49e59ee204a0705a9f8c49431c15f6cefcb9a3928d1a8b52","impliedFormat":1},{"version":"172aa53f22d64f64a4f01dbd6673fff4acdd222b5968867521b2ccc7c3a2fa72","impliedFormat":1},{"version":"deb91ff4aaac0028c4c11666b445bfe512de11bfa0ee6f0b3e16062654ac1572","impliedFormat":1},{"version":"25df589bf92e6611d7b0eeaf78a00f9f89bed9e33559f12cc500ed1a2cb38bb6","impliedFormat":1},{"version":"83b29f8d47132069c6ccf47d3d389d4544c1a993d676f2c942838ad751ba90a4","impliedFormat":1},{"version":"46fdba15c7a90ebf37d27c51e051a4701e0075ba6b110c2daed57fdb8b749210","impliedFormat":1},{"version":"86e34ebc62d1513ef57e89f5e19e6a3fe571e1c9a3e8f364fca552e839f6c520","impliedFormat":1},{"version":"99f551a50dd44127b94fd6a9f0218d5ee7f7883d207722ea3538f469a9641f36","impliedFormat":1},{"version":"db063d3ec8175067198eb238e869662a795d4412e2498f32ea8349b163a8dd05","impliedFormat":1},{"version":"1c336b798915b89b810058b96f5e7fa7bc6c4bfa400e6bf863a4abd0f676478a","impliedFormat":1},{"version":"68bf537faa19a862ba87fabe0d991f90b1423780ff92cd2eadc67bdc0ba53b2b","impliedFormat":1},"156402d9d09ce9ed73a2e0b5450f622df688fd9d10d0c3af21c213c6561205cf","2b3c13f2a1ee9a15b9c5a3c1a2ceadb01ee95e9dfa4e58f879e2d10bc0ae9189","3a4f4d75e5d383ae79367bee02be2b9b0f1cae83432d8bee8e0499eb5dfee722","5f1b68e10ae4525739eb53b8a2f4e3400fb6321546d0a21fe5009969da2ebead","3e8585e629a766e21df7b8f687dc44f6309a2a45b2637a0b7740954267244c95","2050d15696d115116b2abf79cb9577811cc4722485cc06d90b6d6428fa30dfa1","db08d7a8f841c9f0ef9e8e1dce4918474072b235e35a3ecbe7dcf58a9c48f327","b8f5c1a7ee606f6a351a41919c366489cb1fdcd3d025ae233e13c19aa0ba8007",{"version":"e8e7db72a298245092d46b0f5957c0bf4b5ef8c31d849d82431f22c32b77cf30","impliedFormat":1},{"version":"fbe0b74882e6b44032f60be28dfe756ccd90c2a76d0a545f6cf7eadc8b1ccf2a","impliedFormat":1},{"version":"e431c2b334f9c1f822b4eb4cdc70f999ae4ccd3bce0b6bb93ad5e46ece08cbb0","impliedFormat":1},{"version":"c3e91c5161d6a6d5383e7866e20b088b09f47dbc6dc95a6e25588a6802d52cd3","impliedFormat":1},{"version":"d45bc498046ac0f0bc015424165a70d42724886e352e76ba1d460ebc431239a5","impliedFormat":1},{"version":"9f638d020ab5712be98b527859598539c36737e98a1a4954785d2eb7d9f8e6f8","impliedFormat":1},"e256a1b4e666a013dfe895487a552186a745c3cd0df624a97c7f52ee6bad277d","01101b835f37d94d187369699f0a3e368bbdfbb112f7664095fa06ba10574376","621dd67ef39e4cb6e552268254e8585c6ade1b1c2d90f5b7b57ac0953a317dd6","784d82984c9f8c62b8515d1e38a2f25c0c26973d33cc7d31d376e1d6c83d56d6","a82d6f8c15edc740623eb9e2b299a45fa24a0081126d0d327fbee5c5545efc1d","34d8cd69bb83e9f6625c7bebc1eae35bff1d16f3360ad9fe65a4eb21c897fa7c",{"version":"b690b03d8b01dd1aac93081b7142cc5ba18e207920df32da8ba98f77aacea90e","impliedFormat":99},{"version":"7c0f6284a028f79ab60a5438c7e69bd6c36fcd939e995a4cb6f2f094c2bc1c5c","impliedFormat":99},{"version":"1eb1836ca3ebc66730e250f3f067157a86b80e4d186a9210a870d0e944775c35","impliedFormat":99},{"version":"5cb740a65b7279340e8ea026b8df524f4ccfcc3b331d2d5548d8aca51ee31826","impliedFormat":99},{"version":"d26446e23aa9a59a1b554cb7c39010b0995b1b14636882e235d0d95a3f91df02","impliedFormat":99},{"version":"e04321a9c145eaf6fd5e73835ad40f93fb948c1d9b0a352a290dfd154d697d1f","impliedFormat":99},{"version":"9dab2c9c9670fd9f116d3b07305cfa64cddb5d6a9ea224c98ab1ea9c3164bf27","impliedFormat":99},{"version":"479d870cb73e3e04083652439d30ab53793d07579db1ad7b3377b6ed1242746e","impliedFormat":99},{"version":"06936d9beedb17d86a23413ee73c47a94bddb3b65fc0b966609b7bd4b37507ad","impliedFormat":99},{"version":"9f70bbf9e33344177fd3b8fe408baf36e503c20cedea051bfe6adff9874c8eab","impliedFormat":99},{"version":"0d0ae029e0eee0602d10c6b3a116531eb5454ef5c95ede99b6a90cc5bb83f0ac","impliedFormat":99},{"version":"5f232dd9dbb4b0afd6e5313b97025743ca5c659b7e8c0f3a230f2bfa8d319224","impliedFormat":99},{"version":"aa800564f2d16619271d018469b447ab3624c56a20151fa4546026dea4dcf5c6","impliedFormat":99},{"version":"1ce626b21ae7d634245a80e9702cba236ea9e63c5255224c3a1604ae0cd39fbf","impliedFormat":99},{"version":"1f1c8cbfd3dda3558e8ed6ebfe89e8049efade6a44befc81e9baadf5708adb85","impliedFormat":99},{"version":"f7ffdf631fe7abad1a2dac92863d2eb4066ce3385f9e028be4b5634773b6efa0","impliedFormat":99},{"version":"c7fe25e2e8987183922c0c43dbf5228ba401fcec29c07007d6bc0a30c2e260f3","impliedFormat":99},{"version":"bb3e81c607a389385984a00211e9398d9bb96e77e60c5a5fefb40ba6a7432baa","impliedFormat":99},{"version":"65380ac0a76da80ac021aab5f8eb81dbc74c527c6a990f87758f9e1c7a9cd554","impliedFormat":99},{"version":"3afa1621ea8831fed0490a60b8b48bbf025b0c0911184b7fa896ba7996e0b890","impliedFormat":99},{"version":"c70b2bff9d129a0a58c9827a63807a7d64b80f8f0c989f48effb66e7c67aa39c","impliedFormat":99},{"version":"3ee8d19136b9dbda738f727b1e2054bc80c413a513b95665087038e75f91673c","impliedFormat":99},{"version":"75a31bef921144614cf7b084f2d71e0d0dad5f611855b9ea124c7a85bc8a7a08","impliedFormat":99},{"version":"c2889799853dbf1e9f1d4807c545a43ef0ac706dc6719f05e439d87b7c74c7b1","impliedFormat":99},{"version":"6e433bb25f0700fe4fdb50c4d223cbcc2ef3b0aff20fad784bee214f5d034738","impliedFormat":99},{"version":"853f6b8328b7be874fe180b6a74c7271012412f276b7b70610c250b53f794bd7","impliedFormat":99},{"version":"683b6ce58145e69aebd9173c2188ad2c9c6ce5534e3c24e152262f1ddd777e46","impliedFormat":99},{"version":"76fd782173392b4cb52d05d0bb347a0bbe4f3389bc49fd3f741628b9f6a9e52c","impliedFormat":99},{"version":"5a980e1464eb0767b6623214b8ea3bf18f6131348cbed520d2cc6780f2c21436","impliedFormat":99},{"version":"965a714774de81675f22fa4ad804a2c5e89d947d48b4d15a6b4fee6f7b773604","impliedFormat":99},{"version":"7dc60303a93d4c7815944a797e2f3d60ea7b92f8b463345d1a631c092ecebd37","impliedFormat":99},{"version":"3f87beafd7399f50ffdb8404040a35c581d46bbe709e20cdf6d67271f6b1d275","impliedFormat":99},{"version":"268d7a81a7e04f02196f22a256f4cac46003e74a38a0c344eac87391a607acaa","impliedFormat":99},{"version":"f528cce946a949c183286b7097b07070b24e7563ae3f0e3a8373654e21ff4355","impliedFormat":99},{"version":"7a17b9960a11f41bc60abf9be3cc5dff341c418bc855d3c3414fe13c953b1a74","impliedFormat":99},{"version":"7eb141f38f596fe04e111e88fc77449c67d09ba7245337bb8cbc76f456471662","impliedFormat":99},{"version":"77b4ea07151dd0b7e752d2e9c8f73cf8c98149ff8c48b0842b417e74d5d2e0ba","impliedFormat":99},{"version":"97a875f68ec95cb7a66ada395b2869054dd6ae854fabf7a786ed8f0ef433bd32","impliedFormat":99},"63d05fae9362616b1fe9d6f0395e4aced7e704ce4faf2641247fee82a877f607",{"version":"72366ef1fa990929063122e7e15f579a61d6027ab8654c7437cdb6b929b4b241","impliedFormat":1},{"version":"7cceda8c6f7955121132870361f698884a5eeeeaddefe7413ac660b17bb3fe27","impliedFormat":1},{"version":"58ec5d8048b7dd32e6ad3a43a9c60b58272febb3bd54db408dba3aa639a08dce","impliedFormat":1},{"version":"9c25b2109ed9280da48f8e5a9564f011be8d2301c6b3a765ea2c61be4a092d8b","impliedFormat":1},{"version":"0867b56022d3c388aa093b6c575eea972c4197d62e942546803f275963c03073","impliedFormat":1},{"version":"b26c7b1a773c54cc9a740c84e67ef41c494fdc535b4b58b2c2664f6fc801a9e6","impliedFormat":1},{"version":"02dd08d47b068191b930ce5ab6a4f812eb2818d70030ff3e294482390eb90d83","impliedFormat":1},{"version":"7ebc8e06b3aca2e2af5438f56062ddd4664dfb6f0fdc19a3df50e1a383ed6753","impliedFormat":1},{"version":"5931ee44572dce86df73debec64b69c6766c5a85ca36560a42b9d27f80f44e79","impliedFormat":1},{"version":"fb939e898c8dbfb6eddcb77314d687f7879f12144fe4f50acdc70df1ec183640","impliedFormat":1},{"version":"cfaec796e5531757d3834e79ec66c78e3c4ac29e70e320ce1673ec20a59e3740","impliedFormat":1},{"version":"13ceb5c2bab5231ea85d17524665ee976dce8418c934af349735310da0631d2d","impliedFormat":1},{"version":"6d79cf4e077d63d540622641a9bd62cf20dfcf15dee24aa906e2a5fcbc14111f","impliedFormat":1},{"version":"4190e192a51f256a059a2f6643306550751d27e0fedfe7b5c1990a509528ec1d","impliedFormat":1},{"version":"bdb76953a3b8e77d8b2731d5811f0f8493a104b67117aa00507e50cb2eb1e727","impliedFormat":1},{"version":"395595f5fb22cd8084c9dc0e9ba7e204a98d2ce20c28571687aad9b0a11c8ec2","impliedFormat":1},{"version":"57eab48a7bb66bdd1831565faf6f99268899ec685c1d36823874c6b109beca2b","impliedFormat":1},{"version":"1b5fb867db1ecf42cae316f7f7af210ef0896fe43c397f7b5bc2dd485ab66ac0","impliedFormat":1},{"version":"810204c888046e4f1cfea3bcc183261be7630aad408e990b483c900aa7eb1da6","impliedFormat":1},{"version":"d3045fc8585220833efaa5e7341fc056d69e912fab3e2023c50a06c484776361","impliedFormat":1},{"version":"489443eb9ed0ec5d31335e3dde44a8d4e77e63521f2aa5b6ff65f0aeebf29877","impliedFormat":1},{"version":"3b8835f0ae1f3adfe79cab05bbe2c31452f48e7a2837931c4d2caa2709867862","impliedFormat":1},{"version":"881936bb56fc17b13eb2456084f3c054632f4aff7336b6cee058f374683a7349","impliedFormat":1},{"version":"439023fea7651b82aff011e852ed25bb5df9258c3a842d23ff848cefbde054c8","impliedFormat":1},{"version":"9301927e69fee77c414ccd0f39c3528e44bd32589500a361cabbeda3d7e74ba5","impliedFormat":1},{"version":"7bf076f117181ab5773413b22083f7caee4918ccb6cf792920efb97cda5179ce","impliedFormat":1},{"version":"be479eef7e8c67214d5ca11a9339ece2bbd25325ab86b336e5d3f51d0dac1986","impliedFormat":1},{"version":"83762a55c218caf67ee764e8a88244c6aa88073afefdd045f23723910bc6d418","impliedFormat":1},{"version":"639bdba9222a1d443eb01e3dedb7097c30aa1fb4b4d4d58e970a162255e8da0e","impliedFormat":1},{"version":"3ca75cdeffce7973fd95dcd5f75afb6367cc8b6434801c48a6d56d03f9d60408","impliedFormat":1},{"version":"cb93c3a5a607b023dbd2d73e600e297bf392957b6a180238f72ec88ae89f495b","impliedFormat":1},{"version":"32dc611ffb88c70b8cab36c2cf23b93476dcf99217902435f145d03e41081b6e","impliedFormat":1},{"version":"9b4c284371fc9b8ec060e6c61d31bec7897cba3c9a86370e8317e4038e077bf0","impliedFormat":1},{"version":"969b450418a39e16dc58b9376abc4a24f1e4f8277c9ec3bf462b36ddc5a6b855","impliedFormat":1},{"version":"b5b96fae5fec816f065b7401664ec69dd63ddd190c55c7d35a13c3a0ed020e91","impliedFormat":1},{"version":"20e3c35088d83915f5c903f1a81180388bc759ccc85cc684d0064e83442d6d65","impliedFormat":1},{"version":"3233a2c9caa676216934d2c914a33de5e5e699b3f0c287c2f1dfbb866bf761d0","impliedFormat":1},{"version":"05a83c01130e03d19fe78411895af517cb3d8985a0e84e70926418540853dbb1","impliedFormat":1},{"version":"302dc8440b85072dc5e1d30c39dc1d4ddda46ca5a10ff2d40b8d8e99fc665232","impliedFormat":1},{"version":"335bd16d540e601a8a3b80044b08043422b140c4d708b53834864659e6d5a295","impliedFormat":1},{"version":"ba0ea399a131ae764c0bda400c191bb82876e7ba01c3d201e5ba9edcb9bfb1ac","impliedFormat":1},{"version":"d2dd9601857d3cfc3b7da4c37f4492a6cf3efbf4c803a9d31a0ac0a766b9f496","impliedFormat":1},{"version":"68f204992bd1fe55fd0e77e79820c3202157b76fd9808c77358f97a25638474e","impliedFormat":1},{"version":"57f0161f1bb74573e21c738b229ead3b64ebf66d4d4cddb74b620aca1193429b","impliedFormat":1},{"version":"5da72db7084e8d880093f1ea208b3e1fbdbc0b92422556eecda88025e4e98859","impliedFormat":1},{"version":"e1aba05417821fb32851f02295e4de4d6fe991d6917cf03a12682c92949c8d04","impliedFormat":1},{"version":"63cf8b8bd4de61b04c19ea77db82c7e4afc057e3208130bc1886c3918d9e1dfe","impliedFormat":1},{"version":"6d7bdae4d2049d8af88dc233a5b277ed96079cb524c408765ad3d95951215fc0","impliedFormat":1},{"version":"504c1426e4d5f7b8c56526ba8bf228baa2431b29002182546b14e2710d5b538d","impliedFormat":1},{"version":"d43d05f2b63a0a66e72b879c48557a54b4054b17cc9ee36342f41df923f15ffa","impliedFormat":1},{"version":"13ed0ea58b434c9a1cb87545a1f4c7a6611ef9c180c03333f65387b2525d068d","impliedFormat":1},{"version":"f314ac2c5a7d33f9147f0d3e4bc8f80db29d135977c4784ed9c6b8d1c8b870bf","impliedFormat":1},{"version":"3c9709e0ff5fe0d928cce5e735a6d3cb5e36caf04acc685d77438c4410b783bf","impliedFormat":1},{"version":"63ebfb0a8d32d6aed76c219eeb9b51f5e94c575ec659deaa99e8f41e13ccad0a","impliedFormat":1},{"version":"b6678585be337f0524bba99bd932248344f7d077ca22dd9f59ddca36d85dca84","impliedFormat":1},{"version":"50fa4532e87f95ee828ae32882b352fb6a5707eb09f09c56824266ce8a8c71e1","impliedFormat":1},{"version":"4bb4c3174734ab7664012245546a9d78c8e07f1b792025d1df95705fe00b6198","impliedFormat":1},"f59aac6b7d74e4567aed792bb17636c4544cfaf2631bcb4e80bf985f87954e4d","2c3df4f83400f4cda98d81dbbf276838fdb49f28f36ce53bcd2373ecc519c526",{"version":"c2a6a737189ced24ffe0634e9239b087e4c26378d0490f95141b9b9b042b746c","impliedFormat":1},"f16a780506623c7aa4fb64b85178e80d5631015e430e81d4e438d20d2a608261","794b80432a7e75b8a1620a95668a3d5dd9376d1d63379e1ac312c879f02650a3",{"version":"46418d312a216198ad4408dffbf5b37ad773e20ccc7a70d2a2f154d7dc9e0b96","impliedFormat":99},{"version":"83aee4f92c2b56946a745c8900c0812ab64a28ac977b9a88c4d1c3ec2a7c7045","impliedFormat":99},{"version":"979f5b6e6bdd81c52cc7f8befb9a57709b349aaf36d096a6316ab424329cb565","impliedFormat":99},"e63c73b4591de649852ec2d25dc89f977e86367f3211734b475c7908243ad832","d67f7a03141eec7e6c71278f8d0fd673d2200a6200239c7c44f96e33688cba2d","bb0a6694bb055d0cdb459277c465863cc8a7fe2279877f091e5f7d5daa5d7143",{"version":"af6d65360e07dbb1297607024d22a2037991e7c27fa1383a9a472a9217346ac6","affectsGlobalScope":true},"d458fb51e2cab8f377690d53e03ad56f0fa12878ab3184778e58fd81989bcfb6","dbda4df415b2a871ce494efddf1cfc00c6b0e1d8b795005898a3eb2558ead76e","09ff560d57d3c502df5fdbd56b583295db613662f587c58d9e03f153ac7e4bd9","fe671b3198299f5cdb43cadb859516473fc6e62c3590f90edc619ef98e642d3b","fe092427cfe15ce644ba1b972aab606632e7db52addb3b77f87f1c835a9b2ccf","44c7b148e5cd655191bffae4086c0af6212c406ba33327098ce6c9f60e5c0f56","769484fd9ad321c00c27d7204bcb652d3d8eb3bd0f593878fe3eae327012c602","9df797c4456c4101e552a86556556c41f4a2e7f7195b80db472dfc003296de61","666bb9ba1bac71fc04eaf16e214ec2add6b09912fb9fe5d0b6edc70f4fb138ee","2cd428dcd45471b70a223fa9742de114460a991caf9b959d7adad34077c7bcb4","86fb9fcacf352a2c2acbd6816cc596ee77ef8dd01fbf55d47b9d7da64bb9befd","120d83c226f606e2552a7b7607f475ec1a006931458da7f3ec068cd3bbb9dc85","9b4833baabdf0f598feb8f5ad8a9d9b3446b9f0d5bbf60e3424553134fa3a983",{"version":"a74a808879b3ea840ec6ae884050f3d5a7eabf5327c7a3461adba26f091157da","impliedFormat":1},{"version":"c6375ec5035175971764637bc6b6652541964eeacccc3e69a8b19d6397c9e7e0","impliedFormat":1},{"version":"5b1978184b5bb6665bb958606fa7025785d53e029ceb7f3063865761b6c36733","impliedFormat":1},"d490a210a0a755be2dfd96b85509d1760c49582e98c8df4ae9ab634b7f102f58",{"version":"587556a5a7a595e4f084c042bed36c377474d28cbb2a43db0df17833e95b4ac8","impliedFormat":1},{"version":"9a75a9edb563ba6eb31439a1c2af004e77caaec9bb36f232483cf23bae6e3855","impliedFormat":1},{"version":"cdadd819578089988f3fc1f6ddbfa56664b4bed3d2d10910ce06405e7a3c4862","impliedFormat":1},"1fa0601156056f06ca4c3f1c04c6b522ca7c4aa0f5ff5eb5831c174dde1beaf6","af1862a591e8cf8222d1b2ed76ba193af6f24830d6180c2d5847cb4343a8048e","07c1feea954b419410a685b8fb2bf2b3a93dbf137389860b85245f5e66656795","cb7864038f7dabbeeabf7241e3e8b98663ec835135d805352fbe3679345c341c","ae363f9b2f8b841a5d3d837c6a68cf99372091801f014a68b6f63a177455cac8","acfb807a12ba1d06db98205b8eab52ce7c3fa4034f6b5d906b8d6e830b7b818a",{"version":"9ded2554968ccaf60ed3e44f263b8b89effc0b9a3b2f0e812bb0edee31b7be7c","affectsGlobalScope":true},{"version":"73f0651dadc2c51a4526c25b944796bf9632a29234d9d48d4d0674ce592903f3","affectsGlobalScope":true},{"version":"f823c8dc4c799b3f536b07879bfde259c6edeb03c1f6de379ad01a2280875502","affectsGlobalScope":true,"impliedFormat":1},{"version":"12cf104bd89efbb51c4c62b28556653fe285225931aaf6ab8a61dbbe9f399b21","impliedFormat":1},{"version":"6d208c1116f68866f527cf872c9a64f0b88ab5c18f91fb7523575660c01838d8","impliedFormat":1},{"version":"ba4bef7e760ab98e965a447fb9df64db1d67eaf737477197d5998bf3973ab105","impliedFormat":1},{"version":"62e6321f778d39b3399d9a95452430abab1a63b48be4fa26053e49ab5d13fb38","affectsGlobalScope":true,"impliedFormat":1},{"version":"35bb6a2809c76d995636dd931318665e50cc219a7a90d13c86d0ce8ee206cfe6","impliedFormat":1},{"version":"b7eb2717845b7591cf4cd895e6a9d683ef2da470b63d0c47c522431a422af9f0","affectsGlobalScope":true,"impliedFormat":1},{"version":"f79c71cfc2124a525167e14fe6e5dcd017cc8445725df147dd8db8fdac4f34ec","impliedFormat":1},{"version":"deb95beae1447e1d3637a668f348cbf45d5af93824da00da8b55ca7cbd37a3d3","impliedFormat":1},{"version":"ae345b2262d8511c811bd68681f51d99b337e0418e681aabec7edcddf8104d31","impliedFormat":1},{"version":"8d7d800756d81cadb4e1c8074775a9bee51c0102551bd43ea50127491a773237","impliedFormat":1},{"version":"c04ca7355e5fff4c9ed072abbe0a34d73a0d59bcbe1146060fe1e09c3a1ae4e2","impliedFormat":1},{"version":"dccf72099c180a16937a39be8c2005c11f25c0d2fe83f6bfc013cdaba9fbb3a3","impliedFormat":1},{"version":"6fef6366f8856dab93cdf5ca4c9544da1664ea45c5dd1a2cdb4bdb9ff62ec5bb","impliedFormat":1},{"version":"9ccfe61a2ee1aa330392795362865adadeca06d90b52432b39dbd45ed1beaea3","impliedFormat":1},{"version":"4e0a1b11a0beb7ab32f4000291a1f88e99921def4fead97642dcf02aea735286","impliedFormat":1},{"version":"8a19aa91c88a55d81072f5118aa9261bad2c7167aefcd0a5e909f8003dc80ad5","impliedFormat":1},{"version":"2ba193e017bb5fb818671fe7967b92c52ea858ae48de7c9c2319d56412b7b35f","impliedFormat":1},{"version":"e807735c732ca0ea41851e241716c71658851585b1949e7e197881b940257cd2","impliedFormat":1},{"version":"6e960c1f12c8db025a91f4d02f30459978b5d5d21f18b277c8526d38fd6c3ffd","impliedFormat":1},{"version":"83b6dd2c65bd89f1e14b6eec1b8348bd71c6f70b635c6b56adb43e9044a30d36","impliedFormat":1},{"version":"86788b76237cb1aba1b3dd76df0480a6ad6f5de335cc899369ba5af65b14572d","impliedFormat":1},{"version":"9bbb9beb378b9412af211ac4a7af4ec6748218aa6af6319f61f1bc17a51fd503","impliedFormat":1},{"version":"0fcc14b6f7e0f0a9ac5dbf6e41a0867668b9c46e60a59368759e0cdff81d153b","impliedFormat":1},{"version":"5efd255d8c43911b6957b192830ac83fbf329c2d8a9ff0ce3619f01174bf7dae","impliedFormat":1},{"version":"5123a67d0cec83114f95cc19757ce25595535fe1e54c919354c4eef54f1ab35a","impliedFormat":1},{"version":"f683025e85c1f853a8d53ac6376dac32ae21cbc23cca2e11fd47de772f87802a","impliedFormat":1},{"version":"d06df8e4eb43f506224a85a7f5b732c0d5a9dca85833c199c4448f31ac603bd0","impliedFormat":1},{"version":"d1db25f3ab80e7e3fda807fa65c92a7e9b5cb3f9d38e7fe7f4d844e53170882d","impliedFormat":1},{"version":"db4c40b502938b21ba8784c36495c3b5e41430d7f3cacabcc5087bc5f20f9801","impliedFormat":1},{"version":"ba9b97bd6fe933b8e096be83ceb8456faeb97d537e814023c3191e9d2a34d86f","impliedFormat":1},{"version":"ab116fa8def87e765a542d2ef841af846c72a89646b14885f8c5db731b4d4d0e","impliedFormat":1},{"version":"57685668c903be69a1188c6e084eba2c21db5aa9b31d68077902774118867848","impliedFormat":1},{"version":"01d4eca92a63a708663e06cbb281f9fa3ed802c49250f8f75faf06ae99adc2f0","impliedFormat":1},{"version":"a752d1dd37178bbee52411398c60d87947288f566a508b433996d9539c5c08d1","impliedFormat":1},{"version":"960d66b94f1f656aa137022a744cd02cd58c4496a0c1fd52844e08d8dd4ecd3a","impliedFormat":1},{"version":"f61be57fd337ef9f86ff18bda61e5ea6002829ea72c093d9256ff6cbf139a993","impliedFormat":1},{"version":"1eef3816629f8d4da6556b62bc06197ff2b6a9d1954283f445a988e4f61ea883","impliedFormat":1},{"version":"e965ad1e16b2a83b89c67f761b86919aa189c120dafad71e4a90645668a00b12","impliedFormat":1},{"version":"5b7c4f9b0b265c15992e6c8c369418e310de949d799e39bd197d7ea25ffde27f","impliedFormat":1},{"version":"eaf1070ccc065310f73e173966cb3564d5a826eaa199b65d6d5edd965bcb576b","impliedFormat":1},{"version":"2662adec7ca9540bc850afe0e39d57e5a045edfc0adf0c4698269c664371b273","impliedFormat":1},{"version":"59fba174f784e200f997c0142a19146f027f9f5761fe507c0d33d3650effdd88","impliedFormat":1},{"version":"0262d35abbbd434f003c125f820ec13d82a53bd23df19df25cd5e979bef79b5b","impliedFormat":1},{"version":"c8165ac655c9f66fd1ff3dc3a12be1d7a661cc3153cb5ad5b13596532c777e45","impliedFormat":1},{"version":"4b26c57fbdc2f1e810853110cfec7f0e98fc4f0224e7077387e5af37e4982260","impliedFormat":1},{"version":"03ad6f92a7cf0b9fba54e26a9e401cd295feabaa04c55bb560bb12ac9eaa407f","impliedFormat":1},{"version":"efefc9361969b4b9795e4e9fb0a840679812e9fdb0b9aa381d3a001338c72bb4","impliedFormat":1},{"version":"ec554830499b62c4a04539335fd34c47f0976d986387327e6d0141e98b021421","impliedFormat":1},{"version":"40368fa6b56a4d6422a30999acfa05e86ebdae527bf63bafc8219b5f5f66b35c","impliedFormat":1},{"version":"057db04c0903d717f59f06b9c9059ea57909dc39523da1d0313b458dc7118092","impliedFormat":1},{"version":"f1e15b2e63492c51a01bdd6a01f747e45ee845346ec5cc1e54a298bebc259c18","impliedFormat":1},{"version":"e716583445254cc246c5a7fd08dce7e75e397cb222ba40ebac68d3b7b5a9eeab","impliedFormat":1},{"version":"58f3e6e444a445b461d4ee014b162498b4a5312594a5e5310e46208f306f8ffe","impliedFormat":1},{"version":"bb556f133d6dff6cec2de839d266906796fbb3b1a65cd2d9bc688b9ee23b3764","impliedFormat":1},{"version":"ba586069d4925f2f185eab94838cb3805dbe7f5344319bd74debd914e5c7f7d4","impliedFormat":1},{"version":"cff6d4f3a624ac3d5af3ea4d753a9c9ec3e8ebe66db11637eff4cf48fa48300f","impliedFormat":1},{"version":"8741ec6510be29843155db55e72aefae326239a2c8a7fbdedd22631d7f3d1f88","impliedFormat":1},{"version":"a50aad4e8a8ce2d502ac38448acf674bf007e07da463db35ce7d905974fe66c4","impliedFormat":1},{"version":"f16524f233140594964c4ba52c41dc06fb482e14bf7a33dabc0a4ae6dc9e7405","impliedFormat":1},{"version":"f15fd72ce694096c9bab1be1ef971f60236160237d517d48d759b2d4d536fe39","impliedFormat":1},{"version":"e883a8fadcfdaf1ebfea0d8629b5d68cbfcb5e6a7e3f67818fc55dc2d8890501","impliedFormat":1},{"version":"4f50b8dafdbf0cfe7d0c1c470f2f2960976db556beccc9869bc52fd93782d1d9","impliedFormat":1},{"version":"ad6453888092f38a08402aadad22a58c3e91f40ed321d2a067f210b204e7b9d4","impliedFormat":1},{"version":"5084863968a008fe0c4dcf6e92ac891ec7e1d879b403a0cbf6d52d6959a99d6b","impliedFormat":1},{"version":"e8c91490d7790c4bc17df467010e9673d662317e8a1f3a78e4a7e983d46033d1","impliedFormat":1},{"version":"a2fc1956e452bea48b72e47bb6cb2be72de245fe3bc3a4d4e7e7e92d1d9fbcee","impliedFormat":1},{"version":"ac414ec1d2e4b3f41f0544dc68e4b92a45006f55ca449ec7425ce0a6a58ae803","impliedFormat":1},{"version":"4c827319cd298b80c5025873b1eb13d3f1e70b45cb0d361238a66c1e470e5a44","impliedFormat":1},{"version":"86cc49382978e60cac8556b19705ddb729b364dbd7c870ba75fb82020dc375f5","impliedFormat":1},{"version":"873360cb0c75f2d64fa697e25ab8f49f3c727cd7741ed5d8c65123b3d400876d","impliedFormat":1},{"version":"6c1bbf098e683315cb1635c8441638f8fa88659b554735ccc02c27a96d2f1c1a","impliedFormat":1},{"version":"e4faf1f8b2620443764cb8e6df0cabf17e5317377114b858979cdf58c61ae077","impliedFormat":1},{"version":"fda31991988a5aed07fb96194bb981c72ab481ebacec11221c8c8e54cb5b6533","impliedFormat":1},{"version":"60b8ad4ad909e31e5bfb76b09f388901009b0c422b695b20c0d7804e7c54fd1f","impliedFormat":1},{"version":"5adc2df05863f934c865882e641fc4d30baa977a4a55737dd0486c31f20a38dc","impliedFormat":1},{"version":"4642b2e749955bc2c765a22548f1b3fd6c30405b5fa329eec755cadaecff63d9","impliedFormat":1},{"version":"e0806d68246c3a6ad8cf32cb64d6935d709cb5eb776569055824422b91de8c07","impliedFormat":1},{"version":"630eddd094ad3077258f3711663c7f74a076f1bd30f5b957b061642732294be8","impliedFormat":1},{"version":"9ecf74a630a8dc9bec7e07f1380f680f1559933288646cfa0bf1c8d045a5f22b","impliedFormat":1},{"version":"06112ebfa5170da89b69243dfa93e2ba2ca539ce4d9f224e8e715bc85e029e9b","impliedFormat":1},{"version":"447ed1f88fe870d0f0ebb9c9cd6bdd8d85f256d43215e211e702a96f6e48c55d","impliedFormat":1},{"version":"fb981aed4f66b2ae56ba13cdc1f641e5fbb4071168e45e65d15ad3557e5c8224","impliedFormat":1},{"version":"e2b71fc3469354b0507d5b1e2d1c37546f1dbfded286ca3bf412cad18930c4ad","impliedFormat":1},{"version":"0e37e1a15b5208565be3cea374d151b200ce43a798ef8d602e774767c7fe0b4f","impliedFormat":1},{"version":"58e6d1c961370f5dadb19b72fbd5806379895e5b90f0e80438e642e3b9709547","impliedFormat":1},{"version":"7d04240ff7c086ba0bc93f35dbba5cf605ae0edd772ac10953fa73a59efcd67f","impliedFormat":1},{"version":"ee6e2dc0c184173417cfbcaed4ca0749783e348b5abc0739e2786141ba64f22d","impliedFormat":1},{"version":"d961fdbe6c9c94fc25366b64d2fb755e74fef0e959f6a5f627643a8fc854af1a","impliedFormat":1},{"version":"7c06e3006e2663134a97bbb31cdce26047477141cfb54f0850d7b5d270d0ea8e","impliedFormat":1},{"version":"d7c4c49f1ecacf78f65730dc693e2d96d02116c91ba75edd9313375169efa1e6","impliedFormat":1},{"version":"3ddf6465a527f848c178d9a0bcd7400a6900b0586d1fdccb248aae0b60d2e5b8","impliedFormat":1},{"version":"c4eedb91af7c1085d7b18f252289b68b37f05a8aee92b5392cb200a791d981ad","impliedFormat":1},{"version":"dba49a1467849e7a0c0e4a80f63f7e3ee639173f4a0711e0606610055497ec1f","impliedFormat":1},{"version":"9fe08467abbe684082372e42d5f7c823b992eb866aed14cc2fa1cf3ab3638b13","impliedFormat":1},{"version":"c11c0a88a6ced0cbf9c7dd677f611ddbbd61e1e9896a2401ba06ca01d9af77b8","impliedFormat":1},{"version":"48c6d58d919cf6c9354f638dd243c6ba9c94deeb24d4e7372e9998cbf71c687f","impliedFormat":1},{"version":"11d546a505f70f9c5f8092916027d8045c280a817b709fcaf2c4e63fa026c89c","impliedFormat":1},{"version":"78c6838aa430ac7b4ddd160c45df4df237a9b0be5be3e24d73a92c604854ff32","impliedFormat":1},{"version":"7e125d9abc19f62d1480f6c04a45d7bb2c89153316245ae8b8e5a0234b078c4e","impliedFormat":1},{"version":"dae97d701d722e050b0c0b39d1b76750388da3bd63fa5d4f0024bfd5c9ee8687","impliedFormat":1},{"version":"0c354c81598f8dda1f57229f659d2d5f8ffc3a25a206a335b87faa63007791b0","impliedFormat":1},{"version":"8ccf88de28fc84428c3e4767c9b6579e1ad2b3a0d3805aa17ec45c6ad946b649","impliedFormat":1},{"version":"9bbe3a26ccc74b90eaefbfab523fb7b3ef7ebe5a8f7b3ae178bc1bb730107ab7","impliedFormat":1},{"version":"0ca09d92d6469d906a3d1c7192a6294c7f65b75f4f7eb8072bbd1b68c7f021e1","impliedFormat":1},{"version":"64e14470a61b50cd9fdee75a3c8c20c7d92c2f70ee576f9613ad83d587358dfd","impliedFormat":1},{"version":"b1d115b94eb747afaa432029fdcf61be130535596bf26932b34be685bb73d458","impliedFormat":1},{"version":"a9a006050ea7f190d4e04ec5f1157a97c3e92458a3ab746f1c29612f25f80e1f","impliedFormat":1},{"version":"fadcf6890b2b76d22fb63671f7c91943fd290098961b992befdedb53f941cb9a","impliedFormat":1},{"version":"d57affbfa6a544693c4cc898355fdffc35a560138a2d3903e20ab87428b3f9d4","impliedFormat":1},{"version":"0292c3ac727d8b193cbf48c30befa9b2174a22605af23eeabb2397ccac2849ca","impliedFormat":1},{"version":"0927f3fcfed55acc0f645dd627ed835c977a448fb2c609ed0d17cc665908545d","impliedFormat":1},{"version":"a00f2492037d8038646481c76520b3c80b207a6a2bcab7ab910fe510a1a9b778","impliedFormat":1},{"version":"f9b96f053630160fd6f336ba1a21668abe5234727f8354ad594038d0693f5e7c","impliedFormat":1},{"version":"d1671a7f33f9bbbbfee9f965fbe365069bba829eea1124a0e71dcb3557571cef","impliedFormat":1},{"version":"7d21fd816566ffc78ac1614fe0d999528bf6103849e13bea0e850752e4b74943","impliedFormat":1},{"version":"e31d4295eece865b04b072e94685b17eb9b5e1d456da7af26f63f6f0018d5543","impliedFormat":1},{"version":"07c93f85acce96b5c0386de1d28a50c000c96ffcac7a54682033b3be8351b2ae","impliedFormat":1},{"version":"fc8c5094207694c2afce5044203f17caf752b55cc560af26e139e24af211e763","impliedFormat":1},{"version":"e1488747f4ba51f3b3e58f2867603274b08e958cf3a6e43e229e7ff095abd6e2","impliedFormat":1},{"version":"d5a20f097baacc8f460c1836c3680ac5c58335e80d04370e6156d09130fd4fb3","impliedFormat":1},{"version":"5ba921cebdf9da9db79ee0df3f2cd6fab3ad6e0c3a3ee8aaa083d3c39a300feb","impliedFormat":1},{"version":"a7bd692b6dcffabd7a323ede418115039443767f4dd064f03ccbba45bec713ae","impliedFormat":1},{"version":"e330873013270286665840cac809727045b68bea7a6cbfee687ffae566e47038","impliedFormat":1},{"version":"5a3c0aefe0e17923748df62ae581d1c4b5588de344fac6db0622e9b9cc3a99bf","impliedFormat":1},"47313d29bda47aeec137827aa9e15b543b9cc36e934f20ce3dc0d16090a7462f",{"version":"2d18d18845f032e835faa89ad458755018a0b6e2066d902be8dcc2c1bba630d2","impliedFormat":99},{"version":"23adf9a691e233c6579a95e53ffff9b0ee035731bf8dd01145222549e5814fc8","impliedFormat":99},{"version":"fd2e079a8fc0a0b5bba05769c22bff04cd8325d53e8a68b07628cd8f425a558c","impliedFormat":99},{"version":"dc1fca449fd128b7015bbc3cc3ee768b4f40f439699f89e75992aec7ea78ea72","impliedFormat":99},{"version":"a57e8f59b10e57cd3afaac6098d644264e8b19004502233d86b085cdbcf1230c","impliedFormat":99},{"version":"7bd1ffb1e61afdbd2a4a4ca21767b465e7d8ced48fb395882b2534300056d879","impliedFormat":99},{"version":"6a0362405febec3aced42db1e26697db2d1fd8a4f1d426749dd9540272429f49","impliedFormat":99},{"version":"013df4383dbbbcc1a39bc4846045c57531f4b3fd8994892cc7e4964873741156","impliedFormat":99},{"version":"d22c2149bd155a6d4edcef442164facc22cff96426086e8799969b59632d409d","impliedFormat":99},{"version":"00907be752ca34e4051876a7617526dd63e6ef3340b2113ec7ef3999d178e77e","impliedFormat":99},{"version":"052483ad9c28956f08ae4b71adbd00b83b956d8c780086c62ac30b18ba1208c6","impliedFormat":99},{"version":"e1b6f0ed233dd871ec68aba261b65bcc2fbad14fa61f1e037f10ece543c14e99","impliedFormat":99},{"version":"e37034ad022c806eb14b8c5441221e0e5b8002e497d0966c72ab2afcaffbea0b","impliedFormat":99},{"version":"c113f927ddf1590ed18a9956083b72ca33b975c29601297557767d2387d74daa","affectsGlobalScope":true},"d7140f73c76a56d43cbd2bff7ffbaf3545fb50744f2f93982adec0f5711b9099","5d2aae8c9d58e919a55c98b032f629489f97cde6012625037bc663153a796d7c","34fdd8ef78beefc9d61d695225b14ad4b5951ef65ad9e151475228db7840fa14","5f4a75e10fd0f416b2ed87b873fa9bd90dadcaecfe68ca019ba655ca87134937","470114cedd442861239a6d699674b5ab6284f04d579cd1134879423c6cd821b6","012e0b6c8804412b161490386697b9e07b571602c4d89d80ce675cd2ce0284db","338e3055f6c55df5d101dc00ffcdcbda096bea8e9b22d493c378d9f214bcac66","33b5d9fefc76760d0862bc2b7b5b91187ffd3b4f0b97b8ee536c19a82ec2cc5a","765ed25083cdc541d83dcc17f3e2d233fe25893afff287ebd657fd38bd905090","b0d2addfaea54d610cb9ff7dc2e6d39878ddff7e5ee355fd3f9d0f554257d483","2284c64ef85c4338028dbcf549d5a67633fc8f3b2b92ed44f82954e720f8bea0","42336c27b8f71542c7f8bc867a0b2443a253c4d54fbfc3d16651783d2f989897","7bfa12c5c6c56ec614e4e1c328befe6612fee740dccf4376e003edcfd4cfb51f","c46bd51896eb521ab2fe8220bf170f9dae01bdafe749d0eabf38b3c2dc227738","06a195d8dfa531ab1a2c506aac99cb3b1d2d64eb92777d0a74418d856eb1284b","9d3db9e488bf4259a1da3376df15e17b4ddb794908d1d8ece48e0b6a7b947eb4","15de2e2dcc03caa48c51028263ec5a4100c4affbcff18575a4f7ff69fb227e95","7cf77aefbdd07d2a9c05a51a76b2df037c6a3ba83a7d2bc05c9f0e029610ceb9","503c2d743a40603481f6fcf342262f783d2118949fbc43076966a01e652fd063","e2ac7496bd8b5eca921574f6e47b3cfd032f406b5b417e25356a8b6a99062a54","036035cb6273b449677d4cd284418d17a90e597265c51f07adf8d31163994a07","eb405d210fd3d0ef8fee7949136ec5ca5ca0cf68c6bb7c743359776b6ac06008","fb6d2e4d8d1500658979660c7ad6327c7040b66b0006bd86e0e5f2e0c9b0b763","b3a4d8587d5e4a24cbe332364940b774f94858556950daa2f9df75a87e4ca76d","5d953e94deb98fedbf6e629b625d9172f109ee926d8579bd9a0ce264f715fbda","453b15c24ccd0679bff1f62071309413878da2f318214c8c9a46081dc6119231","1f956b3858017b0367feed21c102723058faa909cc8b050763a4151c2abd9ab5","e88695f42ea8e9fcdafb618975c9ffc151110678de4994da8e0511b635d55991","0b555ba98e9c5c953ca4316253ef735dca4ca4ac9f9104bc9ea21427546a8cb3","92fbf33b12d535a53c48b82e4fe0b1670fe38b33924fb5f5e4f9daaf5cfbccdc","be5f679823d882bf675427abba913dd6155bf767cedbe1ebac2f4c75ed4f0228","3958d2fc6d2d9e366d5bc322eddfb8ac79a527948a424e9a29e3ced621485d77","e003a80502cc8752363423b7de03e0a8ecd6481ec92a7d0d4af74c957ea8b3c8","53d96005fe5330396e6fc8c360bbe03fe443d98f52c2cda163fb56ce969b5773","cc512bf5537f2616b5b6695c2dcf41754582e608143203f8158c4dedd918f1de","f5689079fc08f15f3f1209a13ffa97d1f6dbb1596ad930c18dca9d89b77319af","2240ccd7380d225bce90cbcd0588ab3dbfac2c893cb4e8a39d2ae97627ddc268",{"version":"a82f5ba70d26e6ad9d432ab7556506d807c49f35a44af99fb8054004220aaaa1","impliedFormat":1},"617db6908fb0cd2e793f83b3a8420eb8cab6faac35dab3a42fe86ec0d9cbcb1d",{"version":"cceee8cfc7a5f6ee4437f533d3cfa4cab7e7b188531de95ba26eb7b798cce778","impliedFormat":1},{"version":"37660e4d79e29eeabc91d4fe9e0444bf30aba56a93029a5c239131ca935eef27","impliedFormat":1},{"version":"a2e61045dd25dc0ee87d1d4bb057bebb7c5d0c4c74b4cc863c1d4031760e9bf2","impliedFormat":1},"59b8b86859fb60afd5e5bfcc46285ec26cea66c698d9370d97f717092f079f2c","a7f9c5c7b1edbfd4dfbcffdb024d38375fd56489b1964bd19faf0523907b267b","5ce92a17509ffea86de21dd21bad322f927bb8957cd9d76a57f0f6586ae80738","606a0c448a1e3d9b91a73bbdb7e88bb1bc513a606d134ed78690af65c2ee422b","3cd55d79660565fc9f1ece0f7eaea82d47597713fd0753cb3bb97e676d6a4b60","731f4d1b7ea1e400a81fff5efd6cb72d95ada5828f3f74503b09a555f3e7da9b","78d63ce9edc54f7acf678106c4c9db69a203b3cbd3a5dbf4ab9b993e2a206c66","9732a74825bd7a2e6569101fd006f72f448bf8fae2bbe3fb64c7a7a4bacb6b0e",{"version":"9620e0f8e0e9eaab580c0a58975c2cceff1e3c849d751803d10ce638ccc7d79f","impliedFormat":1},{"version":"93c8703280162e66aee0ea629d838f59c732c0d2ef672570022ec76a4bb1390c","impliedFormat":1},{"version":"dde6819cf8168c882d679c880d81efbfe94f995d15882db11573fc72f11911bf","impliedFormat":1},{"version":"d8f273a609dcdbc0a83492c451d4e84e5ccfde686b08d7b6fc5f2f6a5691cbf8","impliedFormat":1},{"version":"4d4fcd4e77259e9bc769d04585e0c7208a0426c9bdf507f5c312ac763c0fe57d","impliedFormat":1},{"version":"e1fd2f8a67f7de6298696c0658a8fa22fa90401e194bbc4e6fff72c3f991e4ad","impliedFormat":1},{"version":"630496bb99f5b48a36e0992b837778a0fab9ee007fd2688007c371f33b8fe1df","impliedFormat":1},{"version":"ad946bf7060d27790e18fb7bbacb1a0e41e7a9ce9d3dfbe2616f076f9854976b","impliedFormat":1},{"version":"5ca251e0e0e0dd63d358e3c1a0a127d7c1b29c74b1f6b453b0337b53ed27e83a","impliedFormat":1},{"version":"e47d9ea0c1f685c8bc22af368bfab9b8d7a597a46c0caf5f94f986b8ae378e7f","impliedFormat":1},{"version":"6dd37bad0dcfb23fb6b2e1cb562aa11bfd3690e5195e912d296fe4c55bae0130","impliedFormat":1},{"version":"72b55ee92c4bcca2e06df885785cf2ff1a6be597dadf35b838aa35d0ba474c78","impliedFormat":1},{"version":"e5da8e383389e445fed720e1173810df63e0887e6214257c984b496d6338639c","impliedFormat":1},{"version":"98ea1f69ceadcaabbc7d3ecebcca7812fbcecd357cad4d580ed590107c5f6190","impliedFormat":1},{"version":"784ea15b7d316235e9c0c5c389b03c7f1b4c4ebeae43775874a24d7515f54d8d","impliedFormat":1},{"version":"d0cb9f970a6c9ecc3f207b0ff78f2c9b362bb5dd884eea8f293c9f4a313164c8","impliedFormat":1},{"version":"13901d6ae6a46b2a00c31ea4642e97a132890482ded15f1cb5aaf75e9a1cd12c","impliedFormat":1},{"version":"703c7e1672aa6bed30995e7f8957f5d2d6185f81f58c0981ce01eda8e8cc0688","impliedFormat":1},{"version":"718a8901abf31edd5d7ce45b4bd9685ecced9b2e7f63358e75ce4cbd5975bf96","impliedFormat":1},{"version":"04abab10474ee8525c0a75279b148f003f087e96add3a530b53b4ba03e6cfef2","impliedFormat":1},{"version":"b511a2cd2beb613dcab7d7dade28043a585daae27d4a3b3f090e8de755eacc77","impliedFormat":1},{"version":"7ee3dfdb7936a08932acc76881e42e8835f58d54ccf05914be6c681a5e2dc67a","impliedFormat":1},{"version":"fbd4252743bf7c516bee742646cf63378684ac4cf81a3c1fbe042ef92c3c4609","impliedFormat":1},{"version":"2cbb70655bc3d6f61fa4337756ca0f0e8cf9b49199d5d7efbe1deae3bbb46d21","impliedFormat":1},{"version":"7c4937d08ed84b22329f10a519a818c742ec3ed72f707a2ce7b4b1afce73dbb1","impliedFormat":99},{"version":"96553b4510021932f611cbf110ed51ad720a26e4e873d46e14865830386e4bf7","impliedFormat":1},{"version":"64ae004702aa9918844cfe9aefcbefacd18319e97ae3a2c424963124c9e69b6f","impliedFormat":99},{"version":"99a9a5a6beb297716830ea11d372d202d630436c07a84b5c2fddb9605efde3a0","impliedFormat":99},{"version":"ec7736327f522373e53c77448dc901c596ed06e042678452fa44f782940f5378","impliedFormat":99},{"version":"4baa42c484ca8f4d785ce068db8998c9afd353457cf22da554aa84c4592e59df","impliedFormat":99},{"version":"604123fb6f4898e81d0742b240187041b409ccd3a635b32f9145038bf7be9b7e","impliedFormat":99},{"version":"03858fa979f7b11d6954a37299c1e5fdae5b188208a029d6d83b2920864b4e13","impliedFormat":1},{"version":"8f060fe8760b19488120d0c5191075eb00b81f858f76824192369fcae7e2ca61","impliedFormat":1},{"version":"f4886ccfe0fe1eeda62828bd9ba45b467e7dc965d94b99c56a324cb73795dac9","impliedFormat":1},{"version":"e13451b8065f38f4260a41a626ff75bde232170123b351e562e13dc7f32d3118","impliedFormat":1},{"version":"9dedd036305407f7b74296c57fb84b3c0fcbb49031d27cc0931a74382789dbdd","impliedFormat":99},{"version":"600c2cfb5326759456a8fbf7740a13dd0494e8ebdeb017dc78b3ac502c5c71ce","impliedFormat":99},{"version":"bce2bb59e93d85f430bd1c63278a8d0aaa14f1a72d2e9716618faace674d6eef","impliedFormat":99},{"version":"613379c981c3082e6467d45dbacc2588d17abaec8f431a38d2ea9d122a50e7e8","impliedFormat":99},{"version":"db3c81a23f984f33afac53f4965b100bdaca6f12891f362aa49324808f383e08","impliedFormat":99},{"version":"5286c9f6ff53be81918fab35836adbc12845c7b1ad3780d38e8dd5b59125bdcf","impliedFormat":99},{"version":"028b794bae27001f841f511d41d4c1a703ae3179448154f3535f3215c79a2be5","impliedFormat":99},"36c9ff7f6d933067e940140081e6947d721fe6e79aa14a6ac1d89dc03edeb253",{"version":"4fb797b3db68833ade93aeb9c3fda4a578c78c27a125ddeb2fbaf5167803f928","impliedFormat":1},{"version":"b619e9595aad00408849155a9ad88e140373de771343de66adad7ceb0619420e","impliedFormat":1},"310ba0df9f9da36e43caf865d7acc30668243d30030567e374d3e4810cc857fd",{"version":"7a3642d37bc700dcee1029f8cf1e84d88f4c13a884059533e2dcc21f753e3141","impliedFormat":1},"87600904580fc85909f20ef26aed51ba546ee1dce2366ff55331ef712c80348e","989171399ff9d380c5aa466eec36a13c9ad129ed15dfc8bc20792c1e7f5c51cd","32dcc5c6725eae89ebbc493ce9eab8f1a0c08a3a2290ed37bb9d3db86abdd56a","127d08ef5e9a42d00b173354d071fd9aff6e7805d87467118d0a4b1ef40a815d","3cbb174e8435ca94afbacc6f7daf6b7422477215dccfe0dbbcaa14864d1353f7","6b089785c5fac29023f33a4f54064188566891418f6872bfd9e5c8b4bdf94781",{"version":"683dda298d1e91c0b699aaa3af11d3372d5bbab1763f13c543e0db6146a79c95","impliedFormat":1},"7df2132ea2f8e3768264927dffb0057619b2a2416fbb04ec94de9f2f247cda3b","0249a1f4842006994abe5f20054a544e7539673a9ea739eb3368662758a59734","b2672401bd43735f188be589c335b66656d0532b4fc52acdceab06e7328a40fa","bbaacf87505cbfce620039b49d0d29a7b3024b1a65809e49bf87610ad00e37d7","788857a8ffbfcb20572084e16b7e000f08db7f306acb53a1c88cfac5de003662","744de1c11224b0ed180b7ba884f938c09aa63f53cd9eb22d1f5825c1c587890f","d6138b346fccf5a7dd3bedc28d0149b423dac8d2d95aec5c57427cef5e9b9581","b043a3652f975ff35c061bda22cd83fbf8e6205135d631cc87471673202886dc","2665cf7ec5cd8a4e6ae10036ee9acfe6427dda27efb26e413a22d70090f31db2","15858ba9378fd1cd56fdc39a90c8fe3da0f183d03978293927adafb7e8776335","948608b7b03d28da290c8ac60d0048d87adde7ae0100f4a3e15752befff486e1","d58de4568ff456904e2be7dc57fdf535c3a773ac2c904d51ae1f4533e2e5f9e3","10e5ae00122168846858e0722d4249855df1fdf0ac8f39bd51e52aa2f6853de3","24b4abd57ecf1b5bad16b2577a06f695234c13abf0b9a42006f5fc5c95179a70","3e125e85f40a54746061a10d09d25be0cc6a50fef5b37dda18ba2e6f7ed66352","d8c02104b0d755c5e14a2f483072c24e56c9cf8a0fc96dd5103543aad92e73be","7dd39734fa116d80379e63767b96ea2c637c4d04525c3a44e3d00b50ccc67e51","9e53ab74cad3eb2aacb6f3619b70ffc56007f329add570d54232fc2d5d22aad6","5092ec4a3f09bd1d48ef7c5cac3e6ee0185ac7f2efed321a893481963e4fd452",{"version":"67ed6b191163cd0e59070ccb5988bfdccebd332bc540b66aac95f97545a9df4a","impliedFormat":1},{"version":"38285399f7b01baf9bafb5d3e1ae297fb5e946a445b9f8a30fec8bae0d83fe69","impliedFormat":1},{"version":"c31c35d81139d9aa6e96564b3c4a2b3480c000af64195cf55ff97217ec43c299","impliedFormat":1},"5a474d27406c3c4d7f17edb6ae6a8a90e3615b8585282f5de60d4a3903b49611","c0f332d5b389feb12a27d20e410b7192ee6bc4d058f9f4848d81f41edc4af8bf","7222e5f2395f1ba7d1cff6806ef4797fe14effe2ae9779b12c626a0d95ce21a2","f5395b80664d10f3093e17c203c5291ceca0aef605fd933230ba2a4467f67b31","2ec76c8c8f0ee3c7f687eb85ad377c48b974c3d51deaa3f4e3bb9f3573338714","ab10f513918849702d5834ba46e94b206601abee5435ba72e091b16f3bb9a9d5","3d67bc3e3abda25dc0532572f7d0977b2d5e80c4893788526d6df5729cd66736","e22aa563bd44724cc4e13a56added89588d87ecf1a4dad69534be92f0204c9a1",{"version":"d794aac705097f0548238d18529a231e947f5071a61e3a1f7629d1e8be306a9d","impliedFormat":1},{"version":"c56bdb6f9902d74cf915e2779c2da57606708eb0c94e5272f705422f08765b11","impliedFormat":1},{"version":"ac2f4efc979a1cc8ce1eb4c0c708770dd92ef4130e10013678823f064fa20d48","impliedFormat":1},"917a2c8a88827d1f24d6372b3a92173059cc5a96c5b01ef8dab1f8a3161d7c90","bb259ef0cd043117ff65d86f1895d5afe50849ccd7f1cd1b60c840784e6f4331","22029aa5b508edc35ba970bf8c57bb2d1f9d1be4f5d8ef18905fb8158420cc66","3d4af34db8cad32b417157abf331ba1d684b5a4d797f577e6e66b4a7316d34dd","9c3c3c553bbdb10efb15a3474327915fefbc5cae8cd850ee4484ca4987af52bf","29981e01d8caa105ec88b0b7ed749056f95ab52740b26b1035c91ce466512faf","14b6c45495db7df8171f10bd6388ef52d56c4befc2ad789869583cac279d3fc3","f5857a68efa076b079ee066aed6418a694e9eb6dc1501eefe1b7b7f48fe30163","0908a4ee3aab0c1a1422b23215d4e21fd4bd5c7b0bc79b91707ee48ea0922ff3","281969392ce03a62f90f9358b36ddaeea3e246540ed35177f8143e637ab509c4","211e69688802d8f23d9df981ec3a8a203f0ed8c86629e753c60785c52bb9426f","a1c759176e51f9cc4a874ed765df293a31600a792d75b76aaeabc077e8914fb2","5c04dd6ab9a1c2e72cb74208ed41b5587005d5ec87bf52d85620949b69d7baad","eeec22660663b08103448fa372e1d7c78a81e04852ddc0f090e7ccb8fe740d2d","3e62d91182f6f5c1840e1b35b389ea7babbea57630dad4fb7c52cf28f09b1f8b","0ba07ce9544d9bf3046eff251abc9239ef00c34d49cf9319374000bacb1203b9","b0d1e28ce0c8a8936cf7f816ddadd40635242df2db1f8abfce5c59550d08a487","a23ba4eea84ee4b3a0840903710aad64297528a83abc2313fe38e639ba83edb2","57ea3b18fa35dd949338548b0450aa96d3e88bf58cf1bd4a3d06983a2cd9498e","7d036c66c41dc6373d18083b609f94f360ca72dd9cf4867052f6a264f0717a5a","eedf7d2e654535ab54c3bd593c8b4a8779c818c3baac2e1e7fa1cfd9bbb220f0","80926114055a5ece753bcd79541a6e7530c5ffc1db78cb67ef252059a709c4d0",{"version":"590052bd57329035b854dee555d0fc30bec3b1d402cfbe024b2b79cff88a7087","impliedFormat":1},"9f2a062871e17aa173dd498c98425f2cabfe1ad4daeeaa224ddf7d74c3d40d41","ddbbb93a386916dd3856af0bf70164b92bf95273792c196fe9d2f9c92ec46bcc","0361cea01670f3839e25f714e9d2f11b026edf7da0bb6c62d31bf0c43a9bca39","549401f6e526b05726f72a0adc30ee272c5f868166ffe4fec6666d8a260b2f17","16e940f8d854ab66206339d43bc4ef0a606fdccc01d0c25ad811a0e7970c1b33","a80d49da0cedf2afd9c5c1d7b86ea8a8eeaab69e25a93a0d91f04117afa42501","ccd79bc54ca95ee95f36ecb6fa013497f8deeeaccf3a4725fcfc779eb05f0702","d193faed9b9728281991636e4420502152c0d6338f580c121008ecff2392d9b8","ebbd6db8946565f6d9000fa238242c138fd52cb3e8578243b9fd67e8a7cdde58","ed63fed7afd52d1d308427da0350ba87286e337be75e66f7305dd9e250d6f27d","bc88a0089f3f99a89dfb90e00cc462a45c8f90fa0996d2e5d941d2769152ced4","2c8c65603f9da02fe663d7f15ee542c76d7e17a9c0767dcadc29d7da8009952a","eaf28b0935dfbabc25617607160359ae801b69f3f778f7d1e41c945857eb012e","8192586d65ef538f7ba254e2397a4f3b72917ffd3bce3976af544c4df4deaf79",{"version":"154d7c8fce9146c225d84451ce3f7344022123dd9c96b38d154b8199150a34dc","impliedFormat":1},{"version":"bdc74f70ca07fb92ef25320a0db804d2ca2e4a9f3dffd1f23e8e839d580953d4","impliedFormat":1},{"version":"f3c3b80170d4a3e9343a3922598cca666079dbb6b8be79e23269499689f4f898","impliedFormat":1},{"version":"a1ac2b164e3a2ce8c85df3fb32785302cb2b9af4ec547be295db4540246438da","impliedFormat":1},{"version":"f56400be0d4876ce99549dda634582f564a52f02a6e23c1f64ce70fbdaf51749","impliedFormat":1},{"version":"2c62f2ef11f8c64fb41fc1b7867d001e308b4eefad7d3b14c6de526edc0138d4","impliedFormat":1},{"version":"b5fb19c4d2bb5f883c90b7ad5c4093f8ba9b457f356b0f92d45bcc4e38ba6bd0","impliedFormat":1},{"version":"85f980b324ec2a4c78d11f5dd2a0ee73eca4e2b164da55cbda572c2314d8507d","impliedFormat":1},{"version":"26ca99773abe1fa6c5895ed0e4be20253293794fefb51e4fe865b7d4bfeb8d27","impliedFormat":1},"2b94c37fc99164902cd1eeb3ed47309b956b451336670df1083ab7f337021927","d81f544838d05d5b2d5ea52e29473b7e77c473d395ede13d9ccf3cc1b7073ff9","04589e4a6ad3f274da4b7cc11a6e03de4e4987444d22863de81d8989da71dc3a","4d52e47c4d8adccc9d0e0399f3f4a25fe3d14292eed16b96540968e3e29eaff4","9098ab1e880acbba9bcb2f461c3c1d0ead20e7b4ac078fbb86a7652c49993714","d1c0ac32483835761295bd2d35549869956655273f1e23ab307578c02810a300","4e6b4fa4994313438c94b48af0abc7627829837baa731df7a3a7818bd889f463","9fc77c286919acd7db583f790dbbbf7caeee8762b392b2677c4bc80368c87a7e","9896931205d0a829cedcdcd5b48d18b951633c50463adab0eff64cc6424ab893","9813a143b061177db7bf30de15d48401d43c5d1a5f4a4f1511e8123928f15ee8","583384da1df63e898feddd8f218667311207197c3beda15f8504c6960eeacf96","dc7c21786a2897f88d09d9ff0a4367e705c6f5dda691561c77d88a58ef487162","4f1b1922177b370e57207106f4c822031a5ede3f738910484e41bf94d53696d7","713dbc2cbc08816aefc86e44872d055fe7aa4e875716daf61420f0970a9c5759","1644f51b9f71479dded70a0be84648f1ba07515c504adbfcdf53cacb3ff0e5d4","9c8247686929e1a913e20eaa9d77aac477029aaae89a8fa58e5c4d2b94c60fea","7cdf3fd80f097572b17ff4408a941dcb77db9329c3ec6a1a95e068ed902792da","fa854307015c6bf7f1019aa8cb28d858ebccc960aea8404fb27d3e4fffe10768","6a19a3c5f5f8a5fffa07dfa0dd93fa8ccb517271de5fce714b86321ad05caa54","4984b2e6da2933f8a39b04913b92b5f8735773ae27dca70ceeacf1d6efa7ee8d","08b1ef9de1d312611a478fc530631c11a9ad24a28cafdcd672c0dfdf586bf047","72ff1dd08d845af9be3627b96fc66269869465907f2c298c13558245285ade34","f4ab70125eaad7434f0fe4cb63ef2df4b6beef1702897a2e2db607fb78eb885e","2e4c84d98a8bc65442cbeece30adf9e1a8e513b95f280405e62b44dc98df12c5","72ec3a7ff110cc3c3a5983bf420573479d057c6b8a0726a8dec07d8f2893c4b1","158d90ba18cafe3780fd54f83e2e886f965873eb476f2a23ffb3a3ca8e89ba65","11aaf43901f0b4d74217be5dbc99835d7783d436a342f802e263a977d0050a05","2c9b001846d80d3786db2033ebd09068cb31fb8208bd9e9bad92cb027a75d0de","a747e3a52828e0fa35d097b4ffe3572e1ce88368e9456c41d8e24374c7d05acc","95a68b77d00047603e2e4bba16e3b498077df8d849e76acf3e4cc7788b49e28d","23f456de29100cad2de8cc04759f547d284d66109c3fab5a27776568037dc7b2","01696ba6570050bfc989375552a0f5165e327c3ac5bb17f0b5e78e743333fd1d","01200301bca701b506facd28dee685b6a8b2b555c699f0ff30dd74e5b99abe1f","4c53adcd886ae511e7ee9440dac2aa16713f2a9afda780ddf19d7253e714d5cb","e399275e9281e05fa9dd84960c409a408fc207dd3580b36de078b56422f9cb0a","c10358936958187f2b27f9e1b0bc6baf2f210709fb64b70de4257429d7359bb8","c2f110d2d4e0cd9504824d81c397e8302ccac4f4c728956ad33be7aabc0a4381","03eb7c12ef39b7816015924f403ceb79485938f0f1a48459c021e9b4055c9a8b","ae9abf60d6fc39555e59418a6e36564716e27b061846f6d3d73c0fbf3b1286a9","1fa6df1d2ca8229dbf361b3ce473977ef2776b95f5fbaa84b3ae00c8dcf86615",{"version":"b837b881a232536ef3e9006e63af7066d81c1288d0bdbf60f27f6ac429737dbb","impliedFormat":1},{"version":"daa5429d381ca4a67e4b492d3f045df62c1f2d990baf5dad35b0ff5c29273f4c","impliedFormat":1},{"version":"51fa6a12e2ec8a91edfe4ae07fcbc1e7c200b05a42041be0e1aeae8b576ee31a","impliedFormat":1},{"version":"48eb5e2da105b7768e8cabf6a39ae879f766de8f74c476d23c11da4291c4ffaa","impliedFormat":1},{"version":"a6f00e1ef00f3ce36a09fb868e4910783fb3e752cff6674b54f6d2de0801aba0","impliedFormat":1},{"version":"c0eaaa617a6cb06edf150a5d03a04a3d0fec80fe3c773938099ff382efa62947","impliedFormat":1},{"version":"1288934f302668043a07db5f78ef2b8179522d1882a3d936e722152fb0639882","impliedFormat":1},{"version":"e1e203918d7a9b7f4cf79462f758ac97bef3e15e457becf11f0a6b5115a26357","impliedFormat":1},{"version":"b382a4c85419aff8e23b5e6b0db06ec89d1a2fe30c413aa2d4eec0c8265ba963","impliedFormat":1},{"version":"fbe6c1d70ea1fb97a1c7334c5320f61b143440bace1afc01a1968a7afef1faa2","impliedFormat":1},"def300c4247c0cba9fa30bb995628aa68a9ccd3cbab8147ef67ba76515ae0924","78e5d71f19e6ffcc6be201e0bf9a009b6e4f19cce4b4d25fe8c9d815614be74c","a74fd7865ed23d77f1931c14fc729b6d3738c4bedafb1d223187a7c1e0cf1462","a7ab49e1395b7afca03b7a60ada24225c35c9af28a07172c7c08213f79ce1db1","4db6754f897fb306db6128465bf1681db730db9b06ce22b7c1bda40d16a7f2ab","48b1c47d0719b748f7fc66cd144da841c2489225ff1372f93dcbf8fa85d56cc3","689c3eea7e2f76b058e31355101ad3ce1268c10759edc890a776c55ce125e904","b70c9651c20bfd70f8f69d9b8043f8e8f73c67167d858a34cce3d10d7395a2c9","4e2e1353acaeb19916b25f56f80e334184531e43af4ab1213ce5b945cbd1dab7","7cf54a47de8095bbfece76f0d793004c46f7083ae049127e25f7ee367516dfc6","4a8b7793e441075f3c661b111277ae6adaa42ca4bd0c9394c03709e6c3ba8a52","dd31571b314c95ca36be81d324c8299a3912edac037e4eb70d4657ff486c77c1",{"version":"e32e18213b5e35a9c626d364e8e74245d13694c67b1d6a62b6b9cf10c70fe494","impliedFormat":1},{"version":"48c116b701a9284a564a4a179b520c1aa9c5bbd3fb5bca1ca23cce9277c89ca9","impliedFormat":1},{"version":"c6aa6f206416ea404019407757300bcbdfe6272c24d10f10431fc3b8f2d22bae","impliedFormat":1},{"version":"ce1d8d570f7803f92bec090fb304515bf3e50a9161451bd7acc59288b4e5c78b","impliedFormat":1},{"version":"4adb18be27fc58e0e4f68f8569abe6e778c7001c627d610c53bdd3799e67eb08","impliedFormat":1},{"version":"0d16150458cfa171ba7c5f5e482c3032122533571acc08b1ec8fd39a131e8fac","impliedFormat":1},{"version":"3287e52d8cb8bc24e070d79362209384f69a64fb271d491a2d1921b8217622e4","impliedFormat":1},{"version":"9582cc456f311f20834c537dd4a516a6b97957ed6ddcdb9efc7b114a3a440e59","impliedFormat":1},{"version":"1510dc24a19fe9bc88e31e7f9f25ad27e8ce321a37c2b2bd464fc04c15188548","impliedFormat":1},"ef074a21b6b032a50aaafee3451418ec5389a426ab0a95c328e97283c99ba9f3","a45da7cab272d35b47b3d287d7439ff2ecc576f02828b749261bbec659a10d02","c5780bda4f591945490f7c0f2bae7abc69a5aedcbc8b190dc0ae72eee9a1632f","d443d1bce83427ae56b7fe2020ffabb53efc18d046397998de6d90e077050c73","3f30df89d464d67698979b34b94aaca244f5eb6f12ecef7450251812096fc060","0744fb9100c3ac438ca1405ca17879cbc35b17b2b0dcb9e0b80f7a63c688b455","32c0783b69e164bae6d85f9086ab57c65d9449f3262383fa28e52951d148a75a","54ba30e7a17889cabfea17acf12bd570b846aff03c676f8a7a25664023b8fab2",{"version":"e8ff2ca0ff1b49de5ee6c03c7e5cccce83fb945e6ae0c554448a88f1ddb0d825","impliedFormat":1},"e04f0a704c6b752929a6445cad2e3fd602c151e7c0b0a3fdecacf668de5e42a0","5f324a4e1bc1a8b297c647bcf41a5125c698450e8bd61c95a2fb35d34dd946ed","ff0a84131ee65b029afc80a3a8faccaa5a9bac7e2d06695dec1846dcb524e12d","63c36f5629bed0645863f2860b9070f63368f240040a08392db5238dae72265b","b7a63422bd015a547621535ca006745ca94adfa466ad7114c5dd280b03859f40","d1cdb5a73172ccc12043ab3a32fde66c1e29a5e7e1bdeceabbb2f2e743aa766a","29fc05cb5bb355cd3da83b2c100b3e58a56eceee9d790937ed2817c2ff08d61d","52a69840910872df1531957ee2e6c8f59692ffec693fe248bc2eaec2b44b93cc",{"version":"e193b1b9d60661598dc4ac33d7c20ab19cdb32980fa0273ae0a600ab92807b4f","impliedFormat":1},{"version":"f5a961c096d21f278cf9b4263ac7e6f4f037db79b2ec7cf2c1bc61ea7c606ec3","impliedFormat":1},"ed1336c6b41c7dcb86de2a5b82cb80e57dfd3a8e88618c308d06f2244a1b72bc",{"version":"0f8e50b08e76e19be9d0f893ecad663e24a276a3d6f27f7dff35a91a4e8680ca","impliedFormat":99},{"version":"ce1ecc9f5d6cb79b582d43bb77c07332ca0d4a79b2c13f54cb3031f381dbc133","impliedFormat":99},{"version":"4449065636492defd8892b12dc093523d161acab80ebd877b24eb292a3f25909","impliedFormat":99},{"version":"e07bdc166a489495b432feb444ae7a23248fd26f8d9219bad53972eeb7fe75fe","impliedFormat":99},{"version":"a40463632231d1be3094e7ead6d70fa87fa51ebb96b90d94a2cb37009c0c4647","impliedFormat":99},{"version":"4fda212bd9c1f0266c2c7234dbc05144e0ae40bef71b506b909233bc1c87246c","impliedFormat":99},"508cbe911c86f851c5c5aa5995c6170718cb12f31f024d2690f6e6656b467a40","85cad17db9b6469c87956a14d8362a0987a9b03d93b0ead17c73da1e4ef74131","6300757703dc65d40d0fa8ce115afb69f576384b4254133b49982e35243d8ac4","4b93abf8adbfe8e9852428c7484bedbcd1248c67f546fef14221a957c72a3bb0","1d181ea4e6f9a08156d5e08d93b8fddecaa711cdf3e16be13d014b99b4c839cd","d5d7995f8af9c7aa81afa7e37edef6fcde1b8c87b0b53f48cb8a9507ed4ecece","7c3ceff955f3403238599d8dde6b3d410be7115a685aee0de63de3441df11c85","b6b912b8d489d07c186dedc7288b4fa859f0561eb6301ed890d7b427dab38314","9dffd26caaab6ee7add8e0d9bc5bb87869d8176c1eb12ad2054d9e9c0c9318e5","402d849ae0d4255f3b810a12ded1cca095ab2aa31e9100156fb5bc3f136c0c67",{"version":"cdcc132f207d097d7d3aa75615ab9a2e71d6a478162dde8b67f88ea19f3e54de","impliedFormat":1},{"version":"0d14fa22c41fdc7277e6f71473b20ebc07f40f00e38875142335d5b63cdfc9d2","impliedFormat":1},{"version":"e1028394c1cf96d5d057ecc647e31e457b919092f882ed0c7092152b077fed9d","impliedFormat":1},{"version":"f315e1e65a1f80992f0509e84e4ae2df15ecd9ef73df975f7c98813b71e4c8da","impliedFormat":1},{"version":"5b9586e9b0b6322e5bfbd2c29bd3b8e21ab9d871f82346cb71020e3d84bae73e","impliedFormat":1},{"version":"3e70a7e67c2cb16f8cd49097360c0309fe9d1e3210ff9222e9dac1f8df9d4fb6","impliedFormat":1},{"version":"ab68d2a3e3e8767c3fba8f80de099a1cfc18c0de79e42cb02ae66e22dfe14a66","impliedFormat":1},{"version":"d96cc6598148bf1a98fb2e8dcf01c63a4b3558bdaec6ef35e087fd0562eb40ec","impliedFormat":1},{"version":"f8db4fea512ab759b2223b90ecbbe7dae919c02f8ce95ec03f7fb1cf757cfbeb","affectsGlobalScope":true,"impliedFormat":1}],"root":[[49,56],90,131,[230,232],235,[238,249],[256,260],[270,274],[458,468],895,[903,908],[912,920],[1239,1245],1411,[1415,1418],[1420,1424],[1431,1439],1442,1443,[1518,1521],[1523,1526],[1538,1553],[1750,1757],[1764,1769],1808,1866,1867,1869,1870,[1874,1890],1894,[1898,1905],2031,[2045,2082],2084,[2088,2095],2138,2141,[2143,2148],[2150,2168],[2172,2179],[2183,2204],[2206,2219],[2229,2268],[2279,2290],[2300,2307],[2309,2316],2319,[2326,2335]],"options":{"allowImportingTsExtensions":true,"allowJs":true,"allowSyntheticDefaultImports":true,"esModuleInterop":true,"jsx":3,"module":7,"noUnusedLocals":true,"skipLibCheck":true,"strict":true,"target":9},"referencedMap":[[2329,1],[51,2],[52,2],[2047,3],[239,4],[238,5],[241,6],[240,5],[243,7],[242,5],[49,8],[910,2],[50,2],[275,9],[276,10],[2270,11],[2269,2],[2278,12],[2277,13],[1962,14],[1963,15],[1961,16],[1947,17],[1948,18],[1946,19],[1950,20],[1951,21],[1949,22],[2018,23],[2019,24],[2017,25],[1953,26],[1954,26],[1955,27],[1952,28],[1958,29],[1959,29],[1960,30],[1957,31],[1965,32],[1966,33],[1964,34],[1968,35],[1969,36],[1967,37],[2014,38],[1990,39],[1991,39],[1989,40],[1992,39],[1987,41],[2015,42],[1988,43],[2024,44],[2025,45],[2023,46],[2021,47],[2022,48],[2020,49],[1944,50],[1977,51],[1978,52],[1980,53],[1975,54],[1971,9],[1979,55],[1970,56],[1984,57],[1976,58],[1972,59],[1981,60],[2016,61],[1973,62],[1974,63],[1983,56],[1956,56],[1982,56],[1985,50],[1986,64],[2027,65],[1945,66],[2026,2],[2336,2],[2339,67],[2149,68],[1247,69],[1246,2],[898,2],[897,70],[899,71],[896,2],[155,49],[156,72],[2308,49],[2205,2],[1532,2],[1531,2],[1537,73],[1527,2],[1530,74],[1534,75],[1533,76],[1536,2],[1535,76],[1529,77],[1528,46],[253,49],[252,2],[254,78],[255,79],[251,80],[250,2],[263,81],[264,2],[265,82],[262,2],[266,83],[261,2],[267,84],[1798,85],[1799,85],[1800,85],[1807,86],[1801,87],[1797,88],[1804,9],[1805,9],[1806,2],[1802,89],[1803,87],[399,90],[402,9],[408,90],[409,90],[431,9],[410,90],[412,91],[413,9],[414,92],[415,9],[416,91],[417,90],[400,93],[401,9],[403,94],[404,95],[405,94],[406,95],[407,95],[434,96],[418,9],[419,91],[420,2],[398,92],[421,2],[422,2],[423,95],[424,97],[425,93],[426,2],[427,98],[428,94],[429,99],[430,95],[432,100],[433,2],[1770,101],[1771,102],[1773,103],[1778,104],[1779,104],[1780,9],[1781,101],[1782,105],[1783,9],[1784,9],[1785,102],[1776,106],[1777,106],[1786,2],[1788,102],[1787,2],[1789,9],[1790,102],[1772,49],[1791,101],[1792,107],[1793,108],[1794,102],[1774,2],[1796,109],[1775,46],[1795,101],[2126,110],[2123,111],[2122,112],[2125,49],[2124,111],[439,113],[440,114],[441,115],[436,114],[443,116],[442,9],[446,9],[437,117],[452,118],[444,119],[445,119],[435,120],[447,121],[438,122],[448,2],[449,119],[450,2],[451,49],[393,123],[392,123],[395,124],[396,125],[394,125],[397,126],[391,127],[2033,128],[2034,128],[2035,128],[2036,128],[2044,129],[2037,130],[2032,131],[2040,132],[2041,133],[2042,134],[2043,133],[2038,132],[2039,135],[2002,136],[2001,137],[1996,138],[1999,139],[2010,2],[2003,2],[2004,140],[2005,2],[2006,141],[2013,142],[2012,9],[2000,143],[2011,144],[1997,145],[1998,46],[1993,2],[2007,2],[2009,2],[2008,146],[1994,147],[1995,2],[2338,2],[1912,9],[1913,2],[1929,148],[1915,149],[1930,46],[1918,150],[1906,2],[1919,2],[1917,151],[1916,2],[1922,2],[1908,152],[1943,153],[1911,154],[1909,46],[1910,155],[1907,2],[1942,156],[1921,157],[1923,152],[1924,158],[1926,159],[1931,160],[1925,159],[1932,161],[1914,2],[1941,162],[1940,163],[1939,164],[1934,165],[1933,166],[1938,167],[1937,168],[1936,169],[1935,164],[1927,157],[1920,170],[1928,171],[1868,172],[2344,173],[934,174],[935,174],[936,174],[937,174],[938,174],[939,174],[940,174],[941,174],[942,174],[943,174],[944,174],[945,174],[946,174],[947,174],[948,174],[949,174],[950,174],[951,174],[952,174],[953,174],[954,174],[955,174],[956,174],[957,174],[958,174],[959,174],[960,174],[961,174],[962,174],[963,174],[964,174],[965,174],[966,174],[967,174],[968,174],[969,174],[972,174],[970,174],[971,174],[973,174],[974,174],[975,174],[976,174],[977,174],[978,174],[979,174],[980,174],[981,174],[982,174],[983,174],[984,174],[985,174],[986,174],[987,174],[988,174],[989,174],[990,174],[991,174],[992,174],[993,174],[994,174],[995,174],[996,174],[997,174],[998,174],[999,174],[1000,174],[1001,174],[1002,174],[1003,174],[1004,174],[1005,174],[1006,174],[1007,174],[1008,174],[1009,174],[1010,174],[1011,174],[1012,174],[1013,174],[1014,174],[1015,174],[1016,174],[1017,174],[1018,174],[1019,174],[1020,174],[1021,174],[1022,174],[1023,174],[1024,174],[1025,174],[1026,174],[1027,174],[1028,174],[1029,174],[1033,174],[1030,174],[1238,175],[1031,174],[1032,174],[1034,174],[1035,174],[1036,174],[1037,174],[1038,174],[1039,174],[1040,174],[1041,174],[1042,174],[1043,174],[1044,174],[1045,174],[1046,174],[1047,174],[1048,174],[1049,174],[1050,174],[1051,174],[1052,174],[1053,174],[1054,174],[1055,174],[1056,174],[1057,174],[1058,174],[1059,174],[1060,174],[1061,174],[1062,174],[1063,174],[1064,174],[1065,174],[1066,174],[1067,174],[1068,174],[1069,174],[1070,174],[1071,174],[1072,174],[1073,174],[1074,174],[1075,174],[1076,174],[1077,174],[1078,174],[1079,174],[1080,174],[1081,174],[1082,174],[1083,174],[1084,174],[1085,174],[1086,174],[1087,174],[1088,174],[1089,174],[1090,174],[1091,174],[1092,174],[1093,174],[1094,174],[1095,174],[1096,174],[1097,174],[1098,174],[1099,174],[1100,174],[1101,174],[1102,174],[1103,174],[1104,174],[1105,174],[1106,174],[1107,174],[1108,174],[1109,174],[1110,174],[1111,174],[1112,174],[1113,174],[1114,174],[1115,174],[1116,174],[1117,174],[1118,174],[1119,174],[1120,174],[1121,174],[1122,174],[1123,174],[1124,174],[1125,174],[1126,174],[1127,174],[1128,174],[1129,174],[1130,174],[1131,174],[1132,174],[1133,174],[1134,174],[1135,174],[1136,174],[1137,174],[1138,174],[1139,174],[1140,174],[1141,174],[1142,174],[1143,174],[1144,174],[1145,174],[1146,174],[1147,174],[1148,174],[1149,174],[1150,174],[1151,174],[1152,174],[1153,174],[1154,174],[1155,174],[1156,174],[1157,174],[1158,174],[1159,174],[1160,174],[1161,174],[1162,174],[1163,174],[1164,174],[1165,174],[1166,174],[1167,174],[1168,174],[1169,174],[1170,174],[1171,174],[1172,174],[1173,174],[1174,174],[1175,174],[1176,174],[1177,174],[1178,174],[1179,174],[1180,174],[1181,174],[1182,174],[1183,174],[1184,174],[1185,174],[1186,174],[1187,174],[1188,174],[1189,174],[1190,174],[1191,174],[1192,174],[1193,174],[1194,174],[1195,174],[1196,174],[1197,174],[1198,174],[1199,174],[1200,174],[1201,174],[1202,174],[1203,174],[1204,174],[1205,174],[1206,174],[1207,174],[1208,174],[1209,174],[1210,174],[1211,174],[1212,174],[1213,174],[1214,174],[1215,174],[1216,174],[1218,174],[1217,174],[1219,174],[1220,174],[1221,174],[1222,174],[1223,174],[1224,174],[1225,174],[1226,174],[1227,174],[1228,174],[1229,174],[1230,174],[1231,174],[1232,174],[1233,174],[1234,174],[1235,174],[1236,174],[1237,174],[922,176],[923,177],[921,178],[924,179],[925,180],[926,181],[927,182],[928,183],[929,184],[930,185],[931,186],[932,187],[933,188],[1310,189],[1311,189],[1312,190],[1250,191],[1313,192],[1314,193],[1315,194],[1248,2],[1316,195],[1317,196],[1318,197],[1319,198],[1320,199],[1321,200],[1322,200],[1323,201],[1324,202],[1325,203],[1326,204],[1251,2],[1249,2],[1327,205],[1328,206],[1329,207],[1369,208],[1330,209],[1331,210],[1332,209],[1333,211],[1334,212],[1335,213],[1336,214],[1337,214],[1338,214],[1339,215],[1340,216],[1341,217],[1342,218],[1343,219],[1344,220],[1345,220],[1346,221],[1347,2],[1348,2],[1349,222],[1350,223],[1351,222],[1352,224],[1353,225],[1354,226],[1355,227],[1356,228],[1357,229],[1358,230],[1359,231],[1360,232],[1361,233],[1362,234],[1363,235],[1364,236],[1365,237],[1366,238],[1252,209],[1253,2],[1254,239],[1255,240],[1256,2],[1257,241],[1258,2],[1301,242],[1302,243],[1303,244],[1304,244],[1305,245],[1306,2],[1307,192],[1308,246],[1309,243],[1367,247],[1368,248],[138,2],[140,249],[411,9],[2083,250],[1494,251],[1496,252],[1495,251],[1259,2],[2337,2],[1395,253],[1398,254],[1401,254],[1402,254],[1400,255],[1399,255],[1403,256],[1406,257],[1405,258],[1396,259],[1404,260],[1397,254],[268,2],[269,261],[1394,262],[1392,2],[1390,263],[1393,264],[1391,265],[1389,266],[1388,267],[1386,268],[1387,268],[1385,2],[139,2],[61,269],[60,270],[59,2],[95,271],[91,272],[92,273],[93,272],[94,274],[1375,275],[1370,2],[1372,276],[1371,277],[1382,275],[1381,275],[1383,278],[1380,279],[1378,275],[1379,275],[1376,280],[1377,275],[48,281],[524,282],[891,283],[1871,284],[1873,285],[1872,286],[471,287],[836,288],[470,2],[473,289],[544,290],[490,291],[509,292],[481,293],[482,294],[483,294],[484,295],[520,296],[516,297],[491,298],[492,299],[493,300],[517,300],[494,294],[495,295],[518,301],[496,294],[480,302],[497,293],[499,303],[500,304],[501,304],[502,293],[503,295],[504,305],[498,306],[505,294],[506,294],[507,295],[508,294],[538,307],[533,308],[519,309],[548,310],[510,311],[512,312],[513,309],[534,313],[529,314],[532,315],[531,316],[542,317],[535,318],[537,319],[528,320],[541,321],[540,322],[530,323],[511,324],[545,325],[479,324],[539,326],[543,327],[515,328],[514,309],[546,329],[547,2],[521,330],[523,331],[885,332],[886,333],[469,2],[892,324],[602,334],[549,335],[573,336],[572,337],[552,337],[553,338],[554,338],[582,339],[555,340],[556,338],[562,341],[557,342],[558,338],[559,338],[574,343],[551,344],[560,337],[561,342],[563,345],[564,345],[565,342],[566,338],[567,337],[568,338],[569,346],[570,346],[571,338],[598,347],[593,348],[575,349],[605,350],[581,351],[577,352],[578,349],[594,353],[595,354],[588,355],[592,356],[590,357],[584,358],[596,359],[597,319],[591,360],[601,361],[600,362],[589,363],[576,324],[603,364],[550,324],[599,365],[583,366],[580,367],[579,349],[604,368],[585,330],[586,2],[587,369],[894,370],[472,324],[525,2],[651,371],[606,372],[627,373],[632,374],[607,375],[608,375],[609,376],[635,377],[610,378],[611,379],[612,380],[613,380],[614,380],[615,380],[616,376],[617,375],[633,381],[618,375],[619,376],[620,375],[621,375],[622,382],[623,376],[624,375],[625,375],[626,376],[642,383],[645,384],[634,385],[654,386],[628,387],[629,385],[650,388],[640,389],[644,390],[639,391],[641,392],[646,393],[649,394],[637,395],[652,396],[643,397],[638,398],[631,399],[630,385],[653,400],[636,330],[647,2],[648,401],[708,402],[661,403],[682,404],[655,405],[656,406],[657,405],[658,406],[660,407],[690,408],[687,409],[663,410],[662,411],[665,412],[666,405],[667,405],[668,407],[669,405],[688,413],[670,405],[671,406],[672,414],[673,406],[674,406],[675,414],[659,415],[676,407],[677,406],[664,416],[678,414],[679,406],[680,407],[681,406],[701,417],[704,418],[689,419],[711,420],[685,421],[683,419],[695,422],[696,423],[707,424],[697,425],[694,426],[693,427],[700,428],[705,429],[706,430],[703,431],[709,432],[702,433],[692,434],[686,435],[684,419],[710,436],[691,330],[698,2],[699,437],[900,438],[902,439],[909,440],[901,441],[887,442],[774,443],[785,444],[786,445],[788,446],[789,447],[777,448],[778,449],[780,450],[781,451],[782,452],[787,453],[783,454],[716,455],[748,456],[747,457],[713,458],[714,458],[715,458],[755,459],[717,458],[759,460],[718,461],[719,458],[720,462],[721,458],[756,459],[757,463],[722,458],[723,464],[724,457],[726,465],[727,466],[728,466],[729,467],[730,458],[731,458],[732,459],[733,467],[734,467],[735,466],[736,458],[737,457],[738,458],[739,459],[740,468],[725,469],[741,458],[742,459],[743,458],[744,458],[745,458],[746,458],[768,470],[749,471],[793,472],[758,473],[751,474],[752,471],[775,475],[784,476],[763,477],[767,478],[765,479],[769,480],[776,481],[779,482],[766,483],[773,484],[772,485],[764,486],[750,324],[790,487],[712,324],[762,488],[761,489],[754,490],[753,471],[791,491],[792,2],[760,330],[770,2],[771,492],[486,493],[522,494],[526,324],[477,495],[527,496],[536,497],[846,498],[820,499],[794,500],[795,501],[796,501],[797,500],[826,502],[798,503],[813,504],[799,505],[800,503],[801,500],[802,500],[803,506],[804,500],[821,507],[805,500],[806,501],[807,508],[808,501],[809,501],[810,508],[811,500],[812,501],[814,509],[815,508],[816,501],[817,500],[818,500],[819,501],[839,510],[842,511],[849,512],[822,513],[823,514],[828,515],[845,516],[831,517],[827,518],[830,519],[835,520],[843,521],[844,522],[841,523],[847,524],[840,525],[829,526],[825,527],[824,514],[848,528],[832,330],[833,2],[834,529],[838,530],[837,531],[474,532],[476,533],[475,532],[485,532],[488,534],[487,535],[489,536],[889,537],[875,538],[851,539],[879,540],[850,541],[884,542],[881,543],[882,544],[878,545],[872,541],[873,546],[874,547],[863,548],[858,549],[883,550],[876,551],[855,552],[870,550],[859,553],[860,554],[853,555],[857,556],[856,557],[868,558],[861,559],[864,560],[867,561],[866,562],[854,563],[865,564],[862,565],[880,566],[871,567],[877,568],[852,330],[869,569],[890,330],[888,570],[478,571],[893,2],[1444,2],[2343,572],[2182,573],[2180,2],[2181,574],[1441,575],[1440,2],[2299,576],[2296,577],[2295,578],[2297,579],[2293,2],[2298,580],[2294,581],[2292,582],[2291,2],[2317,2],[2318,583],[2140,584],[2139,49],[1428,585],[1425,2],[1426,2],[1427,586],[58,587],[57,2],[1453,588],[1472,589],[1454,588],[1473,590],[1460,2],[1461,9],[1466,591],[1465,2],[1457,46],[1467,9],[1455,588],[1456,588],[1464,2],[1470,2],[1471,592],[1468,593],[1474,594],[1447,2],[1463,595],[1469,2],[1462,2],[1446,2],[1448,2],[1449,596],[1450,596],[1451,597],[1452,598],[1459,599],[1458,2],[1892,600],[1891,2],[1893,601],[1497,602],[1509,2],[1488,603],[1483,604],[1516,605],[1515,604],[1500,606],[1479,604],[1513,606],[1514,606],[1512,607],[1475,604],[1503,2],[1502,2],[1506,2],[1487,2],[1492,2],[1482,2],[1481,2],[1499,608],[1493,2],[1476,609],[1478,609],[1507,608],[1504,608],[1485,610],[1490,611],[1489,611],[1484,610],[1480,608],[1517,612],[1510,2],[1501,613],[1498,614],[1505,608],[1486,610],[1491,611],[1477,2],[1511,2],[1508,608],[234,615],[233,2],[1430,616],[1429,2],[1408,617],[1407,2],[1409,618],[1374,619],[1373,2],[1384,620],[84,621],[63,622],[65,622],[64,622],[66,623],[67,622],[68,623],[69,623],[70,622],[83,624],[71,2],[72,622],[73,2],[74,622],[75,623],[76,622],[77,622],[78,623],[79,622],[80,623],[81,623],[82,622],[85,625],[62,626],[2341,627],[2342,628],[2340,629],[1419,2],[1445,630],[1414,631],[1413,632],[1412,2],[2225,633],[2223,634],[2226,634],[2227,635],[2221,636],[2220,637],[2224,62],[2228,638],[2222,639],[2325,640],[2323,641],[2320,642],[2321,643],[2322,62],[2324,644],[1896,645],[1897,646],[1895,2],[2028,9],[2029,647],[2030,648],[2273,46],[2276,649],[2274,2],[2275,2],[1809,2],[1864,2],[1811,2],[1810,2],[1815,2],[1863,650],[1846,651],[1844,652],[1855,652],[1845,653],[1814,654],[1861,655],[1860,656],[1862,657],[2170,658],[2169,659],[2171,660],[1858,661],[1856,49],[1812,49],[1847,2],[1851,662],[1848,663],[1849,664],[1853,46],[1850,664],[1852,665],[1854,666],[1859,667],[1831,668],[1823,668],[1820,669],[1825,668],[1832,668],[1826,668],[1828,668],[1830,668],[1822,668],[1833,670],[1817,671],[1857,672],[1835,673],[1836,674],[1824,675],[1821,676],[1834,667],[1843,677],[1818,2],[1842,678],[1837,679],[1841,680],[1840,681],[1827,682],[1829,678],[1838,678],[1839,683],[1865,684],[1813,685],[1816,2],[1819,686],[2120,2],[87,687],[86,2],[88,688],[89,689],[2128,690],[2127,691],[2130,692],[2129,693],[318,694],[369,694],[371,695],[372,696],[370,697],[373,698],[374,699],[295,700],[292,701],[293,694],[294,694],[317,694],[320,702],[322,703],[321,704],[319,705],[327,706],[323,49],[324,49],[325,694],[326,694],[328,694],[330,707],[331,707],[329,707],[332,708],[333,709],[339,710],[337,707],[334,694],[338,49],[336,707],[335,694],[346,711],[341,49],[340,49],[345,712],[344,49],[343,713],[347,694],[302,714],[300,701],[299,704],[301,694],[316,709],[348,715],[349,701],[350,716],[352,717],[291,694],[353,718],[313,719],[304,720],[305,9],[306,701],[312,694],[307,721],[308,694],[311,722],[303,46],[285,723],[355,724],[354,704],[356,694],[358,725],[357,9],[359,694],[360,694],[362,726],[361,9],[363,707],[364,707],[365,727],[366,728],[384,729],[367,702],[315,730],[368,694],[376,731],[377,732],[375,2],[379,733],[378,694],[380,728],[381,9],[382,49],[383,734],[385,9],[296,49],[298,735],[351,736],[310,737],[309,738],[386,49],[387,49],[388,49],[389,49],[342,49],[390,739],[287,740],[286,741],[279,742],[457,743],[456,744],[454,745],[453,746],[455,745],[290,747],[289,46],[288,46],[284,748],[283,738],[282,738],[314,2],[281,738],[280,738],[278,738],[277,749],[297,9],[1703,750],[1720,2],[1702,751],[1602,752],[1743,752],[1671,753],[1672,754],[1670,755],[1626,756],[1704,2],[1705,752],[1707,757],[1708,758],[1706,752],[1709,752],[1719,759],[1710,752],[1711,752],[1715,760],[1713,761],[1712,752],[1714,761],[1716,762],[1717,763],[1718,764],[1571,2],[1573,765],[1572,2],[1574,2],[1598,766],[1575,2],[1581,767],[1582,767],[1592,768],[1583,769],[1584,770],[1585,771],[1591,772],[1586,770],[1587,771],[1588,770],[1589,771],[1590,771],[1580,771],[1577,773],[1576,9],[1579,774],[1578,46],[1593,771],[1594,771],[1597,775],[1595,46],[1596,2],[1696,776],[1697,777],[1698,778],[1599,9],[1721,9],[1722,752],[1699,779],[1700,778],[1701,778],[1725,780],[1667,781],[1665,782],[1666,783],[1627,784],[1668,785],[1669,786],[1664,787],[1691,788],[1692,789],[1630,790],[1633,791],[1631,790],[1632,790],[1629,792],[1695,793],[1641,794],[1642,795],[1662,796],[1647,797],[1646,798],[1648,799],[1649,800],[1650,801],[1654,802],[1651,803],[1652,792],[1653,804],[1656,805],[1655,806],[1659,807],[1657,804],[1658,808],[1660,809],[1661,751],[1643,810],[1645,811],[1644,812],[1663,813],[1694,814],[1693,794],[1636,815],[1634,2],[1635,2],[1640,816],[1639,817],[1638,818],[1637,815],[1726,2],[1684,2],[1727,819],[1628,820],[1673,821],[1690,822],[1674,752],[1675,756],[1676,823],[1677,824],[1679,825],[1680,752],[1681,823],[1682,826],[1683,756],[1678,823],[1685,819],[1686,823],[1687,2],[1688,827],[1689,752],[1749,828],[1729,829],[1728,2],[1730,752],[1731,830],[1732,831],[1624,832],[1601,752],[1603,833],[1604,752],[1605,834],[1600,752],[1606,832],[1607,832],[1608,832],[1609,832],[1610,832],[1611,832],[1612,832],[1613,832],[1614,832],[1615,832],[1616,835],[1617,836],[1618,832],[1619,832],[1620,832],[1621,832],[1622,832],[1623,837],[1625,838],[1723,752],[1724,752],[1733,2],[1735,839],[1736,756],[1741,840],[1737,756],[1738,756],[1739,2],[1740,756],[1734,756],[1742,2],[1554,2],[1745,841],[1744,752],[1747,842],[1746,841],[1748,830],[2272,2],[1762,843],[1759,844],[1760,845],[1761,846],[1763,847],[1758,685],[2113,9],[2106,848],[2110,849],[2115,49],[2114,49],[2111,849],[2108,850],[2112,848],[2109,849],[2097,2],[2098,46],[2100,851],[2099,852],[2102,853],[2101,854],[2103,855],[2105,2],[2096,46],[2107,46],[2117,2],[2119,856],[2104,857],[2118,46],[2116,2],[2142,49],[2132,858],[2135,859],[2133,860],[2134,861],[2136,860],[2137,862],[2131,863],[2087,864],[2085,685],[2086,865],[1569,866],[1568,867],[1557,868],[1559,869],[1558,2],[1570,870],[1560,871],[1556,871],[1561,871],[1562,871],[1555,2],[1563,2],[1565,872],[1566,2],[1564,873],[1567,872],[2271,46],[165,874],[166,2],[161,875],[167,2],[168,876],[171,877],[172,2],[173,878],[174,879],[194,880],[175,2],[176,881],[178,882],[180,883],[181,9],[182,884],[183,885],[149,885],[184,886],[151,887],[185,888],[186,879],[187,889],[188,890],[189,2],[146,891],[191,892],[193,893],[192,894],[190,895],[150,886],[147,896],[148,897],[195,2],[177,898],[169,898],[170,899],[154,900],[152,2],[153,2],[196,898],[197,901],[198,2],[199,882],[157,902],[159,903],[200,2],[201,904],[202,2],[203,2],[204,2],[206,905],[207,2],[158,9],[208,9],[209,906],[210,907],[211,2],[212,908],[214,908],[213,908],[163,909],[162,910],[164,908],[160,911],[215,2],[216,912],[217,913],[144,906],[218,877],[219,877],[221,914],[222,898],[205,2],[223,2],[224,2],[227,2],[228,46],[225,2],[136,2],[133,2],[220,2],[137,915],[229,916],[132,2],[134,913],[135,917],[179,2],[141,2],[226,46],[142,2],[145,896],[143,9],[2121,2],[46,2],[47,2],[237,2],[9,2],[8,2],[2,2],[10,2],[11,2],[12,2],[13,2],[14,2],[15,2],[16,2],[17,2],[3,2],[18,2],[19,2],[4,2],[20,2],[24,2],[21,2],[22,2],[23,2],[25,2],[26,2],[27,2],[5,2],[28,2],[29,2],[30,2],[31,2],[6,2],[35,2],[32,2],[33,2],[34,2],[36,2],[7,2],[37,2],[42,2],[43,2],[38,2],[39,2],[40,2],[41,2],[1,2],[44,2],[45,2],[236,2],[1277,918],[1289,919],[1275,920],[1290,921],[1299,922],[1266,923],[1267,924],[1265,925],[1298,172],[1293,926],[1297,927],[1269,928],[1286,929],[1268,930],[1296,931],[1263,932],[1264,926],[1270,933],[1271,2],[1276,934],[1274,933],[1261,935],[1300,936],[1291,937],[1280,938],[1279,933],[1281,939],[1284,940],[1278,941],[1282,942],[1294,172],[1272,943],[1273,944],[1285,945],[1262,921],[1288,946],[1287,933],[1283,947],[1292,2],[1260,2],[1295,948],[1410,2],[1522,2],[244,46],[245,46],[246,46],[247,46],[248,46],[249,2],[258,949],[257,950],[256,2],[260,951],[2152,952],[2031,953],[1553,954],[2073,955],[2072,956],[1808,957],[1552,958],[1755,959],[1550,960],[1551,954],[2194,961],[1753,960],[2154,49],[2190,962],[1757,963],[1544,964],[2160,965],[2330,9],[1768,966],[1769,967],[2214,966],[1549,954],[1548,968],[2251,954],[272,962],[1752,959],[2141,969],[1754,960],[274,960],[1866,970],[1765,971],[2257,972],[2145,973],[2138,974],[2076,954],[1756,954],[1764,975],[273,976],[1766,977],[1767,978],[2287,979],[2286,980],[1750,981],[1751,982],[2217,983],[1867,984],[1869,985],[56,2],[918,986],[911,987],[915,988],[914,989],[913,2],[1241,990],[920,991],[1243,992],[1439,993],[1443,994],[1411,995],[1870,996],[1879,997],[1880,998],[1881,999],[1882,1000],[1883,1001],[1884,1002],[1885,1003],[1886,1004],[1877,1005],[1876,1006],[1878,1007],[1875,1008],[1874,1009],[1887,2],[916,1010],[917,2],[903,1011],[905,1011],[908,1012],[904,1011],[906,1011],[907,1011],[55,1013],[1888,2],[1889,1014],[919,1015],[1904,2],[1905,2],[2048,1016],[1901,49],[1899,9],[1902,46],[1898,1017],[2049,1018],[1900,1019],[1890,1020],[1903,1021],[2052,2],[2053,2],[2054,2],[2055,2],[2056,2],[2057,2],[2058,2],[2059,2],[2060,2],[2061,2],[2062,2],[2063,2],[2064,2],[2065,2],[1547,1022],[1435,1023],[1242,1024],[1546,1025],[1244,1026],[2050,1027],[1545,1028],[2051,1029],[1423,1030],[1521,1031],[235,1032],[468,1033],[1438,1034],[1436,1035],[1240,1036],[1415,1037],[2179,1038],[2328,1039],[2239,1040],[2327,1041],[2045,1042],[1418,2],[1420,1043],[1421,2],[1417,1031],[1422,1044],[53,2],[54,1045],[2241,1046],[2240,1047],[2067,1045],[2066,1048],[2233,1049],[2229,1050],[2231,1051],[2232,1052],[2230,1053],[2244,1054],[2243,1055],[2068,1056],[2238,1057],[2262,1058],[2260,1059],[2261,1060],[2176,1061],[2247,1062],[2166,1063],[2174,1064],[2175,1065],[2168,1066],[2172,1067],[2173,1068],[2256,1069],[2167,1070],[2252,960],[2254,1071],[2069,1072],[2248,1073],[2242,1074],[2143,1075],[2253,1052],[2144,1076],[2255,1052],[2246,1077],[2249,1078],[2250,1079],[2245,1080],[2259,1081],[2258,1082],[2165,1083],[2163,1084],[2162,1085],[2164,1052],[2155,1086],[2147,9],[2151,1087],[2150,1088],[2148,1089],[2146,1090],[232,1010],[1543,1091],[2183,1092],[2216,1093],[2178,1094],[2213,1095],[2177,1096],[2215,1084],[2046,1097],[2302,1098],[2157,1099],[2158,1100],[2267,1101],[2266,1102],[2279,1103],[2280,1104],[2282,1105],[2288,1106],[2283,1107],[2265,1108],[2268,1109],[2281,1110],[2289,1111],[2331,1112],[2285,1113],[2301,1114],[2284,1115],[2290,1116],[2300,1117],[2153,1118],[2078,1119],[2080,1120],[2079,1121],[2077,1122],[2075,1123],[2081,1124],[2074,1125],[2071,1126],[2082,1127],[2070,1128],[2264,1129],[2263,1130],[2303,1131],[2316,1132],[2326,1133],[2313,1134],[2314,1135],[2319,1136],[2304,1137],[2312,1138],[2310,1139],[2307,982],[2191,1140],[2195,1141],[2189,1142],[2311,1143],[2309,1144],[2305,1145],[2315,1146],[2306,1147],[2088,1148],[2084,1149],[2206,1150],[2218,1151],[2219,1152],[2202,1153],[2203,1154],[2204,1155],[2212,1156],[2207,1157],[2208,1082],[2211,1158],[2210,1159],[2332,1160],[2333,1161],[2192,1162],[2334,1163],[2188,1164],[2201,1165],[2187,977],[2199,1166],[2200,1167],[2193,1168],[2198,1169],[2196,1170],[2089,2],[2237,1171],[2234,1160],[2235,1172],[2236,1173],[2184,1174],[2186,1175],[2335,1084],[2209,1176],[2197,1177],[2185,1178],[2161,1179],[2159,1180],[2156,1181],[1542,1182],[1431,1183],[1424,2],[1434,1184],[1433,1184],[1432,1183],[1525,1185],[1538,1186],[1526,1187],[1523,2],[1524,1188],[1540,1189],[1518,1190],[1539,1191],[2090,2],[1442,1192],[1519,1193],[1520,1194],[2091,2],[465,1010],[458,1010],[467,1195],[462,1010],[459,1010],[463,1010],[464,1010],[460,1010],[466,1010],[461,1010],[270,2],[1894,1196],[271,1197],[895,1198],[1541,46],[1416,2],[1245,1010],[2095,1199],[1437,2],[259,2],[90,1031],[1239,1200],[230,46],[912,2],[2092,1201],[2093,1202],[2094,1203],[96,2],[97,2],[98,2],[99,2],[100,2],[101,2],[102,2],[103,2],[104,2],[105,2],[106,2],[107,2],[108,2],[109,2],[110,2],[111,2],[112,2],[113,2],[114,2],[115,2],[116,2],[117,2],[118,2],[120,2],[119,2],[121,2],[122,2],[123,2],[124,2],[125,2],[126,2],[127,2],[128,2],[129,2],[130,2],[231,1204],[131,2]],"semanticDiagnosticsPerFile":[[2052,[{"start":6,"length":14,"messageText":"Variable 'mockCategories' implicitly has type 'any[]' in some locations where its type cannot be determined.","category":1,"code":7034},{"start":101,"length":14,"messageText":"Variable 'mockCategories' implicitly has an 'any[]' type.","category":1,"code":7005}]],[2060,[{"start":2030,"length":15,"messageText":"'createMockState' is declared but its value is never read.","category":1,"code":6133,"reportsUnnecessary":true}]],[2088,[{"start":4688,"length":16,"messageText":"'novel.totalPages' is possibly 'null' or 'undefined'.","category":1,"code":18049},{"start":4736,"length":16,"messageText":"'novel.totalPages' is possibly 'null' or 'undefined'.","category":1,"code":18049}]],[2161,[{"start":3030,"length":2,"code":2550,"category":1,"messageText":"Property 'at' does not exist on type '{ data: UpdateOverview[]; date: string; }[]'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2022' or later."},{"start":3200,"length":2,"code":2550,"category":1,"messageText":"Property 'at' does not exist on type '{ data: UpdateOverview[]; date: string; }[]'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2022' or later."}]],[2198,[{"start":1732,"length":36,"code":2345,"category":1,"messageText":"Argument of type '\"readerSettings.volumeButtonsOffset\"' is not assignable to parameter of type 'keyof StringMap'."}]],[2288,[{"start":8481,"length":6,"code":2322,"category":1,"messageText":{"messageText":"Type '{ uri: string | null | undefined; }' is not assignable to type 'ImageURISource'.","category":1,"code":2322,"next":[{"messageText":"Types of property 'uri' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string | null | undefined' is not assignable to type 'string | undefined'.","category":1,"code":2322,"next":[{"messageText":"Type 'null' is not assignable to type 'string | undefined'.","category":1,"code":2322}],"canonicalHead":{"code":2322,"messageText":"Type '{ uri: string | null | undefined; }' is not assignable to type 'ImageURISource'."}}]}]},"relatedInformation":[{"file":"./src/screens/novel/components/Info/NovelInfoComponents.tsx","start":677,"length":6,"messageText":"The expected type comes from property 'source' which is declared here on type 'IntrinsicAttributes & CoverImageProps'","category":3,"code":6500}]},{"start":8635,"length":6,"code":2322,"category":1,"messageText":{"messageText":"Type '{ uri: string | null | undefined; }' is not assignable to type 'ImageURISource'.","category":1,"code":2322,"next":[{"messageText":"Types of property 'uri' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type 'string | null | undefined' is not assignable to type 'string | undefined'.","category":1,"code":2322,"next":[{"messageText":"Type 'null' is not assignable to type 'string | undefined'.","category":1,"code":2322}],"canonicalHead":{"code":2322,"messageText":"Type '{ uri: string | null | undefined; }' is not assignable to type 'ImageURISource'."}}]}]},"relatedInformation":[{"file":"./src/screens/novel/components/Info/NovelInfoComponents.tsx","start":785,"length":6,"messageText":"The expected type comes from property 'source' which is declared here on type 'IntrinsicAttributes & NovelThumbnailProps'","category":3,"code":6500}]},{"start":10449,"length":47,"code":2345,"category":1,"messageText":{"messageText":"Argument of type 'string | null | undefined' is not assignable to parameter of type 'string | undefined'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'string | undefined'.","category":1,"code":2322}]}},{"start":10802,"length":12,"code":2345,"category":1,"messageText":{"messageText":"Argument of type 'string | null | undefined' is not assignable to parameter of type 'string | undefined'.","category":1,"code":2345,"next":[{"messageText":"Type 'null' is not assignable to type 'string | undefined'.","category":1,"code":2322}]}}]],[2301,[{"start":2428,"length":21,"messageText":"Property 'sortAndFilterChapters' does not exist on type 'NovelContextType'.","category":1,"code":2339},{"start":2455,"length":20,"messageText":"Property 'setShowChapterTitles' does not exist on type 'NovelContextType'.","category":1,"code":2339},{"start":15379,"length":21,"code":2322,"category":1,"messageText":{"messageText":"Type '{ bottomSheetRef: RefObject | null>; sortAndFilterChapters: any; setShowChapterTitles: any; sort: \"readTimeAsc\" | ... 4 more ... | \"nameDesc\"; theme: ThemeColors; filter: (\"not-downloaded\" | ... 4 more ... | \"bookmarked\")[]; showChapterTitles: boolean; }' is not assignable to type 'IntrinsicAttributes & ChaptersSettingsSheetProps'.","category":1,"code":2322,"next":[{"messageText":"Property 'sortAndFilterChapters' does not exist on type 'IntrinsicAttributes & ChaptersSettingsSheetProps'.","category":1,"code":2339}]}}]]],"affectedFilesPendingEmit":[2329,51,52,2047,49,910,244,245,246,247,248,249,258,257,256,260,2152,2031,1553,2073,2072,1808,1552,1755,1550,1551,2194,1753,2154,2190,1757,1544,2160,2330,1768,1769,2214,1549,1548,2251,272,1752,2141,1754,274,1866,1765,2257,2145,2138,2076,1756,1764,273,1766,1767,2287,2286,1750,1751,2217,1867,1869,56,918,915,914,913,1241,920,1243,1439,1443,1411,1870,1879,1880,1881,1882,1883,1884,1885,1886,1877,1876,1878,1875,1874,1887,916,917,903,905,908,904,906,907,55,1888,1889,919,1904,1905,2048,1901,1899,1902,1898,2049,1900,1890,1903,2052,2053,2054,2055,2056,2057,2058,2059,2060,2061,2062,2063,2064,2065,1547,1435,1242,1546,1244,2050,1545,2051,1423,1521,235,468,1438,1436,1240,1415,2179,2328,2239,2327,2045,1418,1420,1421,1417,1422,53,54,2241,2240,2067,2066,2233,2229,2231,2232,2230,2244,2243,2068,2238,2262,2260,2261,2176,2247,2166,2174,2175,2168,2172,2173,2256,2167,2252,2254,2069,2248,2242,2143,2253,2144,2255,2246,2249,2250,2245,2259,2258,2165,2163,2162,2164,2155,2147,2151,2150,2148,2146,232,1543,2183,2216,2178,2213,2177,2215,2046,2302,2157,2158,2267,2266,2279,2280,2282,2288,2283,2265,2268,2281,2289,2331,2285,2301,2284,2290,2300,2153,2078,2080,2079,2077,2075,2081,2074,2071,2082,2070,2264,2263,2303,2316,2326,2313,2314,2319,2304,2312,2310,2307,2191,2195,2189,2311,2309,2305,2315,2306,2088,2084,2206,2218,2219,2202,2203,2204,2212,2207,2208,2211,2210,2332,2333,2192,2334,2188,2201,2187,2199,2200,2193,2198,2196,2089,2237,2234,2235,2236,2184,2186,2335,2209,2197,2185,2161,2159,2156,1542,1431,1424,1434,1433,1432,1525,1538,1526,1523,1524,1540,1518,1539,2090,1442,1519,1520,2091,465,458,467,462,459,463,464,460,466,461,270,1894,271,895,1541,1416,1245,2095,1437,259,90,1239,230,912,2092,2093,2094,231,131],"version":"5.9.3"} \ No newline at end of file