├── .editorconfig
├── .github
├── actions
│ └── setup
│ │ └── action.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── .nvmrc
├── .watchmanconfig
├── .yarn
├── plugins
│ └── @yarnpkg
│ │ ├── plugin-interactive-tools.cjs
│ │ └── plugin-workspace-tools.cjs
└── releases
│ └── yarn-3.6.1.cjs
├── .yarnrc.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── babel.config.js
├── definitions
└── pouchdb.d.ts
├── example
├── .watchmanconfig
├── Gemfile
├── README.md
├── android
│ ├── app
│ │ ├── build.gradle
│ │ ├── debug.keystore
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── pouchdbadapterreactnativesqlite
│ │ │ │ └── example
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── MainApplication.kt
│ │ │ └── res
│ │ │ ├── drawable
│ │ │ └── rn_edit_text_material.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ └── values
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── settings.gradle
├── app.json
├── babel.config.js
├── definitions
│ └── pouchdb.d.ts
├── index.js
├── ios
│ ├── .xcode.env
│ ├── File.swift
│ ├── Podfile
│ ├── Podfile.lock
│ ├── PouchdbAdapterReactNativeSqliteExample-Bridging-Header.h
│ ├── PouchdbAdapterReactNativeSqliteExample.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── PouchdbAdapterReactNativeSqliteExample.xcscheme
│ ├── PouchdbAdapterReactNativeSqliteExample.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── PouchdbAdapterReactNativeSqliteExample
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.mm
│ │ ├── Images.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Info.plist
│ │ ├── LaunchScreen.storyboard
│ │ ├── PrivacyInfo.xcprivacy
│ │ └── main.m
│ └── PouchdbAdapterReactNativeSqliteExampleTests
│ │ ├── Info.plist
│ │ └── PouchdbAdapterReactNativeSqliteExampleTests.m
├── jest.config.js
├── metro.config.js
├── package.json
├── react-native.config.js
└── src
│ ├── App.tsx
│ ├── logger.ts
│ ├── pouchdb.ts
│ └── shim.ts
├── lefthook.yml
├── package.json
├── src
├── __tests__
│ └── index.test.tsx
├── bulkDocs.ts
├── constants.ts
├── core.ts
├── debug.ts
├── index.ts
├── openDatabase.ts
├── transactionQueue.ts
└── utils.ts
├── tsconfig.build.json
├── tsconfig.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 |
9 | indent_style = space
10 | indent_size = 2
11 |
12 | end_of_line = lf
13 | charset = utf-8
14 | trim_trailing_whitespace = true
15 | insert_final_newline = true
16 |
--------------------------------------------------------------------------------
/.github/actions/setup/action.yml:
--------------------------------------------------------------------------------
1 | name: Setup
2 | description: Setup Node.js and install dependencies
3 |
4 | runs:
5 | using: composite
6 | steps:
7 | - name: Setup Node.js
8 | uses: actions/setup-node@v3
9 | with:
10 | node-version-file: .nvmrc
11 |
12 | - name: Cache dependencies
13 | id: yarn-cache
14 | uses: actions/cache@v3
15 | with:
16 | path: |
17 | **/node_modules
18 | .yarn/install-state.gz
19 | key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }}
20 | restore-keys: |
21 | ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
22 | ${{ runner.os }}-yarn-
23 |
24 | - name: Install dependencies
25 | if: steps.yarn-cache.outputs.cache-hit != 'true'
26 | run: yarn install --immutable
27 | shell: bash
28 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | branches:
8 | - main
9 |
10 | jobs:
11 | lint:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v3
16 |
17 | - name: Setup
18 | uses: ./.github/actions/setup
19 |
20 | - name: Lint files
21 | run: yarn lint
22 |
23 | - name: Typecheck files
24 | run: yarn typecheck
25 |
26 | test:
27 | runs-on: ubuntu-latest
28 | steps:
29 | - name: Checkout
30 | uses: actions/checkout@v3
31 |
32 | - name: Setup
33 | uses: ./.github/actions/setup
34 |
35 | - name: Run unit tests
36 | run: yarn test --maxWorkers=2 --coverage
37 |
38 | build-library:
39 | runs-on: ubuntu-latest
40 | steps:
41 | - name: Checkout
42 | uses: actions/checkout@v3
43 |
44 | - name: Setup
45 | uses: ./.github/actions/setup
46 |
47 | - name: Build package
48 | run: yarn prepare
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # XDE
6 | .expo/
7 |
8 | # VSCode
9 | .vscode/
10 | jsconfig.json
11 |
12 | # Xcode
13 | #
14 | build/
15 | *.pbxuser
16 | !default.pbxuser
17 | *.mode1v3
18 | !default.mode1v3
19 | *.mode2v3
20 | !default.mode2v3
21 | *.perspectivev3
22 | !default.perspectivev3
23 | xcuserdata
24 | *.xccheckout
25 | *.moved-aside
26 | DerivedData
27 | *.hmap
28 | *.ipa
29 | *.xcuserstate
30 | project.xcworkspace
31 | .xcode.env.local
32 |
33 | # Android/IJ
34 | #
35 | .classpath
36 | .cxx
37 | .gradle
38 | .idea
39 | .project
40 | .settings
41 | local.properties
42 | android.iml
43 |
44 | # Cocoapods
45 | #
46 | example/ios/Pods
47 |
48 | # Ruby
49 | example/vendor/
50 |
51 | # node.js
52 | #
53 | node_modules/
54 | npm-debug.log
55 | yarn-debug.log
56 | yarn-error.log
57 |
58 | # BUCK
59 | buck-out/
60 | \.buckd/
61 | android/app/libs
62 | android/keystores/debug.keystore
63 |
64 | # Yarn
65 | .yarn/*
66 | !.yarn/patches
67 | !.yarn/plugins
68 | !.yarn/releases
69 | !.yarn/sdks
70 | !.yarn/versions
71 |
72 | # Expo
73 | .expo/
74 |
75 | # Turborepo
76 | .turbo/
77 |
78 | # generated by bob
79 | lib/
80 |
81 | .env*.local
82 | .env
83 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v20
2 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 | nmHoistingLimits: workspaces
3 |
4 | plugins:
5 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
6 | spec: '@yarnpkg/plugin-interactive-tools'
7 | - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
8 | spec: '@yarnpkg/plugin-workspace-tools'
9 |
10 | yarnPath: .yarn/releases/yarn-3.6.1.cjs
11 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 |
2 | # Contributor Covenant Code of Conduct
3 |
4 | ## Our Pledge
5 |
6 | We as members, contributors, and leaders pledge to make participation in our
7 | community a harassment-free experience for everyone, regardless of age, body
8 | size, visible or invisible disability, ethnicity, sex characteristics, gender
9 | identity and expression, level of experience, education, socio-economic status,
10 | nationality, personal appearance, race, caste, color, religion, or sexual
11 | identity and orientation.
12 |
13 | We pledge to act and interact in ways that contribute to an open, welcoming,
14 | diverse, inclusive, and healthy community.
15 |
16 | ## Our Standards
17 |
18 | Examples of behavior that contributes to a positive environment for our
19 | community include:
20 |
21 | * Demonstrating empathy and kindness toward other people
22 | * Being respectful of differing opinions, viewpoints, and experiences
23 | * Giving and gracefully accepting constructive feedback
24 | * Accepting responsibility and apologizing to those affected by our mistakes,
25 | and learning from the experience
26 | * Focusing on what is best not just for us as individuals, but for the overall
27 | community
28 |
29 | Examples of unacceptable behavior include:
30 |
31 | * The use of sexualized language or imagery, and sexual attention or advances of
32 | any kind
33 | * Trolling, insulting or derogatory comments, and personal or political attacks
34 | * Public or private harassment
35 | * Publishing others' private information, such as a physical or email address,
36 | without their explicit permission
37 | * Other conduct which could reasonably be considered inappropriate in a
38 | professional setting
39 |
40 | ## Enforcement Responsibilities
41 |
42 | Community leaders are responsible for clarifying and enforcing our standards of
43 | acceptable behavior and will take appropriate and fair corrective action in
44 | response to any behavior that they deem inappropriate, threatening, offensive,
45 | or harmful.
46 |
47 | Community leaders have the right and responsibility to remove, edit, or reject
48 | comments, commits, code, wiki edits, issues, and other contributions that are
49 | not aligned to this Code of Conduct, and will communicate reasons for moderation
50 | decisions when appropriate.
51 |
52 | ## Scope
53 |
54 | This Code of Conduct applies within all community spaces, and also applies when
55 | an individual is officially representing the community in public spaces.
56 | Examples of representing our community include using an official e-mail address,
57 | posting via an official social media account, or acting as an appointed
58 | representative at an online or offline event.
59 |
60 | ## Enforcement
61 |
62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
63 | reported to the community leaders responsible for enforcement at
64 | [INSERT CONTACT METHOD].
65 | All complaints will be reviewed and investigated promptly and fairly.
66 |
67 | All community leaders are obligated to respect the privacy and security of the
68 | reporter of any incident.
69 |
70 | ## Enforcement Guidelines
71 |
72 | Community leaders will follow these Community Impact Guidelines in determining
73 | the consequences for any action they deem in violation of this Code of Conduct:
74 |
75 | ### 1. Correction
76 |
77 | **Community Impact**: Use of inappropriate language or other behavior deemed
78 | unprofessional or unwelcome in the community.
79 |
80 | **Consequence**: A private, written warning from community leaders, providing
81 | clarity around the nature of the violation and an explanation of why the
82 | behavior was inappropriate. A public apology may be requested.
83 |
84 | ### 2. Warning
85 |
86 | **Community Impact**: A violation through a single incident or series of
87 | actions.
88 |
89 | **Consequence**: A warning with consequences for continued behavior. No
90 | interaction with the people involved, including unsolicited interaction with
91 | those enforcing the Code of Conduct, for a specified period of time. This
92 | includes avoiding interactions in community spaces as well as external channels
93 | like social media. Violating these terms may lead to a temporary or permanent
94 | ban.
95 |
96 | ### 3. Temporary Ban
97 |
98 | **Community Impact**: A serious violation of community standards, including
99 | sustained inappropriate behavior.
100 |
101 | **Consequence**: A temporary ban from any sort of interaction or public
102 | communication with the community for a specified period of time. No public or
103 | private interaction with the people involved, including unsolicited interaction
104 | with those enforcing the Code of Conduct, is allowed during this period.
105 | Violating these terms may lead to a permanent ban.
106 |
107 | ### 4. Permanent Ban
108 |
109 | **Community Impact**: Demonstrating a pattern of violation of community
110 | standards, including sustained inappropriate behavior, harassment of an
111 | individual, or aggression toward or disparagement of classes of individuals.
112 |
113 | **Consequence**: A permanent ban from any sort of public interaction within the
114 | community.
115 |
116 | ## Attribution
117 |
118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119 | version 2.1, available at
120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
121 |
122 | Community Impact Guidelines were inspired by
123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
124 |
125 | For answers to common questions about this code of conduct, see the FAQ at
126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
127 | [https://www.contributor-covenant.org/translations][translations].
128 |
129 | [homepage]: https://www.contributor-covenant.org
130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
131 | [Mozilla CoC]: https://github.com/mozilla/diversity
132 | [FAQ]: https://www.contributor-covenant.org/faq
133 | [translations]: https://www.contributor-covenant.org/translations
134 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Contributions are always welcome, no matter how large or small!
4 |
5 | We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md).
6 |
7 | ## Development workflow
8 |
9 | This project is a monorepo managed using [Yarn workspaces](https://yarnpkg.com/features/workspaces). It contains the following packages:
10 |
11 | - The library package in the root directory.
12 | - An example app in the `example/` directory.
13 |
14 | To get started with the project, run `yarn` in the root directory to install the required dependencies for each package:
15 |
16 | ```sh
17 | yarn
18 | ```
19 |
20 | > Since the project relies on Yarn workspaces, you cannot use [`npm`](https://github.com/npm/cli) for development.
21 |
22 | The [example app](/example/) demonstrates usage of the library. You need to run it to test any changes you make.
23 |
24 | It is configured to use the local version of the library, so any changes you make to the library's source code will be reflected in the example app. Changes to the library's JavaScript code will be reflected in the example app without a rebuild, but native code changes will require a rebuild of the example app.
25 |
26 | If you want to use Android Studio or XCode to edit the native code, you can open the `example/android` or `example/ios` directories respectively in those editors. To edit the Objective-C or Swift files, open `example/ios/PouchdbAdapterReactNativeSqliteExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > pouchdb-adapter-react-native-sqlite`.
27 |
28 | To edit the Java or Kotlin files, open `example/android` in Android studio and find the source files at `pouchdb-adapter-react-native-sqlite` under `Android`.
29 |
30 | You can use various commands from the root directory to work with the project.
31 |
32 | To start the packager:
33 |
34 | ```sh
35 | yarn example start
36 | ```
37 |
38 | To run the example app on Android:
39 |
40 | ```sh
41 | yarn example android
42 | ```
43 |
44 | To run the example app on iOS:
45 |
46 | ```sh
47 | yarn example ios
48 | ```
49 |
50 | To confirm that the app is running with the new architecture, you can check the Metro logs for a message like this:
51 |
52 | ```sh
53 | Running "PouchdbAdapterReactNativeSqliteExample" with {"fabric":true,"initialProps":{"concurrentRoot":true},"rootTag":1}
54 | ```
55 |
56 | Note the `"fabric":true` and `"concurrentRoot":true` properties.
57 |
58 | Make sure your code passes TypeScript and ESLint. Run the following to verify:
59 |
60 | ```sh
61 | yarn typecheck
62 | yarn lint
63 | ```
64 |
65 | To fix formatting errors, run the following:
66 |
67 | ```sh
68 | yarn lint --fix
69 | ```
70 |
71 | Remember to add tests for your change if possible. Run the unit tests by:
72 |
73 | ```sh
74 | yarn test
75 | ```
76 |
77 | ### Commit message convention
78 |
79 | We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages:
80 |
81 | - `fix`: bug fixes, e.g. fix crash due to deprecated method.
82 | - `feat`: new features, e.g. add new method to the module.
83 | - `refactor`: code refactor, e.g. migrate from class components to hooks.
84 | - `docs`: changes into documentation, e.g. add usage example for the module..
85 | - `test`: adding or updating tests, e.g. add integration tests using detox.
86 | - `chore`: tooling changes, e.g. change CI config.
87 |
88 | Our pre-commit hooks verify that your commit message matches this format when committing.
89 |
90 | ### Linting and tests
91 |
92 | [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/)
93 |
94 | We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.
95 |
96 | Our pre-commit hooks verify that the linter and tests pass when committing.
97 |
98 | ### Publishing to npm
99 |
100 | We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc.
101 |
102 | To publish new versions, run the following:
103 |
104 | ```sh
105 | yarn release
106 | ```
107 |
108 | ### Scripts
109 |
110 | The `package.json` file contains various scripts for common tasks:
111 |
112 | - `yarn`: setup project by installing dependencies.
113 | - `yarn typecheck`: type-check files with TypeScript.
114 | - `yarn lint`: lint files with ESLint.
115 | - `yarn test`: run unit tests with Jest.
116 | - `yarn example start`: start the Metro server for the example app.
117 | - `yarn example android`: run the example app on Android.
118 | - `yarn example ios`: run the example app on iOS.
119 |
120 | ### Sending a pull request
121 |
122 | > **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github).
123 |
124 | When you're sending a pull request:
125 |
126 | - Prefer small pull requests focused on one change.
127 | - Verify that linters and tests are passing.
128 | - Review the documentation to make sure it looks good.
129 | - Follow the pull request template when opening a pull request.
130 | - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue.
131 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Takuya Matsuyama
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pouchdb-adapter-react-native-sqlite
2 |
3 | PouchDB adapter using [op-sqlite](https://github.com/OP-Engineering/op-sqlite) as its backing store on React Native.
4 | It's much faster than AsyncStorage.
5 |
6 | As of v4.0.0, it no longer requires `@craftzdog/pouchdb-adapter-websql-core`.
7 | It now directly uses op-sqlite, so it's more efficient.
8 |
9 | ## How to use
10 |
11 | ### Installation
12 |
13 | ```sh
14 | yarn add @op-engineering/op-sqlite react-native-quick-crypto @craftzdog/react-native-buffer @craftzdog/pouchdb-errors
15 | ```
16 |
17 | ### Polyfill NodeJS APIs
18 |
19 | Create a `shim.ts` file like so:
20 |
21 | ```ts
22 | import { install } from 'react-native-quick-crypto'
23 |
24 | install()
25 | ```
26 |
27 | Configure babel to use the shim modules. First, you need to install `babel-plugin-module-resolver`.
28 |
29 | ```sh
30 | yarn add --dev babel-plugin-module-resolver
31 | ```
32 |
33 | Then, in your `babel.config.js`, add the plugin to swap the `crypto`, `stream` and `buffer` dependencies:
34 |
35 | ```js
36 | plugins: [
37 | [
38 | 'module-resolver',
39 | {
40 | extensions: ['.tsx', '.ts', '.js', '.json'],
41 | alias: {
42 | crypto: 'react-native-quick-crypto',
43 | stream: 'readable-stream',
44 | buffer: '@craftzdog/react-native-buffer',
45 | 'pouchdb-errors': '@craftzdog/pouchdb-errors'
46 | },
47 | },
48 | ],
49 | ],
50 | ```
51 |
52 | Then restart your bundler using `yarn start --reset-cache`.
53 |
54 | ### Install PouchDB and adapter
55 |
56 | Now it's ready to use PouchDB!
57 |
58 | ```sh
59 | yarn add pouchdb-core pouchdb-mapreduce pouchdb-replication pouchdb-adapter-http pouchdb-adapter-react-native-sqlite
60 | ```
61 |
62 | Create `pouchdb.ts`:
63 |
64 | ```ts
65 | import PouchDB from 'pouchdb-core'
66 | import HttpPouch from 'pouchdb-adapter-http'
67 | import replication from 'pouchdb-replication'
68 | import mapreduce from 'pouchdb-mapreduce'
69 | import sqliteAdapter from 'pouchdb-adapter-react-native-sqlite'
70 |
71 | export default PouchDB.plugin(HttpPouch)
72 | .plugin(replication)
73 | .plugin(mapreduce)
74 | .plugin(sqliteAdapter)
75 | ```
76 |
77 | ### How to use PouchDB
78 |
79 | ```ts
80 | import PouchDB from './pouchdb'
81 |
82 | const pouch = new PouchDB('mydb', {
83 | adapter: 'react-native-sqlite',
84 | // Other options
85 | })
86 | ```
87 |
88 | ### Options
89 |
90 | You can specify the following options in the PouchDB options:
91 |
92 | - `location`: The location of the SQLite database file. See [op-sqlite's docs](https://op-engineering.github.io/op-sqlite/docs/configuration) for more details.
93 | - `encryptionKey`: The encryption key for SQLCipher. See [op-sqlite's docs](https://op-engineering.github.io/op-sqlite/docs/api#sqlcipher-open) for more details.
94 |
95 | ## Troubleshootings
96 |
97 | ### Fails to install crypto shim with `install()` on launch
98 |
99 | You amy get the following error when new arch is enabled:
100 |
101 | ```
102 | (NOBRIDGE) ERROR Error: Failed to install react-native-quick-crypto: React Native is not running on-device. QuickCrypto can only be used when synchronous method invocations (JSI) are possible. If you are using a remote debugger (e.g. Chrome), switch to an on-device debugger (e.g. Flipper) instead.
103 | (NOBRIDGE) ERROR TypeError: Cannot read property 'install' of undefined
104 | ```
105 |
106 | - This is a know issue: [Error: Failed to install react-native-quick-crypto: React Native is not running on-device. · Issue #333 · margelo/react-native-quick-crypto · GitHub](https://github.com/margelo/react-native-quick-crypto/issues/333)
107 |
108 | For now, you have to edit:
109 |
110 | - `lib/module/NativeQuickCrypto/NativeQuickCrypto.js`
111 |
112 | And comment them out:
113 |
114 | ```
115 | // Check if we are running on-device (JSI)
116 | // if (global.nativeCallSyncHook == null || QuickCryptoModule.install == null) {
117 | // throw new Error('Failed to install react-native-quick-crypto: React Native is not running on-device. QuickCrypto can only be used when synchronous method invocations (JSI) are possible. If you are using a remote debugger (e.g. Chrome), switch to an on-device debugger (e.g. Flipper) instead.');
118 | // }
119 | ```
120 |
121 | ## Contributing
122 |
123 | See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
124 |
125 | ## License
126 |
127 | MIT © Takuya Matsuyama
128 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:react-native-builder-bob/babel-preset'],
3 | }
4 |
--------------------------------------------------------------------------------
/definitions/pouchdb.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'pouchdb-utils'
2 | declare module 'pouchdb-adapter-utils'
3 | declare module 'pouchdb-merge'
4 | declare module 'pouchdb-json'
5 | declare module 'pouchdb-errors'
6 | declare module 'pouchdb-binary-utils'
7 |
--------------------------------------------------------------------------------
/example/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/example/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
4 | ruby ">= 2.6.10"
5 |
6 | # Exclude problematic versions of cocoapods and activesupport that causes build failures.
7 | gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
8 | gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
9 | gem 'xcodeproj', '< 1.26.0'
10 | gem 'concurrent-ruby', '< 1.3.4'
11 |
12 | # Ruby 3.4.0 has removed some libraries from the standard library.
13 | gem 'bigdecimal'
14 | gem 'logger'
15 | gem 'benchmark'
16 | gem 'mutex_m'
17 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli).
2 |
3 | # Getting Started
4 |
5 | > **Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding.
6 |
7 | ## Step 1: Start the Metro Server
8 |
9 | First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native.
10 |
11 | To start Metro, run the following command from the _root_ of your React Native project:
12 |
13 | ```bash
14 | # using npm
15 | npm start
16 |
17 | # OR using Yarn
18 | yarn start
19 | ```
20 |
21 | ## Step 2: Start your Application
22 |
23 | Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app:
24 |
25 | ### For Android
26 |
27 | ```bash
28 | # using npm
29 | npm run android
30 |
31 | # OR using Yarn
32 | yarn android
33 | ```
34 |
35 | ### For iOS
36 |
37 | ```bash
38 | # using npm
39 | npm run ios
40 |
41 | # OR using Yarn
42 | yarn ios
43 | ```
44 |
45 | If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly.
46 |
47 | This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively.
48 |
49 | ## Step 3: Modifying your App
50 |
51 | Now that you have successfully run the app, let's modify it.
52 |
53 | 1. Open `App.tsx` in your text editor of choice and edit some lines.
54 | 2. For **Android**: Press the R key twice or select **"Reload"** from the **Developer Menu** (Ctrl + M (on Window and Linux) or Cmd ⌘ + M (on macOS)) to see your changes!
55 |
56 | For **iOS**: Hit Cmd ⌘ + R in your iOS Simulator to reload the app and see your changes!
57 |
58 | ## Congratulations! :tada:
59 |
60 | You've successfully run and modified your React Native App. :partying_face:
61 |
62 | ### Now what?
63 |
64 | - If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps).
65 | - If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started).
66 |
67 | # Troubleshooting
68 |
69 | If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page.
70 |
71 | # Learn More
72 |
73 | To learn more about React Native, take a look at the following resources:
74 |
75 | - [React Native Website](https://reactnative.dev) - learn more about React Native.
76 | - [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment.
77 | - [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**.
78 | - [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts.
79 | - [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native.
80 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 | apply plugin: "org.jetbrains.kotlin.android"
3 | apply plugin: "com.facebook.react"
4 |
5 | /**
6 | * This is the configuration block to customize your React Native Android app.
7 | * By default you don't need to apply any configuration, just uncomment the lines you need.
8 | */
9 | react {
10 | /* Folders */
11 | // The root of your project, i.e. where "package.json" lives. Default is '../..'
12 | // root = file("../../")
13 | // The folder where the react-native NPM package is. Default is ../../node_modules/react-native
14 | // reactNativeDir = file("../../node_modules/react-native")
15 | // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
16 | // codegenDir = file("../../node_modules/@react-native/codegen")
17 | // The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
18 | // cliFile = file("../../node_modules/react-native/cli.js")
19 |
20 | /* Variants */
21 | // The list of variants to that are debuggable. For those we're going to
22 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'.
23 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
24 | // debuggableVariants = ["liteDebug", "prodDebug"]
25 |
26 | /* Bundling */
27 | // A list containing the node command and its flags. Default is just 'node'.
28 | // nodeExecutableAndArgs = ["node"]
29 | //
30 | // The command to run when bundling. By default is 'bundle'
31 | // bundleCommand = "ram-bundle"
32 | //
33 | // The path to the CLI configuration file. Default is empty.
34 | // bundleConfig = file(../rn-cli.config.js)
35 | //
36 | // The name of the generated asset file containing your JS bundle
37 | // bundleAssetName = "MyApplication.android.bundle"
38 | //
39 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
40 | // entryFile = file("../js/MyApplication.android.js")
41 | //
42 | // A list of extra flags to pass to the 'bundle' commands.
43 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
44 | // extraPackagerArgs = []
45 |
46 | /* Hermes Commands */
47 | // The hermes compiler command to run. By default it is 'hermesc'
48 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
49 | //
50 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
51 | // hermesFlags = ["-O", "-output-source-map"]
52 |
53 | /* Autolinking */
54 | autolinkLibrariesWithApp()
55 | }
56 |
57 | /**
58 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
59 | */
60 | def enableProguardInReleaseBuilds = false
61 |
62 | /**
63 | * The preferred build flavor of JavaScriptCore (JSC)
64 | *
65 | * For example, to use the international variant, you can use:
66 | * `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+`
67 | *
68 | * The international variant includes ICU i18n library and necessary data
69 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
70 | * give correct results when using with locales other than en-US. Note that
71 | * this variant is about 6MiB larger per architecture than default.
72 | */
73 | def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
74 |
75 | android {
76 | ndkVersion rootProject.ext.ndkVersion
77 | buildToolsVersion rootProject.ext.buildToolsVersion
78 | compileSdk rootProject.ext.compileSdkVersion
79 |
80 | namespace "pouchdbadapterreactnativesqlite.example"
81 | defaultConfig {
82 | applicationId "pouchdbadapterreactnativesqlite.example"
83 | minSdkVersion rootProject.ext.minSdkVersion
84 | targetSdkVersion rootProject.ext.targetSdkVersion
85 | versionCode 1
86 | versionName "1.0"
87 | }
88 | signingConfigs {
89 | debug {
90 | storeFile file('debug.keystore')
91 | storePassword 'android'
92 | keyAlias 'androiddebugkey'
93 | keyPassword 'android'
94 | }
95 | }
96 | buildTypes {
97 | debug {
98 | signingConfig signingConfigs.debug
99 | }
100 | release {
101 | // Caution! In production, you need to generate your own keystore file.
102 | // see https://reactnative.dev/docs/signed-apk-android.
103 | signingConfig signingConfigs.debug
104 | minifyEnabled enableProguardInReleaseBuilds
105 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
106 | }
107 | }
108 | }
109 |
110 | dependencies {
111 | // The version of react-native is set by the React Native Gradle Plugin
112 | implementation("com.facebook.react:react-android")
113 |
114 | if (hermesEnabled.toBoolean()) {
115 | implementation("com.facebook.react:hermes-android")
116 | } else {
117 | implementation jscFlavor
118 | }
119 | }
120 |
121 | def isNewArchitectureEnabled() {
122 | return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
123 | }
124 |
125 | //if (isNewArchitectureEnabled()) {
126 | // // Since our library doesn't invoke codegen automatically we need to do it here.
127 | // tasks.register('invokeLibraryCodegen', Exec) {
128 | // workingDir "$rootDir/../../"
129 | // def isWindows = System.getProperty('os.name').toLowerCase().contains('windows')
130 | //
131 | // if (isWindows) {
132 | // commandLine 'cmd', '/c', 'npx bob build --target codegen'
133 | // } else {
134 | // commandLine 'sh', '-c', 'npx bob build --target codegen'
135 | // }
136 | // }
137 | // preBuild.dependsOn invokeLibraryCodegen
138 | //}
139 |
--------------------------------------------------------------------------------
/example/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftzdog/pouchdb-adapter-react-native-sqlite/c493965939ddfd7b9e00da9e6a946160c8f64c2d/example/android/app/debug.keystore
--------------------------------------------------------------------------------
/example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/pouchdbadapterreactnativesqlite/example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package pouchdbadapterreactnativesqlite.example
2 |
3 | import com.facebook.react.ReactActivity
4 | import com.facebook.react.ReactActivityDelegate
5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
6 | import com.facebook.react.defaults.DefaultReactActivityDelegate
7 |
8 | class MainActivity : ReactActivity() {
9 |
10 | /**
11 | * Returns the name of the main component registered from JavaScript. This is used to schedule
12 | * rendering of the component.
13 | */
14 | override fun getMainComponentName(): String = "PouchdbAdapterReactNativeSqliteExample"
15 |
16 | /**
17 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
18 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
19 | */
20 | override fun createReactActivityDelegate(): ReactActivityDelegate =
21 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
22 | }
23 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/pouchdbadapterreactnativesqlite/example/MainApplication.kt:
--------------------------------------------------------------------------------
1 | package pouchdbadapterreactnativesqlite.example
2 |
3 | import android.app.Application
4 | import com.facebook.react.PackageList
5 | import com.facebook.react.ReactApplication
6 | import com.facebook.react.ReactHost
7 | import com.facebook.react.ReactNativeHost
8 | import com.facebook.react.ReactPackage
9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
11 | import com.facebook.react.defaults.DefaultReactNativeHost
12 | import com.facebook.react.soloader.OpenSourceMergedSoMapping
13 | import com.facebook.soloader.SoLoader
14 |
15 | class MainApplication : Application(), ReactApplication {
16 |
17 | override val reactNativeHost: ReactNativeHost =
18 | object : DefaultReactNativeHost(this) {
19 | override fun getPackages(): List =
20 | PackageList(this).packages.apply {
21 | // Packages that cannot be autolinked yet can be added manually here, for example:
22 | // add(MyReactNativePackage())
23 | }
24 |
25 | override fun getJSMainModuleName(): String = "index"
26 |
27 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
28 |
29 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
30 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
31 | }
32 |
33 | override val reactHost: ReactHost
34 | get() = getDefaultReactHost(applicationContext, reactNativeHost)
35 |
36 | override fun onCreate() {
37 | super.onCreate()
38 | SoLoader.init(this, OpenSourceMergedSoMapping)
39 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
40 | // If you opted-in for the New Architecture, we load the native entry point for this app.
41 | load()
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
22 |
23 |
24 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftzdog/pouchdb-adapter-react-native-sqlite/c493965939ddfd7b9e00da9e6a946160c8f64c2d/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftzdog/pouchdb-adapter-react-native-sqlite/c493965939ddfd7b9e00da9e6a946160c8f64c2d/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftzdog/pouchdb-adapter-react-native-sqlite/c493965939ddfd7b9e00da9e6a946160c8f64c2d/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftzdog/pouchdb-adapter-react-native-sqlite/c493965939ddfd7b9e00da9e6a946160c8f64c2d/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftzdog/pouchdb-adapter-react-native-sqlite/c493965939ddfd7b9e00da9e6a946160c8f64c2d/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftzdog/pouchdb-adapter-react-native-sqlite/c493965939ddfd7b9e00da9e6a946160c8f64c2d/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftzdog/pouchdb-adapter-react-native-sqlite/c493965939ddfd7b9e00da9e6a946160c8f64c2d/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftzdog/pouchdb-adapter-react-native-sqlite/c493965939ddfd7b9e00da9e6a946160c8f64c2d/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftzdog/pouchdb-adapter-react-native-sqlite/c493965939ddfd7b9e00da9e6a946160c8f64c2d/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftzdog/pouchdb-adapter-react-native-sqlite/c493965939ddfd7b9e00da9e6a946160c8f64c2d/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PouchdbAdapterReactNativeSqliteExample
3 |
4 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | buildToolsVersion = "35.0.0"
4 | minSdkVersion = 24
5 | compileSdkVersion = 35
6 | targetSdkVersion = 35
7 | ndkVersion = "27.1.12297006"
8 | kotlinVersion = "2.0.21"
9 | }
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | dependencies {
15 | classpath("com.android.tools.build:gradle")
16 | classpath("com.facebook.react:react-native-gradle-plugin")
17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
18 | }
19 | }
20 |
21 | apply plugin: "com.facebook.react.rootproject"
22 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 |
25 | # Use this property to specify which architecture you want to build.
26 | # You can also override it from the CLI using
27 | # ./gradlew -PreactNativeArchitectures=x86_64
28 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
29 |
30 | # Use this property to enable support to the new architecture.
31 | # This will allow you to use TurboModules and the Fabric render in
32 | # your application. You should enable this flag either if you want
33 | # to write custom TurboModules/Fabric components OR use libraries that
34 | # are providing them.
35 | newArchEnabled=true
36 |
37 | # Use this property to enable or disable the Hermes JS engine.
38 | # If set to false, you will be using JSC instead.
39 | hermesEnabled=true
40 |
41 | newArchEnabled=true
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftzdog/pouchdb-adapter-react-native-sqlite/c493965939ddfd7b9e00da9e6a946160c8f64c2d/example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | #
20 |
21 | ##############################################################################
22 | #
23 | # Gradle start up script for POSIX generated by Gradle.
24 | #
25 | # Important for running:
26 | #
27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
28 | # noncompliant, but you have some other compliant shell such as ksh or
29 | # bash, then to run this script, type that shell name before the whole
30 | # command line, like:
31 | #
32 | # ksh Gradle
33 | #
34 | # Busybox and similar reduced shells will NOT work, because this script
35 | # requires all of these POSIX shell features:
36 | # * functions;
37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
39 | # * compound commands having a testable exit status, especially «case»;
40 | # * various built-in commands including «command», «set», and «ulimit».
41 | #
42 | # Important for patching:
43 | #
44 | # (2) This script targets any POSIX shell, so it avoids extensions provided
45 | # by Bash, Ksh, etc; in particular arrays are avoided.
46 | #
47 | # The "traditional" practice of packing multiple parameters into a
48 | # space-separated string is a well documented source of bugs and security
49 | # problems, so this is (mostly) avoided, by progressively accumulating
50 | # options in "$@", and eventually passing that to Java.
51 | #
52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
54 | # see the in-line comments for details.
55 | #
56 | # There are tweaks for specific operating systems such as AIX, CygWin,
57 | # Darwin, MinGW, and NonStop.
58 | #
59 | # (3) This script is generated from the Groovy template
60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
61 | # within the Gradle project.
62 | #
63 | # You can find Gradle at https://github.com/gradle/gradle/.
64 | #
65 | ##############################################################################
66 |
67 | # Attempt to set APP_HOME
68 |
69 | # Resolve links: $0 may be a link
70 | app_path=$0
71 |
72 | # Need this for daisy-chained symlinks.
73 | while
74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
75 | [ -h "$app_path" ]
76 | do
77 | ls=$(ls -ld "$app_path")
78 | link=${ls#*' -> '}
79 | case $link in #(
80 | /*) app_path=$link ;; #(
81 | *) app_path=$APP_HOME$link ;;
82 | esac
83 | done
84 |
85 | # This is normally unused
86 | # shellcheck disable=SC2034
87 | APP_BASE_NAME=${0##*/}
88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89 | APP_HOME=$(cd -P "${APP_HOME:-./}" >/dev/null && printf '%s\n' "$PWD") || exit
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn() {
95 | echo "$*"
96 | } >&2
97 |
98 | die() {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$(uname)" in #(
111 | CYGWIN*) cygwin=true ;; #(
112 | Darwin*) darwin=true ;; #(
113 | MSYS* | MINGW*) msys=true ;; #(
114 | NONSTOP*) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 | # Determine the Java command to use to start the JVM.
120 | if [ -n "$JAVA_HOME" ]; then
121 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then
122 | # IBM's JDK on AIX uses strange locations for the executables
123 | JAVACMD=$JAVA_HOME/jre/sh/java
124 | else
125 | JAVACMD=$JAVA_HOME/bin/java
126 | fi
127 | if [ ! -x "$JAVACMD" ]; then
128 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
129 |
130 | Please set the JAVA_HOME variable in your environment to match the
131 | location of your Java installation."
132 | fi
133 | else
134 | JAVACMD=java
135 | if ! command -v java >/dev/null 2>&1; then
136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 | fi
142 |
143 | # Increase the maximum file descriptors if we can.
144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop"; then
145 | case $MAX_FD in #(
146 | max*)
147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148 | # shellcheck disable=SC2039,SC3045
149 | MAX_FD=$(ulimit -H -n) ||
150 | warn "Could not query maximum file descriptor limit"
151 | ;;
152 | esac
153 | case $MAX_FD in #(
154 | '' | soft) : ;; #(
155 | *)
156 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
157 | # shellcheck disable=SC2039,SC3045
158 | ulimit -n "$MAX_FD" ||
159 | warn "Could not set maximum file descriptor limit to $MAX_FD"
160 | ;;
161 | esac
162 | fi
163 |
164 | # Collect all arguments for the java command, stacking in reverse order:
165 | # * args from the command line
166 | # * the main class name
167 | # * -classpath
168 | # * -D...appname settings
169 | # * --module-path (only if needed)
170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
171 |
172 | # For Cygwin or MSYS, switch paths to Windows format before running java
173 | if "$cygwin" || "$msys"; then
174 | APP_HOME=$(cygpath --path --mixed "$APP_HOME")
175 | CLASSPATH=$(cygpath --path --mixed "$CLASSPATH")
176 |
177 | JAVACMD=$(cygpath --unix "$JAVACMD")
178 |
179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
180 | for arg; do
181 | if
182 | case $arg in #(
183 | -*) false ;; # don't mess with options #(
184 | /?*)
185 | t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
186 | [ -e "$t" ]
187 | ;; #(
188 | *) false ;;
189 | esac
190 | then
191 | arg=$(cygpath --path --ignore --mixed "$arg")
192 | fi
193 | # Roll the args list around exactly as many times as the number of
194 | # args, so each arg winds up back in the position where it started, but
195 | # possibly modified.
196 | #
197 | # NB: a `for` loop captures its iteration list before it begins, so
198 | # changing the positional parameters here affects neither the number of
199 | # iterations, nor the values presented in `arg`.
200 | shift # remove old arg
201 | set -- "$@" "$arg" # push replacement arg
202 | done
203 | fi
204 |
205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
207 |
208 | # Collect all arguments for the java command:
209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
210 | # and any embedded shellness will be escaped.
211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
212 | # treated as '${Hostname}' itself on the command line.
213 |
214 | set -- \
215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
216 | -classpath "$CLASSPATH" \
217 | org.gradle.wrapper.GradleWrapperMain \
218 | "$@"
219 |
220 | # Stop when "xargs" is not available.
221 | if ! command -v xargs >/dev/null 2>&1; then
222 | die "xargs is not available"
223 | fi
224 |
225 | # Use "xargs" to parse quoted args.
226 | #
227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
228 | #
229 | # In Bash we could simply go:
230 | #
231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
232 | # set -- "${ARGS[@]}" "$@"
233 | #
234 | # but POSIX shell has neither arrays nor command substitution, so instead we
235 | # post-process each arg (as a line of input to sed) to backslash-escape any
236 | # character that might be a shell metacharacter, then use eval to reverse
237 | # that process (while maintaining the separation between arguments), and wrap
238 | # the whole thing up as a single "set" statement.
239 | #
240 | # This will of course break if any of these variables contains a newline or
241 | # an unmatched quote.
242 | #
243 |
244 | eval "set -- $(
245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
246 | xargs -n1 |
247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
248 | tr '\n' ' '
249 | )" '"$@"'
250 |
251 | exec "$JAVACMD" "$@"
252 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
2 | plugins { id("com.facebook.react.settings") }
3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
4 | rootProject.name = 'pouchdbadapterreactnativesqlite.example'
5 | include ':app'
6 | includeBuild('../node_modules/@react-native/gradle-plugin')
7 |
--------------------------------------------------------------------------------
/example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "PouchdbAdapterReactNativeSqliteExample",
3 | "displayName": "PouchdbAdapterReactNativeSqliteExample"
4 | }
5 |
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const { getConfig } = require('react-native-builder-bob/babel-config')
3 | const pkg = require('../package.json')
4 |
5 | const root = path.resolve(__dirname, '..')
6 |
7 | module.exports = getConfig(
8 | {
9 | presets: ['module:@react-native/babel-preset'],
10 | plugins: [
11 | [
12 | 'module-resolver',
13 | {
14 | extensions: ['.tsx', '.ts', '.js', '.json'],
15 | alias: {
16 | '@': './src',
17 | 'crypto': 'react-native-quick-crypto',
18 | 'stream': 'readable-stream',
19 | 'buffer': '@craftzdog/react-native-buffer',
20 | 'pouchdb-errors': '@craftzdog/pouchdb-errors',
21 | },
22 | },
23 | ],
24 | ['module:react-native-dotenv'],
25 | ],
26 | },
27 | { root, pkg }
28 | )
29 |
--------------------------------------------------------------------------------
/example/definitions/pouchdb.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'pouchdb-adapter-http'
2 | declare module 'pouchdb-replication'
3 | declare module 'pouchdb-mapreduce'
4 |
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | import './src/logger'
2 | import { AppRegistry } from 'react-native'
3 | import App from './src/App'
4 | import { name as appName } from './app.json'
5 |
6 | AppRegistry.registerComponent(appName, () => App)
7 |
--------------------------------------------------------------------------------
/example/ios/.xcode.env:
--------------------------------------------------------------------------------
1 | # This `.xcode.env` file is versioned and is used to source the environment
2 | # used when running script phases inside Xcode.
3 | # To customize your local environment, you can create an `.xcode.env.local`
4 | # file that is not versioned.
5 |
6 | # NODE_BINARY variable contains the PATH to the node executable.
7 | #
8 | # Customize the NODE_BINARY variable here.
9 | # For example, to use nvm with brew, add the following line
10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use
11 | export NODE_BINARY=$(command -v node)
12 |
--------------------------------------------------------------------------------
/example/ios/File.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | // PouchdbAdapterReactNativeSqliteExample
4 | //
5 |
6 | import Foundation
7 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | ENV['RCT_NEW_ARCH_ENABLED'] = '1'
2 |
3 | # Resolve react_native_pods.rb with node to allow for hoisting
4 | require Pod::Executable.execute_command('node', ['-p',
5 | 'require.resolve(
6 | "react-native/scripts/react_native_pods.rb",
7 | {paths: [process.argv[1]]},
8 | )', __dir__]).strip
9 |
10 | platform :ios, min_ios_version_supported
11 | prepare_react_native_project!
12 |
13 | linkage = ENV['USE_FRAMEWORKS']
14 | if linkage != nil
15 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
16 | use_frameworks! :linkage => linkage.to_sym
17 | end
18 |
19 | target 'PouchdbAdapterReactNativeSqliteExample' do
20 | config = use_native_modules!
21 |
22 | use_react_native!(
23 | :path => config[:reactNativePath],
24 | # An absolute path to your application root.
25 | :app_path => "#{Pod::Config.instance.installation_root}/.."
26 | )
27 |
28 |
29 | pre_install do |installer|
30 | system("cd ../../ && npx bob build --target codegen")
31 | end
32 |
33 | post_install do |installer|
34 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
35 | react_native_post_install(
36 | installer,
37 | config[:reactNativePath],
38 | :mac_catalyst_enabled => false,
39 | # :ccache_enabled => true
40 | )
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExample-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 00E356F31AD99517003FC87E /* PouchdbAdapterReactNativeSqliteExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* PouchdbAdapterReactNativeSqliteExampleTests.m */; };
11 | 0C80B921A6F3F58F76C31292 /* libPods-PouchdbAdapterReactNativeSqliteExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-PouchdbAdapterReactNativeSqliteExample.a */; };
12 | 0CA2698B6DF7581835E7F028 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
13 | 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
14 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
15 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
16 | 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXContainerItemProxy section */
20 | 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
21 | isa = PBXContainerItemProxy;
22 | containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
23 | proxyType = 1;
24 | remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
25 | remoteInfo = PouchdbAdapterReactNativeSqliteExample;
26 | };
27 | /* End PBXContainerItemProxy section */
28 |
29 | /* Begin PBXFileReference section */
30 | 00E356EE1AD99517003FC87E /* PouchdbAdapterReactNativeSqliteExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PouchdbAdapterReactNativeSqliteExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
31 | 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
32 | 00E356F21AD99517003FC87E /* PouchdbAdapterReactNativeSqliteExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PouchdbAdapterReactNativeSqliteExampleTests.m; sourceTree = ""; };
33 | 13B07F961A680F5B00A75B9A /* PouchdbAdapterReactNativeSqliteExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PouchdbAdapterReactNativeSqliteExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
34 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = PouchdbAdapterReactNativeSqliteExample/AppDelegate.h; sourceTree = ""; };
35 | 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = PouchdbAdapterReactNativeSqliteExample/AppDelegate.mm; sourceTree = ""; };
36 | 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = PouchdbAdapterReactNativeSqliteExample/Images.xcassets; sourceTree = ""; };
37 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = PouchdbAdapterReactNativeSqliteExample/Info.plist; sourceTree = ""; };
38 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = PouchdbAdapterReactNativeSqliteExample/main.m; sourceTree = ""; };
39 | 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = PouchdbAdapterReactNativeSqliteExample/PrivacyInfo.xcprivacy; sourceTree = ""; };
40 | 3B4392A12AC88292D35C810B /* Pods-PouchdbAdapterReactNativeSqliteExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PouchdbAdapterReactNativeSqliteExample.debug.xcconfig"; path = "Target Support Files/Pods-PouchdbAdapterReactNativeSqliteExample/Pods-PouchdbAdapterReactNativeSqliteExample.debug.xcconfig"; sourceTree = ""; };
41 | 5709B34CF0A7D63546082F79 /* Pods-PouchdbAdapterReactNativeSqliteExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PouchdbAdapterReactNativeSqliteExample.release.xcconfig"; path = "Target Support Files/Pods-PouchdbAdapterReactNativeSqliteExample/Pods-PouchdbAdapterReactNativeSqliteExample.release.xcconfig"; sourceTree = ""; };
42 | 5DCACB8F33CDC322A6C60F78 /* libPods-PouchdbAdapterReactNativeSqliteExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PouchdbAdapterReactNativeSqliteExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
43 | 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = PouchdbAdapterReactNativeSqliteExample/LaunchScreen.storyboard; sourceTree = ""; };
44 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 00E356EB1AD99517003FC87E /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
56 | isa = PBXFrameworksBuildPhase;
57 | buildActionMask = 2147483647;
58 | files = (
59 | 0C80B921A6F3F58F76C31292 /* libPods-PouchdbAdapterReactNativeSqliteExample.a in Frameworks */,
60 | );
61 | runOnlyForDeploymentPostprocessing = 0;
62 | };
63 | /* End PBXFrameworksBuildPhase section */
64 |
65 | /* Begin PBXGroup section */
66 | 00E356EF1AD99517003FC87E /* PouchdbAdapterReactNativeSqliteExampleTests */ = {
67 | isa = PBXGroup;
68 | children = (
69 | 00E356F21AD99517003FC87E /* PouchdbAdapterReactNativeSqliteExampleTests.m */,
70 | 00E356F01AD99517003FC87E /* Supporting Files */,
71 | );
72 | path = PouchdbAdapterReactNativeSqliteExampleTests;
73 | sourceTree = "";
74 | };
75 | 00E356F01AD99517003FC87E /* Supporting Files */ = {
76 | isa = PBXGroup;
77 | children = (
78 | 00E356F11AD99517003FC87E /* Info.plist */,
79 | );
80 | name = "Supporting Files";
81 | sourceTree = "";
82 | };
83 | 13B07FAE1A68108700A75B9A /* PouchdbAdapterReactNativeSqliteExample */ = {
84 | isa = PBXGroup;
85 | children = (
86 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */,
87 | 13B07FB01A68108700A75B9A /* AppDelegate.mm */,
88 | 13B07FB51A68108700A75B9A /* Images.xcassets */,
89 | 13B07FB61A68108700A75B9A /* Info.plist */,
90 | 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
91 | 13B07FB71A68108700A75B9A /* main.m */,
92 | 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
93 | );
94 | name = PouchdbAdapterReactNativeSqliteExample;
95 | sourceTree = "";
96 | };
97 | 2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
98 | isa = PBXGroup;
99 | children = (
100 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
101 | 5DCACB8F33CDC322A6C60F78 /* libPods-PouchdbAdapterReactNativeSqliteExample.a */,
102 | );
103 | name = Frameworks;
104 | sourceTree = "";
105 | };
106 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
107 | isa = PBXGroup;
108 | children = (
109 | );
110 | name = Libraries;
111 | sourceTree = "";
112 | };
113 | 83CBB9F61A601CBA00E9B192 = {
114 | isa = PBXGroup;
115 | children = (
116 | 13B07FAE1A68108700A75B9A /* PouchdbAdapterReactNativeSqliteExample */,
117 | 832341AE1AAA6A7D00B99B32 /* Libraries */,
118 | 00E356EF1AD99517003FC87E /* PouchdbAdapterReactNativeSqliteExampleTests */,
119 | 83CBBA001A601CBA00E9B192 /* Products */,
120 | 2D16E6871FA4F8E400B85C8A /* Frameworks */,
121 | BBD78D7AC51CEA395F1C20DB /* Pods */,
122 | );
123 | indentWidth = 2;
124 | sourceTree = "";
125 | tabWidth = 2;
126 | usesTabs = 0;
127 | };
128 | 83CBBA001A601CBA00E9B192 /* Products */ = {
129 | isa = PBXGroup;
130 | children = (
131 | 13B07F961A680F5B00A75B9A /* PouchdbAdapterReactNativeSqliteExample.app */,
132 | 00E356EE1AD99517003FC87E /* PouchdbAdapterReactNativeSqliteExampleTests.xctest */,
133 | );
134 | name = Products;
135 | sourceTree = "";
136 | };
137 | BBD78D7AC51CEA395F1C20DB /* Pods */ = {
138 | isa = PBXGroup;
139 | children = (
140 | 3B4392A12AC88292D35C810B /* Pods-PouchdbAdapterReactNativeSqliteExample.debug.xcconfig */,
141 | 5709B34CF0A7D63546082F79 /* Pods-PouchdbAdapterReactNativeSqliteExample.release.xcconfig */,
142 | );
143 | path = Pods;
144 | sourceTree = "";
145 | };
146 | /* End PBXGroup section */
147 |
148 | /* Begin PBXNativeTarget section */
149 | 00E356ED1AD99517003FC87E /* PouchdbAdapterReactNativeSqliteExampleTests */ = {
150 | isa = PBXNativeTarget;
151 | buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "PouchdbAdapterReactNativeSqliteExampleTests" */;
152 | buildPhases = (
153 | 00E356EA1AD99517003FC87E /* Sources */,
154 | 00E356EB1AD99517003FC87E /* Frameworks */,
155 | 00E356EC1AD99517003FC87E /* Resources */,
156 | );
157 | buildRules = (
158 | );
159 | dependencies = (
160 | 00E356F51AD99517003FC87E /* PBXTargetDependency */,
161 | );
162 | name = PouchdbAdapterReactNativeSqliteExampleTests;
163 | productName = PouchdbAdapterReactNativeSqliteExampleTests;
164 | productReference = 00E356EE1AD99517003FC87E /* PouchdbAdapterReactNativeSqliteExampleTests.xctest */;
165 | productType = "com.apple.product-type.bundle.unit-test";
166 | };
167 | 13B07F861A680F5B00A75B9A /* PouchdbAdapterReactNativeSqliteExample */ = {
168 | isa = PBXNativeTarget;
169 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "PouchdbAdapterReactNativeSqliteExample" */;
170 | buildPhases = (
171 | C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */,
172 | 13B07F871A680F5B00A75B9A /* Sources */,
173 | 13B07F8C1A680F5B00A75B9A /* Frameworks */,
174 | 13B07F8E1A680F5B00A75B9A /* Resources */,
175 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
176 | 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */,
177 | E235C05ADACE081382539298 /* [CP] Copy Pods Resources */,
178 | );
179 | buildRules = (
180 | );
181 | dependencies = (
182 | );
183 | name = PouchdbAdapterReactNativeSqliteExample;
184 | productName = PouchdbAdapterReactNativeSqliteExample;
185 | productReference = 13B07F961A680F5B00A75B9A /* PouchdbAdapterReactNativeSqliteExample.app */;
186 | productType = "com.apple.product-type.application";
187 | };
188 | /* End PBXNativeTarget section */
189 |
190 | /* Begin PBXProject section */
191 | 83CBB9F71A601CBA00E9B192 /* Project object */ = {
192 | isa = PBXProject;
193 | attributes = {
194 | LastUpgradeCheck = 1210;
195 | TargetAttributes = {
196 | 00E356ED1AD99517003FC87E = {
197 | CreatedOnToolsVersion = 6.2;
198 | TestTargetID = 13B07F861A680F5B00A75B9A;
199 | };
200 | 13B07F861A680F5B00A75B9A = {
201 | LastSwiftMigration = 1120;
202 | };
203 | };
204 | };
205 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "PouchdbAdapterReactNativeSqliteExample" */;
206 | compatibilityVersion = "Xcode 12.0";
207 | developmentRegion = en;
208 | hasScannedForEncodings = 0;
209 | knownRegions = (
210 | en,
211 | Base,
212 | );
213 | mainGroup = 83CBB9F61A601CBA00E9B192;
214 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
215 | projectDirPath = "";
216 | projectRoot = "";
217 | targets = (
218 | 13B07F861A680F5B00A75B9A /* PouchdbAdapterReactNativeSqliteExample */,
219 | 00E356ED1AD99517003FC87E /* PouchdbAdapterReactNativeSqliteExampleTests */,
220 | );
221 | };
222 | /* End PBXProject section */
223 |
224 | /* Begin PBXResourcesBuildPhase section */
225 | 00E356EC1AD99517003FC87E /* Resources */ = {
226 | isa = PBXResourcesBuildPhase;
227 | buildActionMask = 2147483647;
228 | files = (
229 | );
230 | runOnlyForDeploymentPostprocessing = 0;
231 | };
232 | 13B07F8E1A680F5B00A75B9A /* Resources */ = {
233 | isa = PBXResourcesBuildPhase;
234 | buildActionMask = 2147483647;
235 | files = (
236 | 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
237 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
238 | 0CA2698B6DF7581835E7F028 /* PrivacyInfo.xcprivacy in Resources */,
239 | );
240 | runOnlyForDeploymentPostprocessing = 0;
241 | };
242 | /* End PBXResourcesBuildPhase section */
243 |
244 | /* Begin PBXShellScriptBuildPhase section */
245 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
246 | isa = PBXShellScriptBuildPhase;
247 | buildActionMask = 2147483647;
248 | files = (
249 | );
250 | inputPaths = (
251 | "$(SRCROOT)/.xcode.env.local",
252 | "$(SRCROOT)/.xcode.env",
253 | );
254 | name = "Bundle React Native code and images";
255 | outputPaths = (
256 | );
257 | runOnlyForDeploymentPostprocessing = 0;
258 | shellPath = /bin/sh;
259 | 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";
260 | };
261 | 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
262 | isa = PBXShellScriptBuildPhase;
263 | buildActionMask = 2147483647;
264 | files = (
265 | );
266 | inputFileListPaths = (
267 | "${PODS_ROOT}/Target Support Files/Pods-PouchdbAdapterReactNativeSqliteExample/Pods-PouchdbAdapterReactNativeSqliteExample-frameworks-${CONFIGURATION}-input-files.xcfilelist",
268 | );
269 | name = "[CP] Embed Pods Frameworks";
270 | outputFileListPaths = (
271 | "${PODS_ROOT}/Target Support Files/Pods-PouchdbAdapterReactNativeSqliteExample/Pods-PouchdbAdapterReactNativeSqliteExample-frameworks-${CONFIGURATION}-output-files.xcfilelist",
272 | );
273 | runOnlyForDeploymentPostprocessing = 0;
274 | shellPath = /bin/sh;
275 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PouchdbAdapterReactNativeSqliteExample/Pods-PouchdbAdapterReactNativeSqliteExample-frameworks.sh\"\n";
276 | showEnvVarsInLog = 0;
277 | };
278 | C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = {
279 | isa = PBXShellScriptBuildPhase;
280 | buildActionMask = 2147483647;
281 | files = (
282 | );
283 | inputFileListPaths = (
284 | );
285 | inputPaths = (
286 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
287 | "${PODS_ROOT}/Manifest.lock",
288 | );
289 | name = "[CP] Check Pods Manifest.lock";
290 | outputFileListPaths = (
291 | );
292 | outputPaths = (
293 | "$(DERIVED_FILE_DIR)/Pods-PouchdbAdapterReactNativeSqliteExample-checkManifestLockResult.txt",
294 | );
295 | runOnlyForDeploymentPostprocessing = 0;
296 | shellPath = /bin/sh;
297 | 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";
298 | showEnvVarsInLog = 0;
299 | };
300 | E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
301 | isa = PBXShellScriptBuildPhase;
302 | buildActionMask = 2147483647;
303 | files = (
304 | );
305 | inputFileListPaths = (
306 | "${PODS_ROOT}/Target Support Files/Pods-PouchdbAdapterReactNativeSqliteExample/Pods-PouchdbAdapterReactNativeSqliteExample-resources-${CONFIGURATION}-input-files.xcfilelist",
307 | );
308 | name = "[CP] Copy Pods Resources";
309 | outputFileListPaths = (
310 | "${PODS_ROOT}/Target Support Files/Pods-PouchdbAdapterReactNativeSqliteExample/Pods-PouchdbAdapterReactNativeSqliteExample-resources-${CONFIGURATION}-output-files.xcfilelist",
311 | );
312 | runOnlyForDeploymentPostprocessing = 0;
313 | shellPath = /bin/sh;
314 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PouchdbAdapterReactNativeSqliteExample/Pods-PouchdbAdapterReactNativeSqliteExample-resources.sh\"\n";
315 | showEnvVarsInLog = 0;
316 | };
317 | /* End PBXShellScriptBuildPhase section */
318 |
319 | /* Begin PBXSourcesBuildPhase section */
320 | 00E356EA1AD99517003FC87E /* Sources */ = {
321 | isa = PBXSourcesBuildPhase;
322 | buildActionMask = 2147483647;
323 | files = (
324 | 00E356F31AD99517003FC87E /* PouchdbAdapterReactNativeSqliteExampleTests.m in Sources */,
325 | );
326 | runOnlyForDeploymentPostprocessing = 0;
327 | };
328 | 13B07F871A680F5B00A75B9A /* Sources */ = {
329 | isa = PBXSourcesBuildPhase;
330 | buildActionMask = 2147483647;
331 | files = (
332 | 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
333 | 13B07FC11A68108700A75B9A /* main.m in Sources */,
334 | );
335 | runOnlyForDeploymentPostprocessing = 0;
336 | };
337 | /* End PBXSourcesBuildPhase section */
338 |
339 | /* Begin PBXTargetDependency section */
340 | 00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
341 | isa = PBXTargetDependency;
342 | target = 13B07F861A680F5B00A75B9A /* PouchdbAdapterReactNativeSqliteExample */;
343 | targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
344 | };
345 | /* End PBXTargetDependency section */
346 |
347 | /* Begin XCBuildConfiguration section */
348 | 00E356F61AD99517003FC87E /* Debug */ = {
349 | isa = XCBuildConfiguration;
350 | buildSettings = {
351 | BUNDLE_LOADER = "$(TEST_HOST)";
352 | GCC_PREPROCESSOR_DEFINITIONS = (
353 | "DEBUG=1",
354 | "$(inherited)",
355 | );
356 | INFOPLIST_FILE = PouchdbAdapterReactNativeSqliteExampleTests/Info.plist;
357 | IPHONEOS_DEPLOYMENT_TARGET = 15.1;
358 | LD_RUNPATH_SEARCH_PATHS = (
359 | "$(inherited)",
360 | "@executable_path/Frameworks",
361 | "@loader_path/Frameworks",
362 | );
363 | OTHER_LDFLAGS = (
364 | "-ObjC",
365 | "-lc++",
366 | "$(inherited)",
367 | );
368 | PRODUCT_BUNDLE_IDENTIFIER = pouchdbadapterreactnativesqlite.example;
369 | PRODUCT_NAME = "$(TARGET_NAME)";
370 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PouchdbAdapterReactNativeSqliteExample.app/PouchdbAdapterReactNativeSqliteExample";
371 | };
372 | name = Debug;
373 | };
374 | 00E356F71AD99517003FC87E /* Release */ = {
375 | isa = XCBuildConfiguration;
376 | buildSettings = {
377 | BUNDLE_LOADER = "$(TEST_HOST)";
378 | COPY_PHASE_STRIP = NO;
379 | INFOPLIST_FILE = PouchdbAdapterReactNativeSqliteExampleTests/Info.plist;
380 | IPHONEOS_DEPLOYMENT_TARGET = 15.1;
381 | LD_RUNPATH_SEARCH_PATHS = (
382 | "$(inherited)",
383 | "@executable_path/Frameworks",
384 | "@loader_path/Frameworks",
385 | );
386 | OTHER_LDFLAGS = (
387 | "-ObjC",
388 | "-lc++",
389 | "$(inherited)",
390 | );
391 | PRODUCT_BUNDLE_IDENTIFIER = pouchdbadapterreactnativesqlite.example;
392 | PRODUCT_NAME = "$(TARGET_NAME)";
393 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PouchdbAdapterReactNativeSqliteExample.app/PouchdbAdapterReactNativeSqliteExample";
394 | };
395 | name = Release;
396 | };
397 | 13B07F941A680F5B00A75B9A /* Debug */ = {
398 | isa = XCBuildConfiguration;
399 | baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-PouchdbAdapterReactNativeSqliteExample.debug.xcconfig */;
400 | buildSettings = {
401 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
402 | CLANG_ENABLE_MODULES = YES;
403 | CURRENT_PROJECT_VERSION = 1;
404 | ENABLE_BITCODE = NO;
405 | INFOPLIST_FILE = PouchdbAdapterReactNativeSqliteExample/Info.plist;
406 | IPHONEOS_DEPLOYMENT_TARGET = 15.1;
407 | LD_RUNPATH_SEARCH_PATHS = (
408 | "$(inherited)",
409 | "@executable_path/Frameworks",
410 | );
411 | MARKETING_VERSION = 1.0;
412 | OTHER_LDFLAGS = (
413 | "$(inherited)",
414 | "-ObjC",
415 | "-lc++",
416 | );
417 | PRODUCT_BUNDLE_IDENTIFIER = pouchdbadapterreactnativesqlite.example;
418 | PRODUCT_NAME = PouchdbAdapterReactNativeSqliteExample;
419 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
420 | SWIFT_VERSION = 5.0;
421 | VERSIONING_SYSTEM = "apple-generic";
422 | };
423 | name = Debug;
424 | };
425 | 13B07F951A680F5B00A75B9A /* Release */ = {
426 | isa = XCBuildConfiguration;
427 | baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-PouchdbAdapterReactNativeSqliteExample.release.xcconfig */;
428 | buildSettings = {
429 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
430 | CLANG_ENABLE_MODULES = YES;
431 | CURRENT_PROJECT_VERSION = 1;
432 | INFOPLIST_FILE = PouchdbAdapterReactNativeSqliteExample/Info.plist;
433 | IPHONEOS_DEPLOYMENT_TARGET = 15.1;
434 | LD_RUNPATH_SEARCH_PATHS = (
435 | "$(inherited)",
436 | "@executable_path/Frameworks",
437 | );
438 | MARKETING_VERSION = 1.0;
439 | OTHER_LDFLAGS = (
440 | "$(inherited)",
441 | "-ObjC",
442 | "-lc++",
443 | );
444 | PRODUCT_BUNDLE_IDENTIFIER = pouchdbadapterreactnativesqlite.example;
445 | PRODUCT_NAME = PouchdbAdapterReactNativeSqliteExample;
446 | SWIFT_VERSION = 5.0;
447 | VERSIONING_SYSTEM = "apple-generic";
448 | };
449 | name = Release;
450 | };
451 | 83CBBA201A601CBA00E9B192 /* Debug */ = {
452 | isa = XCBuildConfiguration;
453 | buildSettings = {
454 | ALWAYS_SEARCH_USER_PATHS = NO;
455 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
456 | CLANG_CXX_LANGUAGE_STANDARD = "c++20";
457 | CLANG_CXX_LIBRARY = "libc++";
458 | CLANG_ENABLE_MODULES = YES;
459 | CLANG_ENABLE_OBJC_ARC = YES;
460 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
461 | CLANG_WARN_BOOL_CONVERSION = YES;
462 | CLANG_WARN_COMMA = YES;
463 | CLANG_WARN_CONSTANT_CONVERSION = YES;
464 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
465 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
466 | CLANG_WARN_EMPTY_BODY = YES;
467 | CLANG_WARN_ENUM_CONVERSION = YES;
468 | CLANG_WARN_INFINITE_RECURSION = YES;
469 | CLANG_WARN_INT_CONVERSION = YES;
470 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
471 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
472 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
473 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
474 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
475 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
476 | CLANG_WARN_STRICT_PROTOTYPES = YES;
477 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
478 | CLANG_WARN_UNREACHABLE_CODE = YES;
479 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
480 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
481 | COPY_PHASE_STRIP = NO;
482 | ENABLE_STRICT_OBJC_MSGSEND = YES;
483 | ENABLE_TESTABILITY = YES;
484 | "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
485 | GCC_C_LANGUAGE_STANDARD = gnu99;
486 | GCC_DYNAMIC_NO_PIC = NO;
487 | GCC_NO_COMMON_BLOCKS = YES;
488 | GCC_OPTIMIZATION_LEVEL = 0;
489 | GCC_PREPROCESSOR_DEFINITIONS = (
490 | "DEBUG=1",
491 | "$(inherited)",
492 | );
493 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
494 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
495 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
496 | GCC_WARN_UNDECLARED_SELECTOR = YES;
497 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
498 | GCC_WARN_UNUSED_FUNCTION = YES;
499 | GCC_WARN_UNUSED_VARIABLE = YES;
500 | IPHONEOS_DEPLOYMENT_TARGET = 15.1;
501 | LD_RUNPATH_SEARCH_PATHS = (
502 | /usr/lib/swift,
503 | "$(inherited)",
504 | );
505 | LIBRARY_SEARCH_PATHS = (
506 | "\"$(SDKROOT)/usr/lib/swift\"",
507 | "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
508 | "\"$(inherited)\"",
509 | );
510 | MTL_ENABLE_DEBUG_INFO = YES;
511 | ONLY_ACTIVE_ARCH = YES;
512 | OTHER_CPLUSPLUSFLAGS = (
513 | "$(OTHER_CFLAGS)",
514 | "-DFOLLY_NO_CONFIG",
515 | "-DFOLLY_MOBILE=1",
516 | "-DFOLLY_USE_LIBCPP=1",
517 | "-DFOLLY_CFG_NO_COROUTINES=1",
518 | "-DFOLLY_HAVE_CLOCK_GETTIME=1",
519 | );
520 | OTHER_LDFLAGS = (
521 | "$(inherited)",
522 | " ",
523 | );
524 | REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
525 | SDKROOT = iphoneos;
526 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
527 | USE_HERMES = true;
528 | };
529 | name = Debug;
530 | };
531 | 83CBBA211A601CBA00E9B192 /* Release */ = {
532 | isa = XCBuildConfiguration;
533 | buildSettings = {
534 | ALWAYS_SEARCH_USER_PATHS = NO;
535 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
536 | CLANG_CXX_LANGUAGE_STANDARD = "c++20";
537 | CLANG_CXX_LIBRARY = "libc++";
538 | CLANG_ENABLE_MODULES = YES;
539 | CLANG_ENABLE_OBJC_ARC = YES;
540 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
541 | CLANG_WARN_BOOL_CONVERSION = YES;
542 | CLANG_WARN_COMMA = YES;
543 | CLANG_WARN_CONSTANT_CONVERSION = YES;
544 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
545 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
546 | CLANG_WARN_EMPTY_BODY = YES;
547 | CLANG_WARN_ENUM_CONVERSION = YES;
548 | CLANG_WARN_INFINITE_RECURSION = YES;
549 | CLANG_WARN_INT_CONVERSION = YES;
550 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
551 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
552 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
553 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
554 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
555 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
556 | CLANG_WARN_STRICT_PROTOTYPES = YES;
557 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
558 | CLANG_WARN_UNREACHABLE_CODE = YES;
559 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
560 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
561 | COPY_PHASE_STRIP = YES;
562 | ENABLE_NS_ASSERTIONS = NO;
563 | ENABLE_STRICT_OBJC_MSGSEND = YES;
564 | "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
565 | GCC_C_LANGUAGE_STANDARD = gnu99;
566 | GCC_NO_COMMON_BLOCKS = YES;
567 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
568 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
569 | GCC_WARN_UNDECLARED_SELECTOR = YES;
570 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
571 | GCC_WARN_UNUSED_FUNCTION = YES;
572 | GCC_WARN_UNUSED_VARIABLE = YES;
573 | IPHONEOS_DEPLOYMENT_TARGET = 15.1;
574 | LD_RUNPATH_SEARCH_PATHS = (
575 | /usr/lib/swift,
576 | "$(inherited)",
577 | );
578 | LIBRARY_SEARCH_PATHS = (
579 | "\"$(SDKROOT)/usr/lib/swift\"",
580 | "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
581 | "\"$(inherited)\"",
582 | );
583 | MTL_ENABLE_DEBUG_INFO = NO;
584 | OTHER_CPLUSPLUSFLAGS = (
585 | "$(OTHER_CFLAGS)",
586 | "-DFOLLY_NO_CONFIG",
587 | "-DFOLLY_MOBILE=1",
588 | "-DFOLLY_USE_LIBCPP=1",
589 | "-DFOLLY_CFG_NO_COROUTINES=1",
590 | "-DFOLLY_HAVE_CLOCK_GETTIME=1",
591 | );
592 | OTHER_LDFLAGS = (
593 | "$(inherited)",
594 | " ",
595 | );
596 | REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
597 | SDKROOT = iphoneos;
598 | USE_HERMES = true;
599 | VALIDATE_PRODUCT = YES;
600 | };
601 | name = Release;
602 | };
603 | /* End XCBuildConfiguration section */
604 |
605 | /* Begin XCConfigurationList section */
606 | 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "PouchdbAdapterReactNativeSqliteExampleTests" */ = {
607 | isa = XCConfigurationList;
608 | buildConfigurations = (
609 | 00E356F61AD99517003FC87E /* Debug */,
610 | 00E356F71AD99517003FC87E /* Release */,
611 | );
612 | defaultConfigurationIsVisible = 0;
613 | defaultConfigurationName = Release;
614 | };
615 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "PouchdbAdapterReactNativeSqliteExample" */ = {
616 | isa = XCConfigurationList;
617 | buildConfigurations = (
618 | 13B07F941A680F5B00A75B9A /* Debug */,
619 | 13B07F951A680F5B00A75B9A /* Release */,
620 | );
621 | defaultConfigurationIsVisible = 0;
622 | defaultConfigurationName = Release;
623 | };
624 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "PouchdbAdapterReactNativeSqliteExample" */ = {
625 | isa = XCConfigurationList;
626 | buildConfigurations = (
627 | 83CBBA201A601CBA00E9B192 /* Debug */,
628 | 83CBBA211A601CBA00E9B192 /* Release */,
629 | );
630 | defaultConfigurationIsVisible = 0;
631 | defaultConfigurationName = Release;
632 | };
633 | /* End XCConfigurationList section */
634 | };
635 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
636 | }
637 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExample.xcodeproj/xcshareddata/xcschemes/PouchdbAdapterReactNativeSqliteExample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExample.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExample/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : RCTAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExample/AppDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 |
5 | @implementation AppDelegate
6 |
7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
8 | {
9 | self.moduleName = @"PouchdbAdapterReactNativeSqliteExample";
10 | // You can add your custom initial props in the dictionary below.
11 | // They will be passed down to the ViewController used by React Native.
12 | self.initialProps = @{};
13 |
14 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
15 | }
16 |
17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
18 | {
19 | return [self bundleURL];
20 | }
21 |
22 | - (NSURL *)bundleURL
23 | {
24 | #if DEBUG
25 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
26 | #else
27 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
28 | #endif
29 | }
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExample/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "iphone",
5 | "scale": "2x",
6 | "size": "20x20"
7 | },
8 | {
9 | "idiom": "iphone",
10 | "scale": "3x",
11 | "size": "20x20"
12 | },
13 | {
14 | "idiom": "iphone",
15 | "scale": "2x",
16 | "size": "29x29"
17 | },
18 | {
19 | "idiom": "iphone",
20 | "scale": "3x",
21 | "size": "29x29"
22 | },
23 | {
24 | "idiom": "iphone",
25 | "scale": "2x",
26 | "size": "40x40"
27 | },
28 | {
29 | "idiom": "iphone",
30 | "scale": "3x",
31 | "size": "40x40"
32 | },
33 | {
34 | "idiom": "iphone",
35 | "scale": "2x",
36 | "size": "60x60"
37 | },
38 | {
39 | "idiom": "iphone",
40 | "scale": "3x",
41 | "size": "60x60"
42 | },
43 | {
44 | "idiom": "ios-marketing",
45 | "scale": "1x",
46 | "size": "1024x1024"
47 | }
48 | ],
49 | "info": {
50 | "author": "xcode",
51 | "version": 1
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExample/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "version": 1,
4 | "author": "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | PouchdbAdapterReactNativeSqliteExample
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 | NSAllowsLocalNetworking
32 |
33 |
34 | NSLocationWhenInUseUsageDescription
35 |
36 | UILaunchStoryboardName
37 | LaunchScreen
38 | UIRequiredDeviceCapabilities
39 |
40 | arm64
41 |
42 | UISupportedInterfaceOrientations
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | UIViewControllerBasedStatusBarAppearance
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExample/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExample/PrivacyInfo.xcprivacy:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSPrivacyAccessedAPITypes
6 |
7 |
8 | NSPrivacyAccessedAPIType
9 | NSPrivacyAccessedAPICategoryFileTimestamp
10 | NSPrivacyAccessedAPITypeReasons
11 |
12 | C617.1
13 |
14 |
15 |
16 | NSPrivacyAccessedAPIType
17 | NSPrivacyAccessedAPICategoryUserDefaults
18 | NSPrivacyAccessedAPITypeReasons
19 |
20 | CA92.1
21 |
22 |
23 |
24 | NSPrivacyAccessedAPIType
25 | NSPrivacyAccessedAPICategorySystemBootTime
26 | NSPrivacyAccessedAPITypeReasons
27 |
28 | 35F9.1
29 |
30 |
31 |
32 | NSPrivacyCollectedDataTypes
33 |
34 | NSPrivacyTracking
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExample/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char *argv[])
6 | {
7 | @autoreleasepool {
8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/example/ios/PouchdbAdapterReactNativeSqliteExampleTests/PouchdbAdapterReactNativeSqliteExampleTests.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import
5 | #import
6 |
7 | #define TIMEOUT_SECONDS 600
8 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
9 |
10 | @interface PouchdbAdapterReactNativeSqliteExampleTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation PouchdbAdapterReactNativeSqliteExampleTests
15 |
16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
17 | {
18 | if (test(view)) {
19 | return YES;
20 | }
21 | for (UIView *subview in [view subviews]) {
22 | if ([self findSubviewInView:subview matching:test]) {
23 | return YES;
24 | }
25 | }
26 | return NO;
27 | }
28 |
29 | - (void)testRendersWelcomeScreen
30 | {
31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
33 | BOOL foundElement = NO;
34 |
35 | __block NSString *redboxError = nil;
36 | #ifdef DEBUG
37 | RCTSetLogFunction(
38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
39 | if (level >= RCTLogLevelError) {
40 | redboxError = message;
41 | }
42 | });
43 | #endif
44 |
45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
48 |
49 | foundElement = [self findSubviewInView:vc.view
50 | matching:^BOOL(UIView *view) {
51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
52 | return YES;
53 | }
54 | return NO;
55 | }];
56 | }
57 |
58 | #ifdef DEBUG
59 | RCTSetLogFunction(RCTDefaultLogFunction);
60 | #endif
61 |
62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/example/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'react-native',
3 | }
4 |
--------------------------------------------------------------------------------
/example/metro.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const { getDefaultConfig } = require('@react-native/metro-config')
3 | const { getConfig } = require('react-native-builder-bob/metro-config')
4 | const pkg = require('../package.json')
5 |
6 | const root = path.resolve(__dirname, '..')
7 |
8 | /**
9 | * Metro configuration
10 | * https://facebook.github.io/metro/docs/configuration
11 | *
12 | * @type {import('@react-native/metro-config').MetroConfig}
13 | */
14 | module.exports = getConfig(getDefaultConfig(__dirname), {
15 | root,
16 | pkg,
17 | project: __dirname,
18 | })
19 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pouchdb-adapter-react-native-sqlite-example",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android",
7 | "ios": "react-native run-ios",
8 | "start": "react-native start",
9 | "build:android": "react-native build-android --extra-params \"--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a\"",
10 | "build:ios": "react-native build-ios --scheme PouchdbAdapterReactNativeSqliteExample --mode Debug --extra-params \"-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO\""
11 | },
12 | "dependencies": {
13 | "@craftzdog/pouchdb-errors": "^9.0.1",
14 | "@op-engineering/op-sqlite": "^12.0.2",
15 | "debug": "^4.4.0",
16 | "pouchdb-adapter-http": "^9.0.0",
17 | "pouchdb-core": "^9.0.0",
18 | "pouchdb-mapreduce": "^9.0.0",
19 | "pouchdb-replication": "^9.0.0",
20 | "react": "19.0.0",
21 | "react-native": "0.79.2",
22 | "react-native-quick-crypto": "^0.7.8"
23 | },
24 | "devDependencies": {
25 | "@babel/core": "^7.25.2",
26 | "@babel/preset-env": "^7.25.3",
27 | "@babel/runtime": "^7.25.0",
28 | "@react-native-community/cli": "18.0.0",
29 | "@react-native-community/cli-platform-android": "18.0.0",
30 | "@react-native-community/cli-platform-ios": "18.0.0",
31 | "@react-native/babel-preset": "0.79.2",
32 | "@react-native/eslint-config": "0.79.2",
33 | "@react-native/metro-config": "0.79.2",
34 | "@react-native/typescript-config": "0.79.2",
35 | "react-native-builder-bob": "^0.35.2",
36 | "react-native-dotenv": "^3.4.11"
37 | },
38 | "op-sqlite": {
39 | "fts5": true
40 | },
41 | "engines": {
42 | "node": ">=18"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/example/react-native.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const pkg = require('../package.json')
3 |
4 | module.exports = {
5 | project: {
6 | ios: {
7 | automaticPodsInstallation: true,
8 | },
9 | },
10 | dependencies: {
11 | [pkg.name]: {
12 | root: path.join(__dirname, '..'),
13 | platforms: {
14 | // Codegen script incorrectly fails without this
15 | // So we explicitly specify the platforms with empty object
16 | ios: null,
17 | android: null,
18 | },
19 | },
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/example/src/App.tsx:
--------------------------------------------------------------------------------
1 | import './shim'
2 | import React from 'react'
3 |
4 | import {
5 | StyleSheet,
6 | View,
7 | Text,
8 | TouchableOpacity,
9 | Image,
10 | ScrollView,
11 | } from 'react-native'
12 | import PouchDB from './pouchdb'
13 | // import { open } from '@op-engineering/op-sqlite'
14 |
15 | // @ts-ignore eslint-ignore-next-line
16 | const uiManager = global?.nativeFabricUIManager ? 'Fabric' : 'Paper'
17 |
18 | console.log(`Using ${uiManager}`)
19 |
20 | const pouch = new PouchDB('mydb', {
21 | adapter: 'react-native-sqlite',
22 | revs_limit: 100,
23 | })
24 | // @ts-ignore
25 | globalThis.pouch = pouch
26 |
27 | // async function run() {
28 | // const db = open({ name: 'test' })
29 | // db.execute(
30 | // 'CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, name TEXT)'
31 | // )
32 | //
33 | // await db.transaction(async (tx) => {
34 | // await Promise.all(
35 | // Array.from({ length: 100 }, (_, i) =>
36 | // tx
37 | // .executeAsync('INSERT INTO test (name) VALUES (?)', ['foo'])
38 | // .then((result) => console.log('insertId:', result.insertId))
39 | // )
40 | // )
41 | // })
42 | //
43 | // db.execute('DROP TABLE IF EXISTS test')
44 | // }
45 | //
46 | // run()
47 |
48 | export default function App() {
49 | const [result, setResult] = React.useState('')
50 | const [imageData, setImageData] = React.useState(null)
51 | const docId = 'mydoc\u0000hoge'
52 |
53 | const handleAllDocs = async () => {
54 | setResult('')
55 | const docs = await pouch.allDocs()
56 | setResult(JSON.stringify(docs, null, 2))
57 | }
58 | const handleGetDoc = async () => {
59 | setResult('')
60 | try {
61 | const mydoc = await pouch.get(docId)
62 | setResult(JSON.stringify(mydoc, null, 2))
63 | } catch (e: any) {
64 | setResult(e.name + ': ' + e.message)
65 | }
66 | }
67 | const handlePutDoc = async () => {
68 | setResult('')
69 | let rev
70 | let count = 0
71 | try {
72 | const mydoc = await pouch.get(docId)
73 | console.log('mydoc:', mydoc)
74 | rev = mydoc._rev
75 | count = mydoc.count || 0
76 | } catch (e) {}
77 |
78 | const r = await pouch.put({
79 | _rev: rev,
80 | _id: docId,
81 | title: 'Heroes',
82 | count: count + 1,
83 | })
84 | console.log('put:', r)
85 | setResult(JSON.stringify(r, null, 2))
86 | }
87 | const handlePutMultiDocs = async () => {
88 | setResult('')
89 | let count = 0
90 |
91 | try {
92 | const res = await pouch.bulkDocs(
93 | Array.from({ length: 10 }, (_, i) => ({
94 | _id: `test:${i}`,
95 | title: 'Heroes',
96 | count: count + 1,
97 | }))
98 | )
99 | setResult(JSON.stringify(res, null, 2))
100 | } catch (e: any) {
101 | setResult(e.name + ': ' + e.message + '\n' + e.stack)
102 | console.error(e)
103 | }
104 | }
105 | // const handlePutMultiDocs = async () => {
106 | // setResult('')
107 | // let count = 0
108 | //
109 | // const res = await Promise.all(
110 | // Array.from({ length: 100 }, (_, i) =>
111 | // pouch.put({
112 | // _id: `test:${i}`,
113 | // title: 'Heroes',
114 | // count: count + 1,
115 | // })
116 | // )
117 | // )
118 | // setResult(JSON.stringify(res, null, 2))
119 | // }
120 | const handleRemoveDoc = async () => {
121 | setResult('')
122 | try {
123 | const mydoc = await pouch.get(docId)
124 | const result = await pouch.remove(mydoc)
125 | console.log('ret:', result)
126 | setResult(JSON.stringify(result, null, 2))
127 | } catch (e: any) {
128 | setResult(e.name + ': ' + e.message)
129 | }
130 | }
131 | const handleReplicate = async () => {
132 | const remoteUrl = process.env.COUCHDB_URL
133 | setResult(`Replicating from remote...${remoteUrl}`)
134 | if (!remoteUrl) {
135 | setResult(
136 | 'CouchDB URL is not set. Please set COUCHDB_URL environment variable in `.env.local`.'
137 | )
138 | return
139 | }
140 | try {
141 | const result = await pouch.replicate
142 | .from(remoteUrl, { live: false })
143 | .on('error', (err: any) => console.log('error:', err))
144 | console.log('ret:', result)
145 | setResult(JSON.stringify(result, null, 2))
146 | } catch (e: any) {
147 | console.error(e)
148 | setResult(e.name + ': ' + e.message)
149 | }
150 | }
151 | const handleDestroyDB = async () => {
152 | setResult('')
153 | try {
154 | const result = await pouch.destroy()
155 | console.log('ret:', result)
156 | setResult(JSON.stringify(result, null, 2))
157 | } catch (e: any) {
158 | setResult(e.name + ': ' + e.message)
159 | }
160 | }
161 | const handleQuery = async () => {
162 | setResult('')
163 | try {
164 | const result = await pouch.query('index_notes', {
165 | startkey: ['b', 'u', 'book:tjnPbJakw', 2, {}, {}],
166 | endkey: ['b', 'u', 'book:tjnPbJakw', 1, 0, 0],
167 | include_docs: true,
168 | descending: true,
169 | limit: 50,
170 | skip: 0,
171 | conflicts: true,
172 | })
173 | console.log('ret:', result)
174 | setResult(JSON.stringify(result, null, 2))
175 | } catch (e: any) {
176 | setResult(e.name + ': ' + e.message)
177 | }
178 | }
179 | const handleGetAttachment = async () => {
180 | setResult('')
181 | setImageData(null)
182 | try {
183 | const result = await pouch.get('file:9yqbnLGSq', {
184 | attachments: true,
185 | })
186 | setResult(JSON.stringify(result, null, 2))
187 | setImageData('data:image/png;base64,' + result._attachments.index.data)
188 | console.log('ret:', result)
189 | } catch (e: any) {
190 | setResult(e.name + ': ' + e.message)
191 | }
192 | }
193 |
194 | const handleWriteLocalDoc = async () => {
195 | setResult('')
196 | try {
197 | const result = await pouch.put({
198 | _id: '_local/mydoc',
199 | title: 'Heroes',
200 | })
201 | console.log('ret:', result)
202 | setResult(JSON.stringify(result, null, 2))
203 | } catch (e: any) {
204 | setResult(e.name + ': ' + e.message)
205 | }
206 | }
207 |
208 | const handleRemoveLocalDoc = async () => {
209 | setResult('')
210 | try {
211 | const mydoc = await pouch.get('_local/mydoc')
212 | const result = await pouch.remove(mydoc)
213 | console.log('ret:', result)
214 | setResult(JSON.stringify(result, null, 2))
215 | } catch (e: any) {
216 | setResult(e.name + ': ' + e.message)
217 | }
218 | }
219 |
220 | return (
221 |
225 |
226 | Fetch all docs
227 |
228 |
229 | Get a doc
230 |
231 |
232 | Put a doc
233 |
234 |
235 | Put docs
236 |
237 |
238 | Delete a doc
239 |
240 |
241 | Replicate from a server
242 |
243 |
244 | Destroy db
245 |
246 |
247 | Run a query
248 |
249 |
250 | Get an attachment
251 |
252 |
253 | Write a local doc
254 |
255 |
256 | Remove a local doc
257 |
258 | {imageData && (
259 |
263 | )}
264 |
265 | {result}
266 |
267 |
268 | )
269 | }
270 |
271 | const styles = StyleSheet.create({
272 | container: {
273 | flex: 1,
274 | gap: 12,
275 | padding: 12,
276 | paddingTop: 80,
277 | backgroundColor: 'white',
278 | },
279 | contentContainerStyle: {
280 | gap: 12,
281 | },
282 | box: {
283 | width: 60,
284 | height: 60,
285 | marginVertical: 20,
286 | },
287 | button: {
288 | padding: 8,
289 | paddingHorizontal: 16,
290 | backgroundColor: 'navy',
291 | borderRadius: 4,
292 | },
293 | buttonText: {
294 | color: 'white',
295 | fontSize: 16,
296 | },
297 | })
298 |
--------------------------------------------------------------------------------
/example/src/logger.ts:
--------------------------------------------------------------------------------
1 | import debug from 'debug'
2 |
3 | console.log('enabling all logs')
4 | debug.enable('*')
5 |
--------------------------------------------------------------------------------
/example/src/pouchdb.ts:
--------------------------------------------------------------------------------
1 | import PouchDB from 'pouchdb-core'
2 | import HttpPouch from 'pouchdb-adapter-http'
3 | import replication from 'pouchdb-replication'
4 | import mapreduce from 'pouchdb-mapreduce'
5 | import sqliteAdapter from 'pouchdb-adapter-react-native-sqlite'
6 |
7 | export default PouchDB.plugin(HttpPouch)
8 | .plugin(replication)
9 | .plugin(mapreduce)
10 | .plugin(sqliteAdapter)
11 |
--------------------------------------------------------------------------------
/example/src/shim.ts:
--------------------------------------------------------------------------------
1 | import { install } from 'react-native-quick-crypto'
2 |
3 | install()
4 |
--------------------------------------------------------------------------------
/lefthook.yml:
--------------------------------------------------------------------------------
1 | pre-commit:
2 | parallel: true
3 | commands:
4 | lint:
5 | glob: '*.{js,ts,jsx,tsx}'
6 | run: npx eslint {staged_files}
7 | types:
8 | glob: '*.{js,ts, jsx, tsx}'
9 | run: npx tsc
10 | commit-msg:
11 | parallel: true
12 | commands:
13 | commitlint:
14 | run: npx commitlint --edit
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pouchdb-adapter-react-native-sqlite",
3 | "version": "4.1.2",
4 | "description": "PouchDB adapter for React Native SQLite",
5 | "source": "./src/index.ts",
6 | "main": "./lib/commonjs/index.js",
7 | "module": "./lib/module/index.js",
8 | "exports": {
9 | ".": {
10 | "import": {
11 | "types": "./lib/typescript/module/src/index.d.ts",
12 | "default": "./lib/module/index.js"
13 | },
14 | "require": {
15 | "types": "./lib/typescript/commonjs/src/index.d.ts",
16 | "default": "./lib/commonjs/index.js"
17 | }
18 | }
19 | },
20 | "files": [
21 | "src",
22 | "lib",
23 | "android",
24 | "ios",
25 | "cpp",
26 | "*.podspec",
27 | "react-native.config.js",
28 | "!ios/build",
29 | "!android/build",
30 | "!android/gradle",
31 | "!android/gradlew",
32 | "!android/gradlew.bat",
33 | "!android/local.properties",
34 | "!**/__tests__",
35 | "!**/__fixtures__",
36 | "!**/__mocks__",
37 | "!**/.*"
38 | ],
39 | "scripts": {
40 | "example": "yarn workspace pouchdb-adapter-react-native-sqlite-example",
41 | "test": "jest",
42 | "typecheck": "tsc",
43 | "lint": "eslint \"**/*.{js,ts,tsx}\"",
44 | "clean": "del-cli lib",
45 | "prepare": "bob build",
46 | "release": "release-it"
47 | },
48 | "keywords": [
49 | "react-native",
50 | "ios",
51 | "android"
52 | ],
53 | "repository": {
54 | "type": "git",
55 | "url": "git+https://github.com/craftzdog/pouchdb-adapter-react-native-sqlite.git"
56 | },
57 | "author": "Takuya Matsuyama (https://github.com/craftzdog)",
58 | "license": "MIT",
59 | "bugs": {
60 | "url": "https://github.com/craftzdog/pouchdb-adapter-react-native-sqlite/issues"
61 | },
62 | "homepage": "https://github.com/craftzdog/pouchdb-adapter-react-native-sqlite#readme",
63 | "publishConfig": {
64 | "registry": "https://registry.npmjs.org/"
65 | },
66 | "devDependencies": {
67 | "@commitlint/config-conventional": "^17.0.2",
68 | "@craftzdog/pouchdb-errors": "^9.0.1",
69 | "@evilmartians/lefthook": "^1.5.0",
70 | "@react-native-community/cli": "15.0.1",
71 | "@react-native/eslint-config": "^0.73.1",
72 | "@release-it/conventional-changelog": "^9.0.2",
73 | "@types/jest": "^29.5.5",
74 | "@types/pouchdb-core": "^7.0.15",
75 | "@types/react": "^18.2.44",
76 | "commitlint": "^17.0.2",
77 | "del-cli": "^5.1.0",
78 | "eslint": "^8.57.0",
79 | "eslint-config-prettier": "^9.0.0",
80 | "eslint-plugin-prettier": "^5.0.1",
81 | "jest": "^29.7.0",
82 | "prettier": "^3.0.3",
83 | "react": "18.3.1",
84 | "react-native": "0.76.4",
85 | "react-native-builder-bob": "^0.35.2",
86 | "release-it": "^17.10.0",
87 | "turbo": "^1.10.7",
88 | "typescript": "^5.2.2"
89 | },
90 | "dependencies": {
91 | "@op-engineering/op-sqlite": "^12.0.2",
92 | "debug": "^4.4.0",
93 | "events": "^3.3.0",
94 | "pouchdb-adapter-utils": "^9.0.0",
95 | "pouchdb-binary-utils": "^9.0.0",
96 | "pouchdb-errors": "^9.0.0",
97 | "pouchdb-json": "^9.0.0",
98 | "pouchdb-merge": "^9.0.0",
99 | "pouchdb-utils": "^9.0.0"
100 | },
101 | "resolutions": {
102 | "@types/react": "^18.2.44"
103 | },
104 | "peerDependencies": {
105 | "react": "*",
106 | "react-native": "*"
107 | },
108 | "workspaces": [
109 | "example"
110 | ],
111 | "packageManager": "yarn@3.6.1",
112 | "jest": {
113 | "preset": "react-native",
114 | "modulePathIgnorePatterns": [
115 | "/example/node_modules",
116 | "/lib/"
117 | ]
118 | },
119 | "commitlint": {
120 | "extends": [
121 | "@commitlint/config-conventional"
122 | ]
123 | },
124 | "release-it": {
125 | "git": {
126 | "commitMessage": "chore: release ${version}",
127 | "tagName": "v${version}"
128 | },
129 | "npm": {
130 | "publish": true
131 | },
132 | "github": {
133 | "release": true
134 | },
135 | "plugins": {
136 | "@release-it/conventional-changelog": {
137 | "preset": "angular"
138 | }
139 | }
140 | },
141 | "eslintConfig": {
142 | "root": true,
143 | "extends": [
144 | "@react-native",
145 | "prettier"
146 | ],
147 | "rules": {
148 | "@typescript-eslint/no-shadow": "off",
149 | "react-native/no-inline-styles": "off",
150 | "react/react-in-jsx-scope": "off",
151 | "prettier/prettier": [
152 | "error",
153 | {
154 | "quoteProps": "consistent",
155 | "singleQuote": true,
156 | "tabWidth": 2,
157 | "trailingComma": "es5",
158 | "useTabs": false,
159 | "semi": false
160 | }
161 | ]
162 | }
163 | },
164 | "eslintIgnore": [
165 | "node_modules/",
166 | "lib/"
167 | ],
168 | "prettier": {
169 | "quoteProps": "consistent",
170 | "singleQuote": true,
171 | "tabWidth": 2,
172 | "trailingComma": "es5",
173 | "useTabs": false,
174 | "semi": false
175 | },
176 | "react-native-builder-bob": {
177 | "source": "src",
178 | "output": "lib",
179 | "targets": [
180 | [
181 | "commonjs",
182 | {
183 | "esm": true
184 | }
185 | ],
186 | [
187 | "module",
188 | {
189 | "esm": true
190 | }
191 | ],
192 | [
193 | "typescript",
194 | {
195 | "project": "tsconfig.build.json",
196 | "esm": true
197 | }
198 | ]
199 | ]
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/src/__tests__/index.test.tsx:
--------------------------------------------------------------------------------
1 | it.todo('write a test')
2 |
--------------------------------------------------------------------------------
/src/bulkDocs.ts:
--------------------------------------------------------------------------------
1 | import {
2 | preprocessAttachments,
3 | isLocalId,
4 | processDocs,
5 | parseDoc,
6 | } from 'pouchdb-adapter-utils'
7 | import { compactTree } from 'pouchdb-merge'
8 | import { safeJsonParse, safeJsonStringify } from 'pouchdb-json'
9 | import { MISSING_STUB, createError } from 'pouchdb-errors'
10 |
11 | import {
12 | DOC_STORE,
13 | BY_SEQ_STORE,
14 | ATTACH_STORE,
15 | ATTACH_AND_SEQ_STORE,
16 | } from './constants'
17 |
18 | import { select, stringifyDoc, compactRevs, handleSQLiteError } from './utils'
19 | import type { Transaction } from '@op-engineering/op-sqlite'
20 | import { logger } from './debug'
21 |
22 | interface DocInfo {
23 | _id: string
24 | metadata: any
25 | data: any
26 | stemmedRevs?: string[]
27 | error?: any
28 | }
29 |
30 | interface DBOptions {
31 | revs_limit?: number
32 | }
33 |
34 | interface Request {
35 | docs: any[]
36 | }
37 |
38 | interface Options {
39 | new_edits: boolean
40 | }
41 |
42 | async function sqliteBulkDocs(
43 | dbOpts: DBOptions,
44 | req: Request,
45 | opts: Options,
46 | api: any,
47 | transaction: (fn: (tx: Transaction) => Promise) => Promise,
48 | sqliteChanges: any
49 | ): Promise {
50 | const newEdits = opts.new_edits
51 | const userDocs = req.docs
52 |
53 | const docInfos: DocInfo[] = userDocs.map((doc) => {
54 | if (doc._id && isLocalId(doc._id)) {
55 | return doc
56 | }
57 | return parseDoc(doc, newEdits, dbOpts)
58 | })
59 |
60 | const docInfoErrors = docInfos.filter((docInfo) => docInfo.error)
61 | if (docInfoErrors.length) {
62 | throw docInfoErrors[0]
63 | }
64 |
65 | let tx: Transaction
66 | const results = new Array(docInfos.length)
67 | const fetchedDocs = new Map()
68 |
69 | async function verifyAttachment(digest: string) {
70 | logger.debug('verify attachment:', digest)
71 | const sql =
72 | 'SELECT count(*) as cnt FROM ' + ATTACH_STORE + ' WHERE digest=?'
73 | const result = await tx.execute(sql, [digest])
74 | if (result.rows[0]?.cnt === 0) {
75 | const err = createError(
76 | MISSING_STUB,
77 | 'unknown stub attachment with digest ' + digest
78 | )
79 | logger.error('unknown:', err)
80 | throw err
81 | } else {
82 | logger.debug('ok')
83 | return true
84 | }
85 | }
86 |
87 | async function verifyAttachments(): Promise {
88 | const digests: string[] = []
89 | docInfos.forEach((docInfo) => {
90 | if (docInfo.data && docInfo.data._attachments) {
91 | Object.keys(docInfo.data._attachments).forEach((filename) => {
92 | const att = docInfo.data._attachments[filename]
93 | if (att.stub) {
94 | logger.debug('attachment digest', att.digest)
95 | digests.push(att.digest)
96 | }
97 | })
98 | }
99 | })
100 |
101 | if (!digests.length) return
102 |
103 | for (const digest of digests) {
104 | await verifyAttachment(digest)
105 | }
106 | }
107 |
108 | async function writeDoc(
109 | docInfo: DocInfo,
110 | winningRev: string,
111 | _winningRevIsDeleted: boolean,
112 | newRevIsDeleted: boolean,
113 | isUpdate: boolean,
114 | _delta: number,
115 | resultsIdx: number
116 | ) {
117 | logger.debug('writeDoc:', { ...docInfo, data: null })
118 |
119 | async function dataWritten(tx: Transaction, seq: number) {
120 | const id = docInfo.metadata.id
121 |
122 | let revsToCompact = docInfo.stemmedRevs || []
123 | if (isUpdate && api.auto_compaction) {
124 | revsToCompact = compactTree(docInfo.metadata).concat(revsToCompact)
125 | }
126 | if (revsToCompact.length) {
127 | compactRevs(revsToCompact, id, tx)
128 | }
129 |
130 | docInfo.metadata.seq = seq
131 | const rev = docInfo.metadata.rev
132 | delete docInfo.metadata.rev
133 |
134 | const sql = isUpdate
135 | ? 'UPDATE ' +
136 | DOC_STORE +
137 | ' SET json=?, max_seq=?, winningseq=' +
138 | '(SELECT seq FROM ' +
139 | BY_SEQ_STORE +
140 | ' WHERE doc_id=' +
141 | DOC_STORE +
142 | '.id AND rev=?) WHERE id=?'
143 | : 'INSERT INTO ' +
144 | DOC_STORE +
145 | ' (id, winningseq, max_seq, json) VALUES (?,?,?,?);'
146 | const metadataStr = safeJsonStringify(docInfo.metadata)
147 | const params = isUpdate
148 | ? [metadataStr, seq, winningRev, id]
149 | : [id, seq, seq, metadataStr]
150 | await tx.execute(sql, params)
151 | results[resultsIdx] = {
152 | ok: true,
153 | id: docInfo.metadata.id,
154 | rev: rev,
155 | }
156 | fetchedDocs.set(id, docInfo.metadata)
157 | }
158 |
159 | async function insertAttachmentMappings(seq: number) {
160 | const attsToAdd = Object.keys(data._attachments || {})
161 |
162 | if (!attsToAdd.length) {
163 | return
164 | }
165 |
166 | function add(att: string) {
167 | const sql =
168 | 'INSERT INTO ' + ATTACH_AND_SEQ_STORE + ' (digest, seq) VALUES (?,?)'
169 | const sqlArgs = [data._attachments[att].digest, seq]
170 | return tx.execute(sql, sqlArgs)
171 | }
172 |
173 | await Promise.all(attsToAdd.map((att) => add(att)))
174 | }
175 |
176 | docInfo.data._id = docInfo.metadata.id
177 | docInfo.data._rev = docInfo.metadata.rev
178 | const attachments = Object.keys(docInfo.data._attachments || {})
179 |
180 | if (newRevIsDeleted) {
181 | docInfo.data._deleted = true
182 | }
183 |
184 | for (const key of attachments) {
185 | const att = docInfo.data._attachments[key]
186 | if (!att.stub) {
187 | const data = att.data
188 | delete att.data
189 | att.revpos = parseInt(winningRev, 10)
190 | const digest = att.digest
191 | await saveAttachment(digest, data)
192 | }
193 | }
194 |
195 | const data = docInfo.data
196 | const deletedInt = newRevIsDeleted ? 1 : 0
197 |
198 | const id = data._id
199 | const rev = data._rev
200 | const json = stringifyDoc(data)
201 | const sql =
202 | 'INSERT INTO ' +
203 | BY_SEQ_STORE +
204 | ' (doc_id, rev, json, deleted) VALUES (?, ?, ?, ?);'
205 | const sqlArgs = [id, rev, json, deletedInt]
206 |
207 | try {
208 | const result = await tx.execute(sql, sqlArgs)
209 | const seq = result.insertId
210 | if (typeof seq === 'number') {
211 | await insertAttachmentMappings(seq)
212 | await dataWritten(tx, seq)
213 | }
214 | } catch (e) {
215 | // constraint error, recover by updating instead (see #1638)
216 | // https://github.com/pouchdb/pouchdb/issues/1638
217 | const fetchSql = select('seq', BY_SEQ_STORE, null, 'doc_id=? AND rev=?')
218 | const res = await tx.execute(fetchSql, [id, rev])
219 | const seq = res.rows[0]!.seq as number
220 | logger.debug(
221 | `Got a constraint error, updating instead: seq=${seq}, id=${id}, rev=${rev}`
222 | )
223 | const sql =
224 | 'UPDATE ' +
225 | BY_SEQ_STORE +
226 | ' SET json=?, deleted=? WHERE doc_id=? AND rev=?;'
227 | const sqlArgs = [json, deletedInt, id, rev]
228 | await tx.execute(sql, sqlArgs)
229 | await insertAttachmentMappings(seq)
230 | await dataWritten(tx, seq)
231 | }
232 | }
233 |
234 | function websqlProcessDocs(): Promise {
235 | return new Promise((resolve, reject) => {
236 | let chain = Promise.resolve()
237 | processDocs(
238 | dbOpts.revs_limit,
239 | docInfos,
240 | api,
241 | fetchedDocs,
242 | tx,
243 | results,
244 | (
245 | docInfo: DocInfo,
246 | winningRev: string,
247 | winningRevIsDeleted: boolean,
248 | newRevIsDeleted: boolean,
249 | isUpdate: boolean,
250 | delta: number,
251 | resultsIdx: number,
252 | callback: (err?: any) => void
253 | ) => {
254 | chain = chain.then(() => {
255 | return writeDoc(
256 | docInfo,
257 | winningRev,
258 | winningRevIsDeleted,
259 | newRevIsDeleted,
260 | isUpdate,
261 | delta,
262 | resultsIdx
263 | ).then(callback, callback)
264 | })
265 | },
266 | opts,
267 | (err?: any) => {
268 | if (!err) resolve()
269 | else reject(err)
270 | }
271 | )
272 | })
273 | }
274 |
275 | async function fetchExistingDocs(): Promise {
276 | if (!docInfos.length) return
277 |
278 | for (const docInfo of docInfos) {
279 | if (docInfo._id && isLocalId(docInfo._id)) {
280 | continue
281 | }
282 | const id = docInfo.metadata.id
283 | const result = await tx.execute(
284 | 'SELECT json FROM ' + DOC_STORE + ' WHERE id = ?',
285 | [id]
286 | )
287 | if (result.rows?.length) {
288 | const metadata = safeJsonParse(result.rows[0]!.json)
289 | fetchedDocs.set(id, metadata)
290 | }
291 | }
292 | }
293 |
294 | async function saveAttachment(digest: string, data: any) {
295 | logger.debug('saveAttachment:', digest)
296 | let sql = 'SELECT digest FROM ' + ATTACH_STORE + ' WHERE digest=?'
297 | const result = await tx.execute(sql, [digest])
298 | if (result.rows?.length) return
299 | sql =
300 | 'INSERT INTO ' + ATTACH_STORE + ' (digest, body, escaped) VALUES (?,?,0)'
301 | await tx.execute(sql, [digest, data])
302 | }
303 |
304 | await new Promise((resolve, reject) => {
305 | preprocessAttachments(docInfos, 'binary', (err: any) => {
306 | if (err) reject(err)
307 | else resolve()
308 | })
309 | })
310 |
311 | await transaction(async (txn: Transaction) => {
312 | await verifyAttachments()
313 |
314 | try {
315 | tx = txn
316 | await fetchExistingDocs()
317 | await websqlProcessDocs()
318 | sqliteChanges.notify(api._name)
319 | } catch (err: any) {
320 | throw handleSQLiteError(err)
321 | }
322 | })
323 |
324 | return results
325 | }
326 |
327 | export default sqliteBulkDocs
328 |
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
1 | function quote(str: string) {
2 | return "'" + str + "'"
3 | }
4 |
5 | const ADAPTER_VERSION = 7 // used to manage migrations
6 |
7 | // The object stores created for each database
8 | // DOC_STORE stores the document meta data, its revision history and state
9 | const DOC_STORE = quote('document-store')
10 | // BY_SEQ_STORE stores a particular version of a document, keyed by its
11 | // sequence id
12 | const BY_SEQ_STORE = quote('by-sequence')
13 | // Where we store attachments
14 | const ATTACH_STORE = quote('attach-store')
15 | const LOCAL_STORE = quote('local-store')
16 | const META_STORE = quote('metadata-store')
17 | // where we store many-to-many relations between attachment
18 | // digests and seqs
19 | const ATTACH_AND_SEQ_STORE = quote('attach-seq-store')
20 |
21 | export {
22 | ADAPTER_VERSION as ADAPTER_VERSION,
23 | DOC_STORE as DOC_STORE,
24 | BY_SEQ_STORE as BY_SEQ_STORE,
25 | ATTACH_STORE as ATTACH_STORE,
26 | LOCAL_STORE as LOCAL_STORE,
27 | META_STORE as META_STORE,
28 | ATTACH_AND_SEQ_STORE as ATTACH_AND_SEQ_STORE,
29 | }
30 |
--------------------------------------------------------------------------------
/src/core.ts:
--------------------------------------------------------------------------------
1 | import {
2 | clone,
3 | pick,
4 | filterChange,
5 | changesHandler as Changes,
6 | uuid,
7 | } from 'pouchdb-utils'
8 | import {
9 | collectConflicts,
10 | traverseRevTree,
11 | latest as getLatest,
12 | } from 'pouchdb-merge'
13 | import { safeJsonParse, safeJsonStringify } from 'pouchdb-json'
14 | import {
15 | binaryStringToBlobOrBuffer as binStringToBlob,
16 | btoa,
17 | } from 'pouchdb-binary-utils'
18 |
19 | import sqliteBulkDocs from './bulkDocs'
20 |
21 | import { MISSING_DOC, REV_CONFLICT, createError } from 'pouchdb-errors'
22 |
23 | import {
24 | ADAPTER_VERSION,
25 | DOC_STORE,
26 | BY_SEQ_STORE,
27 | ATTACH_STORE,
28 | LOCAL_STORE,
29 | META_STORE,
30 | ATTACH_AND_SEQ_STORE,
31 | } from './constants'
32 |
33 | import {
34 | qMarks,
35 | stringifyDoc,
36 | unstringifyDoc,
37 | select,
38 | compactRevs,
39 | handleSQLiteError,
40 | } from './utils'
41 |
42 | import openDB, { closeDB, type OpenDatabaseOptions } from './openDatabase'
43 | import type { DB, Transaction } from '@op-engineering/op-sqlite'
44 | import type { TransactionQueue } from './transactionQueue'
45 | import { logger } from './debug'
46 |
47 | // these indexes cover the ground for most allDocs queries
48 | const BY_SEQ_STORE_DELETED_INDEX_SQL =
49 | "CREATE INDEX IF NOT EXISTS 'by-seq-deleted-idx' ON " +
50 | BY_SEQ_STORE +
51 | ' (seq, deleted)'
52 | const BY_SEQ_STORE_DOC_ID_REV_INDEX_SQL =
53 | "CREATE UNIQUE INDEX IF NOT EXISTS 'by-seq-doc-id-rev' ON " +
54 | BY_SEQ_STORE +
55 | ' (doc_id, rev)'
56 | const DOC_STORE_WINNINGSEQ_INDEX_SQL =
57 | "CREATE INDEX IF NOT EXISTS 'doc-winningseq-idx' ON " +
58 | DOC_STORE +
59 | ' (winningseq)'
60 | const ATTACH_AND_SEQ_STORE_SEQ_INDEX_SQL =
61 | "CREATE INDEX IF NOT EXISTS 'attach-seq-seq-idx' ON " +
62 | ATTACH_AND_SEQ_STORE +
63 | ' (seq)'
64 | const ATTACH_AND_SEQ_STORE_ATTACH_INDEX_SQL =
65 | "CREATE UNIQUE INDEX IF NOT EXISTS 'attach-seq-digest-idx' ON " +
66 | ATTACH_AND_SEQ_STORE +
67 | ' (digest, seq)'
68 |
69 | const DOC_STORE_AND_BY_SEQ_JOINER =
70 | BY_SEQ_STORE + '.seq = ' + DOC_STORE + '.winningseq'
71 |
72 | const SELECT_DOCS =
73 | BY_SEQ_STORE +
74 | '.seq AS seq, ' +
75 | BY_SEQ_STORE +
76 | '.deleted AS deleted, ' +
77 | BY_SEQ_STORE +
78 | '.json AS data, ' +
79 | BY_SEQ_STORE +
80 | '.rev AS rev, ' +
81 | DOC_STORE +
82 | '.json AS metadata'
83 |
84 | const sqliteChanges = new Changes()
85 |
86 | function SqlPouch(opts: OpenDatabaseOptions, cb: (err: any) => void) {
87 | // @ts-ignore
88 | let api = this as any
89 | let db: DB
90 | // @ts-ignore
91 | let txnQueue: TransactionQueue
92 | let instanceId: string
93 | let encoding: string = 'UTF-8'
94 | api.auto_compaction = false
95 |
96 | api._name = opts.name
97 | logger.debug('Creating SqlPouch instance: %s', api._name, opts)
98 |
99 | const sqlOpts = Object.assign({}, opts, { name: opts.name + '.sqlite' })
100 | const openDBResult = openDB(sqlOpts)
101 | if ('db' in openDBResult) {
102 | db = openDBResult.db
103 | txnQueue = openDBResult.transactionQueue
104 | setup(cb)
105 | logger.debug('Database was opened successfully.', db.getDbPath())
106 | } else {
107 | handleSQLiteError(openDBResult.error, cb)
108 | }
109 |
110 | async function transaction(fn: (tx: Transaction) => Promise) {
111 | return txnQueue.push(fn)
112 | }
113 |
114 | async function readTransaction(fn: (tx: Transaction) => Promise) {
115 | return txnQueue.pushReadOnly(fn)
116 | }
117 |
118 | async function setup(callback: (err: any) => void) {
119 | await db.transaction(async (tx) => {
120 | await Promise.all([checkEncoding(tx), fetchVersion(tx)])
121 | })
122 | callback(null)
123 | }
124 |
125 | async function checkEncoding(tx: Transaction) {
126 | const res = await tx.execute("SELECT HEX('a') AS hex")
127 | const hex = res.rows[0]!.hex as string
128 | encoding = hex.length === 2 ? 'UTF-8' : 'UTF-16'
129 | }
130 |
131 | async function fetchVersion(tx: Transaction) {
132 | const sql = 'SELECT sql FROM sqlite_master WHERE tbl_name = ' + META_STORE
133 | const result = await tx.execute(sql, [])
134 | if (!result.rows?.length) {
135 | await onGetVersion(tx, 0)
136 | } else if (!/db_version/.test(result.rows[0]!.sql as string)) {
137 | await tx.execute(
138 | 'ALTER TABLE ' + META_STORE + ' ADD COLUMN db_version INTEGER'
139 | )
140 | await onGetVersion(tx, 1)
141 | } else {
142 | const resDBVer = await tx.execute('SELECT db_version FROM ' + META_STORE)
143 | const dbVersion = resDBVer.rows[0]!.db_version as number
144 | await onGetVersion(tx, dbVersion)
145 | }
146 | }
147 |
148 | async function onGetVersion(tx: Transaction, dbVersion: number) {
149 | if (dbVersion === 0) {
150 | await createInitialSchema(tx)
151 | } else {
152 | await runMigrations(tx, dbVersion)
153 | }
154 | }
155 |
156 | async function createInitialSchema(tx: Transaction) {
157 | const meta =
158 | 'CREATE TABLE IF NOT EXISTS ' + META_STORE + ' (dbid, db_version INTEGER)'
159 | const attach =
160 | 'CREATE TABLE IF NOT EXISTS ' +
161 | ATTACH_STORE +
162 | ' (digest UNIQUE, escaped TINYINT(1), body BLOB)'
163 | const attachAndRev =
164 | 'CREATE TABLE IF NOT EXISTS ' +
165 | ATTACH_AND_SEQ_STORE +
166 | ' (digest, seq INTEGER)'
167 | const doc =
168 | 'CREATE TABLE IF NOT EXISTS ' +
169 | DOC_STORE +
170 | ' (id unique, json, winningseq, max_seq INTEGER UNIQUE)'
171 | const seq =
172 | 'CREATE TABLE IF NOT EXISTS ' +
173 | BY_SEQ_STORE +
174 | ' (seq INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, json, deleted TINYINT(1), doc_id, rev)'
175 | const local =
176 | 'CREATE TABLE IF NOT EXISTS ' + LOCAL_STORE + ' (id UNIQUE, rev, json)'
177 |
178 | await tx.execute(attach)
179 | await tx.execute(local)
180 | await tx.execute(attachAndRev)
181 | await tx.execute(ATTACH_AND_SEQ_STORE_SEQ_INDEX_SQL)
182 | await tx.execute(ATTACH_AND_SEQ_STORE_ATTACH_INDEX_SQL)
183 | await tx.execute(doc)
184 | await tx.execute(DOC_STORE_WINNINGSEQ_INDEX_SQL)
185 | await tx.execute(seq)
186 | await tx.execute(BY_SEQ_STORE_DELETED_INDEX_SQL)
187 | await tx.execute(BY_SEQ_STORE_DOC_ID_REV_INDEX_SQL)
188 | await tx.execute(meta)
189 | const initSeq =
190 | 'INSERT INTO ' + META_STORE + ' (db_version, dbid) VALUES (?,?)'
191 | instanceId = uuid()
192 | const initSeqArgs = [ADAPTER_VERSION, instanceId]
193 | await tx.execute(initSeq, initSeqArgs)
194 | onGetInstanceId()
195 | }
196 |
197 | async function runMigrations(_tx: Transaction, dbVersion: number) {
198 | // const tasks = [setupDone]
199 | //
200 | // let i = dbVersion
201 | // const nextMigration = (tx: Transaction) => {
202 | // tasks[i - 1](tx, nextMigration)
203 | // i++
204 | // }
205 | // nextMigration(tx)
206 |
207 | const migrated = dbVersion < ADAPTER_VERSION
208 | if (migrated) {
209 | await db.execute(
210 | 'UPDATE ' + META_STORE + ' SET db_version = ' + ADAPTER_VERSION
211 | )
212 | }
213 | const result = await db.execute('SELECT dbid FROM ' + META_STORE)
214 | instanceId = result.rows[0]!.dbid as string
215 | onGetInstanceId()
216 | }
217 |
218 | function onGetInstanceId() {
219 | // Do nothing
220 | }
221 |
222 | api._remote = false
223 |
224 | api._id = (callback: (err: any, id?: string) => void) => {
225 | callback(null, instanceId)
226 | }
227 |
228 | api._info = (callback: (err: any, info?: any) => void) => {
229 | readTransaction(async (tx: Transaction) => {
230 | try {
231 | const seq = await getMaxSeq(tx)
232 | const docCount = await countDocs(tx)
233 | callback(null, {
234 | doc_count: docCount,
235 | update_seq: seq,
236 | sqlite_encoding: encoding,
237 | })
238 | } catch (e: any) {
239 | handleSQLiteError(e, callback)
240 | }
241 | })
242 | }
243 |
244 | api._bulkDocs = async (
245 | req: any,
246 | reqOpts: any,
247 | callback: (err: any, response?: any) => void
248 | ) => {
249 | logger.debug('**********bulkDocs!!!!!!!!!!!!!!!!!!!')
250 | try {
251 | const response = await sqliteBulkDocs(
252 | { revs_limit: opts.revs_limit },
253 | req,
254 | reqOpts,
255 | api,
256 | transaction,
257 | sqliteChanges
258 | )
259 | callback(null, response)
260 | } catch (err: any) {
261 | handleSQLiteError(err, callback)
262 | }
263 | }
264 |
265 | api._get = (
266 | id: string,
267 | opts: any,
268 | callback: (err: any, response?: any) => void
269 | ) => {
270 | logger.debug('get:', id)
271 | let doc: any
272 | let metadata: any
273 | const tx: Transaction = opts.ctx
274 | if (!tx) {
275 | readTransaction(async (txn) => {
276 | return new Promise((resolve) => {
277 | api._get(
278 | id,
279 | Object.assign({ ctx: txn }, opts),
280 | (err: any, response: any) => {
281 | callback(err, response)
282 | resolve()
283 | }
284 | )
285 | })
286 | })
287 | return
288 | }
289 |
290 | const finish = (err: any) => {
291 | callback(err, { doc, metadata, ctx: tx })
292 | }
293 |
294 | let sql: string
295 | let sqlArgs: any[]
296 |
297 | if (!opts.rev) {
298 | sql = select(
299 | SELECT_DOCS,
300 | [DOC_STORE, BY_SEQ_STORE],
301 | DOC_STORE_AND_BY_SEQ_JOINER,
302 | DOC_STORE + '.id=?'
303 | )
304 | sqlArgs = [id]
305 | } else if (opts.latest) {
306 | latest(
307 | tx,
308 | id,
309 | opts.rev,
310 | (latestRev: string) => {
311 | opts.latest = false
312 | opts.rev = latestRev
313 | api._get(id, opts, callback)
314 | },
315 | finish
316 | )
317 | return
318 | } else {
319 | sql = select(
320 | SELECT_DOCS,
321 | [DOC_STORE, BY_SEQ_STORE],
322 | DOC_STORE + '.id=' + BY_SEQ_STORE + '.doc_id',
323 | [BY_SEQ_STORE + '.doc_id=?', BY_SEQ_STORE + '.rev=?']
324 | )
325 | sqlArgs = [id, opts.rev]
326 | }
327 |
328 | tx.execute(sql, sqlArgs)
329 | .then((results) => {
330 | if (!results.rows?.length) {
331 | const missingErr = createError(MISSING_DOC, 'missing')
332 | return finish(missingErr)
333 | }
334 | const item = results.rows[0]!
335 | metadata = safeJsonParse(item.metadata)
336 | if (item.deleted && !opts.rev) {
337 | const deletedErr = createError(MISSING_DOC, 'deleted')
338 | return finish(deletedErr)
339 | }
340 | doc = unstringifyDoc(
341 | item.data as string,
342 | metadata.id,
343 | item.rev as string
344 | )
345 | finish(null)
346 | })
347 | .catch((e) => {
348 | // createError will throw in RN 0.76.3
349 | // https://github.com/facebook/hermes/issues/1496
350 | return finish(e)
351 | })
352 | }
353 |
354 | api._allDocs = (opts: any, callback: (err: any, response?: any) => void) => {
355 | const results: any[] = []
356 |
357 | const start = 'startkey' in opts ? opts.startkey : false
358 | const end = 'endkey' in opts ? opts.endkey : false
359 | const key = 'key' in opts ? opts.key : false
360 | const keys = 'keys' in opts ? opts.keys : false
361 | const descending = 'descending' in opts ? opts.descending : false
362 | let limit = 'limit' in opts ? opts.limit : -1
363 | const offset = 'skip' in opts ? opts.skip : 0
364 | const inclusiveEnd = opts.inclusive_end !== false
365 |
366 | let sqlArgs: any[] = []
367 | const criteria: string[] = []
368 | const keyChunks: any[] = []
369 |
370 | if (keys) {
371 | const destinctKeys: string[] = []
372 | keys.forEach((key: string) => {
373 | if (destinctKeys.indexOf(key) === -1) {
374 | destinctKeys.push(key)
375 | }
376 | })
377 |
378 | for (let index = 0; index < destinctKeys.length; index += 999) {
379 | const chunk = destinctKeys.slice(index, index + 999)
380 | if (chunk.length > 0) {
381 | keyChunks.push(chunk)
382 | }
383 | }
384 | } else if (key !== false) {
385 | criteria.push(DOC_STORE + '.id = ?')
386 | sqlArgs.push(key)
387 | } else if (start !== false || end !== false) {
388 | if (start !== false) {
389 | criteria.push(DOC_STORE + '.id ' + (descending ? '<=' : '>=') + ' ?')
390 | sqlArgs.push(start)
391 | }
392 | if (end !== false) {
393 | let comparator = descending ? '>' : '<'
394 | if (inclusiveEnd) {
395 | comparator += '='
396 | }
397 | criteria.push(DOC_STORE + '.id ' + comparator + ' ?')
398 | sqlArgs.push(end)
399 | }
400 | if (key !== false) {
401 | criteria.push(DOC_STORE + '.id = ?')
402 | sqlArgs.push(key)
403 | }
404 | }
405 |
406 | if (!keys) {
407 | criteria.push(BY_SEQ_STORE + '.deleted = 0')
408 | }
409 |
410 | readTransaction(async (tx: Transaction) => {
411 | const processResult = (rows: any[], results: any[], keys: any) => {
412 | for (let i = 0, l = rows.length; i < l; i++) {
413 | const item = rows[i]
414 | const metadata = safeJsonParse(item.metadata)
415 | const id = metadata.id
416 | const data = unstringifyDoc(item.data, id, item.rev)
417 | const winningRev = data._rev
418 | const doc: any = {
419 | id: id,
420 | key: id,
421 | value: { rev: winningRev },
422 | }
423 | if (opts.include_docs) {
424 | doc.doc = data
425 | doc.doc._rev = winningRev
426 | if (opts.conflicts) {
427 | const conflicts = collectConflicts(metadata)
428 | if (conflicts.length) {
429 | doc.doc._conflicts = conflicts
430 | }
431 | }
432 | fetchAttachmentsIfNecessary(doc.doc, opts, api, tx)
433 | }
434 | if (item.deleted) {
435 | if (keys) {
436 | doc.value.deleted = true
437 | doc.doc = null
438 | } else {
439 | continue
440 | }
441 | }
442 | if (!keys) {
443 | results.push(doc)
444 | } else {
445 | let index = keys.indexOf(id)
446 | do {
447 | results[index] = doc
448 | index = keys.indexOf(id, index + 1)
449 | } while (index > -1 && index < keys.length)
450 | }
451 | }
452 | if (keys) {
453 | keys.forEach((key: string, index: number) => {
454 | if (!results[index]) {
455 | results[index] = { key: key, error: 'not_found' }
456 | }
457 | })
458 | }
459 | }
460 |
461 | try {
462 | const totalRows = await countDocs(tx)
463 | const updateSeq = opts.update_seq ? await getMaxSeq(tx) : undefined
464 |
465 | if (limit === 0) {
466 | limit = 1
467 | }
468 |
469 | if (keys) {
470 | let finishedCount = 0
471 | const allRows: any[] = []
472 | for (const keyChunk of keyChunks) {
473 | sqlArgs = []
474 | criteria.length = 0
475 | let bindingStr = ''
476 | keyChunk.forEach(() => {
477 | bindingStr += '?,'
478 | })
479 | bindingStr = bindingStr.substring(0, bindingStr.length - 1)
480 | criteria.push(DOC_STORE + '.id IN (' + bindingStr + ')')
481 | sqlArgs = sqlArgs.concat(keyChunk)
482 |
483 | const sql =
484 | select(
485 | SELECT_DOCS,
486 | [DOC_STORE, BY_SEQ_STORE],
487 | DOC_STORE_AND_BY_SEQ_JOINER,
488 | criteria,
489 | DOC_STORE + '.id ' + (descending ? 'DESC' : 'ASC')
490 | ) +
491 | ' LIMIT ' +
492 | limit +
493 | ' OFFSET ' +
494 | offset
495 | const result = await tx.execute(sql, sqlArgs)
496 | finishedCount++
497 | if (result.rows) {
498 | for (let index = 0; index < result.rows.length; index++) {
499 | allRows.push(result.rows[index])
500 | }
501 | }
502 | if (finishedCount === keyChunks.length) {
503 | processResult(allRows, results, keys)
504 | }
505 | }
506 | } else {
507 | const sql =
508 | select(
509 | SELECT_DOCS,
510 | [DOC_STORE, BY_SEQ_STORE],
511 | DOC_STORE_AND_BY_SEQ_JOINER,
512 | criteria,
513 | DOC_STORE + '.id ' + (descending ? 'DESC' : 'ASC')
514 | ) +
515 | ' LIMIT ' +
516 | limit +
517 | ' OFFSET ' +
518 | offset
519 | const result = await tx.execute(sql, sqlArgs)
520 | const rows: any[] = []
521 | if (result.rows) {
522 | for (let index = 0; index < result.rows.length; index++) {
523 | rows.push(result.rows[index])
524 | }
525 | }
526 | processResult(rows, results, keys)
527 | }
528 |
529 | const returnVal: any = {
530 | total_rows: totalRows,
531 | offset: opts.skip,
532 | rows: results,
533 | }
534 |
535 | if (opts.update_seq) {
536 | returnVal.update_seq = updateSeq
537 | }
538 | callback(null, returnVal)
539 | } catch (e: any) {
540 | handleSQLiteError(e, callback)
541 | }
542 | })
543 | }
544 |
545 | api._changes = (opts: any): any => {
546 | opts = clone(opts)
547 |
548 | if (opts.continuous) {
549 | const id = api._name + ':' + uuid()
550 | sqliteChanges.addListener(api._name, id, api, opts)
551 | sqliteChanges.notify(api._name)
552 | return {
553 | cancel: () => {
554 | sqliteChanges.removeListener(api._name, id)
555 | },
556 | }
557 | }
558 |
559 | const descending = opts.descending
560 | opts.since = opts.since && !descending ? opts.since : 0
561 | let limit = 'limit' in opts ? opts.limit : -1
562 | if (limit === 0) {
563 | limit = 1
564 | }
565 |
566 | const results: any[] = []
567 | let numResults = 0
568 |
569 | const fetchChanges = () => {
570 | const selectStmt =
571 | DOC_STORE +
572 | '.json AS metadata, ' +
573 | DOC_STORE +
574 | '.max_seq AS maxSeq, ' +
575 | BY_SEQ_STORE +
576 | '.json AS winningDoc, ' +
577 | BY_SEQ_STORE +
578 | '.rev AS winningRev '
579 | const from = DOC_STORE + ' JOIN ' + BY_SEQ_STORE
580 | const joiner =
581 | DOC_STORE +
582 | '.id=' +
583 | BY_SEQ_STORE +
584 | '.doc_id' +
585 | ' AND ' +
586 | DOC_STORE +
587 | '.winningseq=' +
588 | BY_SEQ_STORE +
589 | '.seq'
590 | const criteria = ['maxSeq > ?']
591 | const sqlArgs = [opts.since]
592 |
593 | if (opts.doc_ids) {
594 | criteria.push(DOC_STORE + '.id IN ' + qMarks(opts.doc_ids.length))
595 | sqlArgs.push(...opts.doc_ids)
596 | }
597 |
598 | const orderBy = 'maxSeq ' + (descending ? 'DESC' : 'ASC')
599 | let sql = select(selectStmt, from, joiner, criteria, orderBy)
600 | const filter = filterChange(opts)
601 |
602 | if (!opts.view && !opts.filter) {
603 | sql += ' LIMIT ' + limit
604 | }
605 |
606 | let lastSeq = opts.since || 0
607 | readTransaction(async (tx: Transaction) => {
608 | try {
609 | const result = await tx.execute(sql, sqlArgs)
610 |
611 | if (result.rows) {
612 | for (let i = 0, l = result.rows.length; i < l; i++) {
613 | const item = result.rows[i]!
614 | const metadata = safeJsonParse(item.metadata)
615 | lastSeq = item.maxSeq
616 |
617 | const doc = unstringifyDoc(
618 | item.winningDoc as string,
619 | metadata.id,
620 | item.winningRev as string
621 | )
622 | const change = opts.processChange(doc, metadata, opts)
623 | change.seq = item.maxSeq
624 |
625 | const filtered = filter(change)
626 | if (typeof filtered === 'object') {
627 | return opts.complete(filtered)
628 | }
629 |
630 | if (filtered) {
631 | numResults++
632 | if (opts.return_docs) {
633 | results.push(change)
634 | }
635 | if (opts.attachments && opts.include_docs) {
636 | fetchAttachmentsIfNecessary(doc, opts, api, tx, () =>
637 | opts.onChange(change)
638 | )
639 | } else {
640 | opts.onChange(change)
641 | }
642 | }
643 | if (numResults === limit) {
644 | break
645 | }
646 | }
647 | }
648 |
649 | if (!opts.continuous) {
650 | opts.complete(null, {
651 | results,
652 | last_seq: lastSeq,
653 | })
654 | }
655 | } catch (e: any) {
656 | handleSQLiteError(e, opts.complete)
657 | }
658 | })
659 | }
660 |
661 | fetchChanges()
662 | }
663 |
664 | api._close = (callback: (err?: any) => void) => {
665 | closeDB(api._name)
666 | callback()
667 | }
668 |
669 | api._getAttachment = (
670 | _docId: string,
671 | _attachId: string,
672 | attachment: any,
673 | opts: any,
674 | callback: (err: any, response?: any) => void
675 | ) => {
676 | let res: any
677 | const tx: Transaction = opts.ctx
678 | const digest = attachment.digest
679 | const type = attachment.content_type
680 | const sql =
681 | 'SELECT escaped, body AS body FROM ' + ATTACH_STORE + ' WHERE digest=?'
682 | tx.execute(sql, [digest]).then((result) => {
683 | const item = result.rows[0]!
684 | const data = item.body
685 | if (opts.binary) {
686 | res = binStringToBlob(data, type)
687 | } else {
688 | res = btoa(data)
689 | }
690 | callback(null, res)
691 | })
692 | }
693 |
694 | api._getRevisionTree = (
695 | docId: string,
696 | callback: (err: any, rev_tree?: any) => void
697 | ) => {
698 | readTransaction(async (tx: Transaction) => {
699 | const sql = 'SELECT json AS metadata FROM ' + DOC_STORE + ' WHERE id = ?'
700 | const result = await tx.execute(sql, [docId])
701 | if (!result.rows?.length) {
702 | callback(createError(MISSING_DOC))
703 | } else {
704 | const data = safeJsonParse(result.rows[0]!.metadata)
705 | callback(null, data.rev_tree)
706 | }
707 | })
708 | }
709 |
710 | api._doCompaction = (
711 | docId: string,
712 | revs: string[],
713 | callback: (err?: any) => void
714 | ) => {
715 | if (!revs.length) {
716 | return callback()
717 | }
718 | transaction(async (tx: Transaction) => {
719 | try {
720 | let sql = 'SELECT json AS metadata FROM ' + DOC_STORE + ' WHERE id = ?'
721 | const result = await tx.execute(sql, [docId])
722 | const metadata = safeJsonParse(result.rows[0]!.metadata)
723 | traverseRevTree(
724 | metadata.rev_tree,
725 | (
726 | _isLeaf: boolean,
727 | pos: number,
728 | revHash: string,
729 | _ctx: Transaction,
730 | opts: any
731 | ) => {
732 | const rev = pos + '-' + revHash
733 | if (revs.indexOf(rev) !== -1) {
734 | opts.status = 'missing'
735 | }
736 | }
737 | )
738 | sql = 'UPDATE ' + DOC_STORE + ' SET json = ? WHERE id = ?'
739 | await tx.execute(sql, [safeJsonStringify(metadata), docId])
740 |
741 | compactRevs(revs, docId, tx)
742 | } catch (e: any) {
743 | handleSQLiteError(e, callback)
744 | }
745 | callback()
746 | })
747 | }
748 |
749 | api._getLocal = (id: string, callback: (err: any, doc?: any) => void) => {
750 | readTransaction(async (tx: Transaction) => {
751 | try {
752 | const sql = 'SELECT json, rev FROM ' + LOCAL_STORE + ' WHERE id=?'
753 | const res = await tx.execute(sql, [id])
754 | if (res.rows?.length) {
755 | const item = res.rows[0]!
756 | const doc = unstringifyDoc(
757 | item.json as string,
758 | id,
759 | item.rev as string
760 | )
761 | callback(null, doc)
762 | } else {
763 | callback(createError(MISSING_DOC))
764 | }
765 | } catch (e: any) {
766 | handleSQLiteError(e, callback)
767 | }
768 | })
769 | }
770 |
771 | api._putLocal = (
772 | doc: any,
773 | opts: any,
774 | callback: (err: any, response?: any) => void
775 | ) => {
776 | if (typeof opts === 'function') {
777 | callback = opts
778 | opts = {}
779 | }
780 | delete doc._revisions
781 | const oldRev = doc._rev
782 | const id = doc._id
783 | let newRev: string
784 | if (!oldRev) {
785 | newRev = doc._rev = '0-1'
786 | } else {
787 | newRev = doc._rev = '0-' + (parseInt(oldRev.split('-')[1], 10) + 1)
788 | }
789 | const json = stringifyDoc(doc)
790 |
791 | let ret: any
792 | const putLocal = async (tx: Transaction) => {
793 | try {
794 | let sql: string
795 | let values: any[]
796 | if (oldRev) {
797 | sql =
798 | 'UPDATE ' + LOCAL_STORE + ' SET rev=?, json=? WHERE id=? AND rev=?'
799 | values = [newRev, json, id, oldRev]
800 | } else {
801 | sql = 'INSERT INTO ' + LOCAL_STORE + ' (id, rev, json) VALUES (?,?,?)'
802 | values = [id, newRev, json]
803 | }
804 | const res = await tx.execute(sql, values)
805 | if (res.rowsAffected) {
806 | ret = { ok: true, id: id, rev: newRev }
807 | callback(null, ret)
808 | } else {
809 | callback(createError(REV_CONFLICT))
810 | }
811 | } catch (e: any) {
812 | handleSQLiteError(e, callback)
813 | }
814 | }
815 |
816 | if (opts.ctx) {
817 | putLocal(opts.ctx)
818 | } else {
819 | transaction(putLocal)
820 | }
821 | }
822 |
823 | api._removeLocal = (
824 | doc: any,
825 | opts: any,
826 | callback: (err: any, response?: any) => void
827 | ) => {
828 | if (typeof opts === 'function') {
829 | callback = opts
830 | opts = {}
831 | }
832 | let ret: any
833 |
834 | const removeLocal = async (tx: Transaction) => {
835 | try {
836 | const sql = 'DELETE FROM ' + LOCAL_STORE + ' WHERE id=? AND rev=?'
837 | const params = [doc._id, doc._rev]
838 | const res = await tx.execute(sql, params)
839 | if (!res.rowsAffected) {
840 | return callback(createError(MISSING_DOC))
841 | }
842 | ret = { ok: true, id: doc._id, rev: '0-0' }
843 | callback(null, ret)
844 | } catch (e: any) {
845 | handleSQLiteError(e, callback)
846 | }
847 | }
848 |
849 | if (opts.ctx) {
850 | removeLocal(opts.ctx)
851 | } else {
852 | transaction(removeLocal)
853 | }
854 | }
855 |
856 | api._destroy = (_opts: any, callback: (err: any, response?: any) => void) => {
857 | sqliteChanges.removeAllListeners(api._name)
858 | transaction(async (tx: Transaction) => {
859 | try {
860 | const stores = [
861 | DOC_STORE,
862 | BY_SEQ_STORE,
863 | ATTACH_STORE,
864 | META_STORE,
865 | LOCAL_STORE,
866 | ATTACH_AND_SEQ_STORE,
867 | ]
868 | stores.forEach((store) => {
869 | tx.execute('DROP TABLE IF EXISTS ' + store, [])
870 | })
871 | callback(null, { ok: true })
872 | } catch (e: any) {
873 | handleSQLiteError(e, callback)
874 | }
875 | })
876 | }
877 |
878 | function fetchAttachmentsIfNecessary(
879 | doc: any,
880 | opts: any,
881 | api: any,
882 | txn: any,
883 | cb?: () => void
884 | ) {
885 | const attachments = Object.keys(doc._attachments || {})
886 | if (!attachments.length) {
887 | return cb && cb()
888 | }
889 | let numDone = 0
890 |
891 | const checkDone = () => {
892 | if (++numDone === attachments.length && cb) {
893 | cb()
894 | }
895 | }
896 |
897 | const fetchAttachment = (doc: any, att: string) => {
898 | const attObj = doc._attachments[att]
899 | const attOpts = { binary: opts.binary, ctx: txn }
900 | api._getAttachment(doc._id, att, attObj, attOpts, (_: any, data: any) => {
901 | doc._attachments[att] = Object.assign(
902 | pick(attObj, ['digest', 'content_type']),
903 | { data }
904 | )
905 | checkDone()
906 | })
907 | }
908 |
909 | attachments.forEach((att) => {
910 | if (opts.attachments && opts.include_docs) {
911 | fetchAttachment(doc, att)
912 | } else {
913 | doc._attachments[att].stub = true
914 | checkDone()
915 | }
916 | })
917 | }
918 |
919 | async function getMaxSeq(tx: Transaction): Promise {
920 | const sql = 'SELECT MAX(seq) AS seq FROM ' + BY_SEQ_STORE
921 | const res = await tx.execute(sql, [])
922 | const updateSeq = (res.rows[0]!.seq as number) || 0
923 | return updateSeq
924 | }
925 |
926 | async function countDocs(tx: Transaction): Promise {
927 | const sql = select(
928 | 'COUNT(' + DOC_STORE + ".id) AS 'num'",
929 | [DOC_STORE, BY_SEQ_STORE],
930 | DOC_STORE_AND_BY_SEQ_JOINER,
931 | BY_SEQ_STORE + '.deleted=0'
932 | )
933 | const result = await tx.execute(sql, [])
934 | return (result.rows[0]!.num as number) || 0
935 | }
936 |
937 | async function latest(
938 | tx: Transaction,
939 | id: string,
940 | rev: string,
941 | callback: (latestRev: string) => void,
942 | finish: (err: any) => void
943 | ) {
944 | const sql = select(
945 | SELECT_DOCS,
946 | [DOC_STORE, BY_SEQ_STORE],
947 | DOC_STORE_AND_BY_SEQ_JOINER,
948 | DOC_STORE + '.id=?'
949 | )
950 | const sqlArgs = [id]
951 |
952 | const results = await tx.execute(sql, sqlArgs)
953 | if (!results.rows?.length) {
954 | const err = createError(MISSING_DOC, 'missing')
955 | return finish(err)
956 | }
957 | const item = results.rows[0]!
958 | const metadata = safeJsonParse(item.metadata)
959 | callback(getLatest(rev, metadata))
960 | }
961 | }
962 |
963 | export default SqlPouch
964 |
--------------------------------------------------------------------------------
/src/debug.ts:
--------------------------------------------------------------------------------
1 | import debug from 'debug'
2 |
3 | export const logger = {
4 | debug: debug('pouch-sqlite:debug'),
5 | error: debug('pouch-sqlite:error'),
6 | }
7 |
8 | logger.debug.log = console.log.bind(console)
9 | logger.error.log = console.error.bind(console)
10 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import SqlPouchCore from './core'
2 | import type { OpenDatabaseOptions } from './openDatabase'
3 |
4 | function ReactNativeSQLitePouch(
5 | opts: OpenDatabaseOptions,
6 | callback: (err: any) => void
7 | ) {
8 | try {
9 | // @ts-ignore
10 | SqlPouchCore.call(this, opts, callback)
11 | } catch (err) {
12 | callback(err)
13 | }
14 | }
15 |
16 | // Set static properties
17 | ReactNativeSQLitePouch.valid = function () {
18 | return true
19 | }
20 | ReactNativeSQLitePouch.use_prefix = false
21 |
22 | export default function reactNativeSqlitePlugin(PouchDB: any) {
23 | PouchDB.adapter('react-native-sqlite', ReactNativeSQLitePouch, true)
24 | }
25 |
--------------------------------------------------------------------------------
/src/openDatabase.ts:
--------------------------------------------------------------------------------
1 | import { open } from '@op-engineering/op-sqlite'
2 | import { TransactionQueue } from './transactionQueue'
3 | import type { DB } from '@op-engineering/op-sqlite'
4 |
5 | type SQLiteOpenParams = Parameters
6 | export type OpenDatabaseOptions = SQLiteOpenParams[0] & {
7 | revs_limit?: number
8 | }
9 | type OpenDatabaseResult =
10 | | {
11 | db: DB
12 | transactionQueue: TransactionQueue
13 | }
14 | | {
15 | error: Error
16 | }
17 |
18 | const cachedDatabases = new Map()
19 |
20 | function openDBSafely(opts: OpenDatabaseOptions): OpenDatabaseResult {
21 | try {
22 | const db = open(opts)
23 | const transactionQueue = new TransactionQueue(db)
24 | return { db, transactionQueue }
25 | } catch (err: any) {
26 | return { error: err }
27 | }
28 | }
29 |
30 | function openDB(opts: OpenDatabaseOptions) {
31 | let cachedResult: OpenDatabaseResult | undefined = cachedDatabases.get(
32 | opts.name
33 | )
34 | if (!cachedResult) {
35 | cachedResult = openDBSafely(opts)
36 | cachedDatabases.set(opts.name, cachedResult)
37 | }
38 | return cachedResult
39 | }
40 |
41 | export function closeDB(name: string) {
42 | const cachedResult = cachedDatabases.get(name)
43 | if (cachedResult) {
44 | if ('db' in cachedResult) {
45 | cachedResult.db.close()
46 | }
47 | cachedDatabases.delete(name)
48 | }
49 | }
50 |
51 | export default openDB
52 |
--------------------------------------------------------------------------------
/src/transactionQueue.ts:
--------------------------------------------------------------------------------
1 | import type { DB, Transaction } from '@op-engineering/op-sqlite'
2 | import { logger } from './debug'
3 |
4 | export interface PendingTransaction {
5 | readonly: boolean
6 | start: (tx: Transaction) => Promise
7 | finish: () => void
8 | }
9 |
10 | export class TransactionQueue {
11 | queue: PendingTransaction[] = []
12 | inProgress = false
13 | db: DB
14 |
15 | constructor(db: DB) {
16 | this.db = db
17 | }
18 |
19 | run() {
20 | if (this.inProgress) {
21 | // Transaction is already in process bail out
22 | return
23 | }
24 |
25 | if (this.queue.length) {
26 | this.inProgress = true
27 | const tx = this.queue.shift()
28 |
29 | if (!tx) {
30 | throw new Error('Could not get a operation on database')
31 | }
32 |
33 | setImmediate(async () => {
34 | try {
35 | if (tx.readonly) {
36 | logger.debug('---> transaction start!')
37 | await this.db.transaction(tx.start)
38 | // await tx.start({
39 | // commit: async () => {return { rowsAffected: 0 }},
40 | // execute: this.db.execute.bind(this.db),
41 | // rollback: async () => {return { rowsAffected: 0 }},
42 | // })
43 | } else {
44 | logger.debug('---> write transaction start!')
45 | await this.db.transaction(tx.start)
46 | }
47 | } finally {
48 | logger.debug(
49 | '<--- transaction finished! queue.length:',
50 | this.queue.length
51 | )
52 | tx.finish()
53 | this.inProgress = false
54 | if (this.queue.length) this.run()
55 | }
56 | })
57 | } else {
58 | this.inProgress = false
59 | }
60 | }
61 |
62 | async push(fn: (tx: Transaction) => Promise) {
63 | return new Promise((resolve) => {
64 | this.queue.push({ readonly: false, start: fn, finish: resolve })
65 | this.run()
66 | })
67 | }
68 |
69 | async pushReadOnly(fn: (tx: Transaction) => Promise) {
70 | return new Promise((resolve) => {
71 | this.queue.push({ readonly: true, start: fn, finish: resolve })
72 | this.run()
73 | })
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { createError, WSQ_ERROR } from 'pouchdb-errors'
2 | import { guardedConsole } from 'pouchdb-utils'
3 | import { BY_SEQ_STORE, ATTACH_STORE, ATTACH_AND_SEQ_STORE } from './constants'
4 | import type { Transaction } from '@op-engineering/op-sqlite'
5 |
6 | function stringifyDoc(doc: Record): string {
7 | // don't bother storing the id/rev. it uses lots of space,
8 | // in persistent map/reduce especially
9 | delete doc._id
10 | delete doc._rev
11 | return JSON.stringify(doc)
12 | }
13 |
14 | function unstringifyDoc(
15 | doc: string,
16 | id: string,
17 | rev: string
18 | ): Record {
19 | const parsedDoc = JSON.parse(doc)
20 | parsedDoc._id = id
21 | parsedDoc._rev = rev
22 | return parsedDoc
23 | }
24 |
25 | // question mark groups IN queries, e.g. 3 -> '(?,?,?)'
26 | function qMarks(num: number): string {
27 | let s = '('
28 | while (num--) {
29 | s += '?'
30 | if (num) {
31 | s += ','
32 | }
33 | }
34 | return s + ')'
35 | }
36 |
37 | function select(
38 | selector: string,
39 | table: string | string[],
40 | joiner?: string | null,
41 | where?: string | string[],
42 | orderBy?: string
43 | ): string {
44 | return (
45 | 'SELECT ' +
46 | selector +
47 | ' FROM ' +
48 | (typeof table === 'string' ? table : table.join(' JOIN ')) +
49 | (joiner ? ' ON ' + joiner : '') +
50 | (where
51 | ? ' WHERE ' + (typeof where === 'string' ? where : where.join(' AND '))
52 | : '') +
53 | (orderBy ? ' ORDER BY ' + orderBy : '')
54 | )
55 | }
56 |
57 | async function compactRevs(
58 | revs: string[],
59 | docId: string,
60 | tx: Transaction
61 | ): Promise {
62 | if (!revs.length) {
63 | return
64 | }
65 |
66 | let numDone = 0
67 | const seqs: number[] = []
68 |
69 | function checkDone() {
70 | if (++numDone === revs.length) {
71 | // done
72 | deleteOrphans()
73 | }
74 | }
75 |
76 | async function deleteOrphans() {
77 | // find orphaned attachment digests
78 |
79 | if (!seqs.length) {
80 | return
81 | }
82 |
83 | let sql =
84 | 'SELECT DISTINCT digest AS digest FROM ' +
85 | ATTACH_AND_SEQ_STORE +
86 | ' WHERE seq IN ' +
87 | qMarks(seqs.length)
88 |
89 | let res = await tx.execute(sql, seqs)
90 | const digestsToCheck: string[] = []
91 | if (res.rows) {
92 | for (let i = 0; i < res.rows.length; i++) {
93 | digestsToCheck.push(res.rows[i]!.digest as string)
94 | }
95 | }
96 | if (!digestsToCheck.length) {
97 | return
98 | }
99 |
100 | sql =
101 | 'DELETE FROM ' +
102 | ATTACH_AND_SEQ_STORE +
103 | ' WHERE seq IN (' +
104 | seqs.map(() => '?').join(',') +
105 | ')'
106 | await tx.execute(sql, seqs)
107 | sql =
108 | 'SELECT digest FROM ' +
109 | ATTACH_AND_SEQ_STORE +
110 | ' WHERE digest IN (' +
111 | digestsToCheck.map(() => '?').join(',') +
112 | ')'
113 | res = await tx.execute(sql, digestsToCheck)
114 | const nonOrphanedDigests = new Set()
115 | if (res.rows) {
116 | for (let i = 0; i < res.rows.length; i++) {
117 | nonOrphanedDigests.add(res.rows[i]!.digest as string)
118 | }
119 | }
120 | for (const digest of digestsToCheck) {
121 | if (nonOrphanedDigests.has(digest)) {
122 | return
123 | }
124 | await tx.execute(
125 | 'DELETE FROM ' + ATTACH_AND_SEQ_STORE + ' WHERE digest=?',
126 | [digest]
127 | )
128 | await tx.execute('DELETE FROM ' + ATTACH_STORE + ' WHERE digest=?', [
129 | digest,
130 | ])
131 | }
132 | }
133 |
134 | // update by-seq and attach stores in parallel
135 | for (const rev of revs) {
136 | const sql = 'SELECT seq FROM ' + BY_SEQ_STORE + ' WHERE doc_id=? AND rev=?'
137 |
138 | const res = await tx.execute(sql, [docId, rev])
139 | if (!res.rows?.length) {
140 | // already deleted
141 | return checkDone()
142 | }
143 | const seq = res.rows[0]!.seq as number
144 | seqs.push(seq)
145 |
146 | await tx.execute('DELETE FROM ' + BY_SEQ_STORE + ' WHERE seq=?', [seq])
147 | }
148 | }
149 |
150 | export function handleSQLiteError(
151 | event: Error,
152 | callback?: (error: any) => void
153 | ) {
154 | guardedConsole('error', 'SQLite threw an error', event)
155 | // event may actually be a SQLError object, so report is as such
156 | const errorNameMatch =
157 | event && event.constructor.toString().match(/function ([^(]+)/)
158 | const errorName = (errorNameMatch && errorNameMatch[1]) || event.name
159 | const errorReason = event.message
160 | const error = createError(WSQ_ERROR, errorReason, errorName)
161 | if (callback) callback(error)
162 | else return error
163 | }
164 |
165 | export { stringifyDoc, unstringifyDoc, qMarks, select, compactRevs }
166 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig",
3 | "exclude": ["example", "lib"]
4 | }
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "rootDir": ".",
4 | "paths": {
5 | "pouchdb-adapter-react-native-sqlite": ["./src/index"]
6 | },
7 | "allowUnreachableCode": false,
8 | "allowUnusedLabels": false,
9 | "esModuleInterop": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "jsx": "react-jsx",
12 | "lib": ["ESNext"],
13 | "module": "ESNext",
14 | "moduleResolution": "Bundler",
15 | "noEmit": true,
16 | "noFallthroughCasesInSwitch": true,
17 | "noImplicitReturns": true,
18 | "noImplicitUseStrict": false,
19 | "noStrictGenericChecks": false,
20 | "noUncheckedIndexedAccess": true,
21 | "noUnusedLocals": true,
22 | "noUnusedParameters": true,
23 | "resolveJsonModule": true,
24 | "resolvePackageJsonImports": false,
25 | "skipLibCheck": true,
26 | "strict": true,
27 | "target": "ESNext",
28 | "verbatimModuleSyntax": true
29 | }
30 | }
31 |
--------------------------------------------------------------------------------