├── .all-contributorsrc ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── ci.yml ├── .gitignore ├── .husky ├── .gitignore └── pre-commit ├── .npmignore ├── CapacitorCommunityHttp.podspec ├── LICENSE ├── README.md ├── android ├── .idea │ ├── compiler.xml │ ├── misc.xml │ └── runConfigurations.xml ├── android.iml ├── build.gradle ├── capacitor-community-http.iml ├── capacitor-http.iml ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── proguard-rules.pro ├── settings.gradle └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── getcapacitor │ │ └── android │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── getcapacitor │ │ │ └── plugin │ │ │ └── http │ │ │ ├── CapacitorCookieManager.java │ │ │ ├── CapacitorHttpUrlConnection.java │ │ │ ├── FilesystemUtils.java │ │ │ ├── FormUploader.java │ │ │ ├── Http.java │ │ │ ├── HttpRequestHandler.java │ │ │ ├── ICapacitorHttpUrlConnection.java │ │ │ ├── JSValue.java │ │ │ └── MimeType.java │ └── res │ │ ├── layout │ │ └── bridge_layout_main.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── getcapacitor │ ├── ExampleUnitTest.java │ └── plugin │ └── http │ └── HttpRequestHandlerTest.java ├── example ├── .editorconfig ├── .gitignore ├── .prettierrc.json ├── android │ ├── .gitignore │ ├── app │ │ ├── .npmignore │ │ ├── build.gradle │ │ ├── capacitor.build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── androidTest │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── getcapacitor │ │ │ │ └── myapp │ │ │ │ └── ExampleInstrumentedTest.java │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── assets │ │ │ │ ├── capacitor.config.json │ │ │ │ └── capacitor.plugins.json │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── app │ │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ │ ├── drawable-land-hdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-land-mdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-land-xhdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-land-xxhdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-land-xxxhdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-port-hdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-port-mdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-port-xhdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-port-xxhdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-port-xxxhdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ ├── drawable │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ └── splash.png │ │ │ │ ├── layout │ │ │ │ └── activity_main.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── values │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ │ └── xml │ │ │ │ ├── config.xml │ │ │ │ └── file_paths.xml │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── getcapacitor │ │ │ └── myapp │ │ │ └── ExampleUnitTest.java │ ├── build.gradle │ ├── capacitor.settings.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── variables.gradle ├── ios │ └── App │ │ ├── App.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ ├── App.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── App │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── AppIcon-20x20@1x.png │ │ │ │ ├── AppIcon-20x20@2x-1.png │ │ │ │ ├── AppIcon-20x20@2x.png │ │ │ │ ├── AppIcon-20x20@3x.png │ │ │ │ ├── AppIcon-29x29@1x.png │ │ │ │ ├── AppIcon-29x29@2x-1.png │ │ │ │ ├── AppIcon-29x29@2x.png │ │ │ │ ├── AppIcon-29x29@3x.png │ │ │ │ ├── AppIcon-40x40@1x.png │ │ │ │ ├── AppIcon-40x40@2x-1.png │ │ │ │ ├── AppIcon-40x40@2x.png │ │ │ │ ├── AppIcon-40x40@3x.png │ │ │ │ ├── AppIcon-512@2x.png │ │ │ │ ├── AppIcon-60x60@2x.png │ │ │ │ ├── AppIcon-60x60@3x.png │ │ │ │ ├── AppIcon-76x76@1x.png │ │ │ │ ├── AppIcon-76x76@2x.png │ │ │ │ ├── AppIcon-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ └── Splash.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── splash-2732x2732-1.png │ │ │ │ ├── splash-2732x2732-2.png │ │ │ │ └── splash-2732x2732.png │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── capacitor.config.json │ │ └── config.xml │ │ └── Podfile ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── readme.md ├── server │ ├── README.md │ ├── files │ │ └── static │ │ │ ├── test.jpg │ │ │ ├── test.jpg.zip │ │ │ ├── test.mp4 │ │ │ └── test.pdf │ ├── insomnia.json │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── routes │ │ │ ├── basic │ │ │ │ ├── bool.ts │ │ │ │ ├── empty.ts │ │ │ │ ├── index.ts │ │ │ │ ├── json.ts │ │ │ │ ├── number.ts │ │ │ │ └── string.ts │ │ │ ├── content-type │ │ │ │ ├── image.ts │ │ │ │ ├── index.ts │ │ │ │ ├── json.ts │ │ │ │ ├── multipart-form.ts │ │ │ │ ├── octet-stream.ts │ │ │ │ ├── pdf.ts │ │ │ │ ├── plaintext.ts │ │ │ │ ├── video.ts │ │ │ │ ├── x-www-form-urlencoded.ts │ │ │ │ ├── xml.ts │ │ │ │ └── zip.ts │ │ │ └── io │ │ │ │ ├── download.ts │ │ │ │ ├── index.ts │ │ │ │ └── upload.ts │ │ └── server.ts │ ├── tests │ │ ├── basic.routes.js │ │ └── content-type.routes.js │ └── tsconfig.json ├── src │ ├── App.tsx │ ├── index.tsx │ └── react-app-env.d.ts └── tsconfig.json ├── ios ├── Plugin.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── max.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── Plugin.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── max.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── Plugin │ ├── CapacitorCookieManager.swift │ ├── CapacitorUrlRequest.swift │ ├── FilesystemUtils.swift │ ├── HttpRequestHandler.swift │ ├── Info.plist │ ├── Plugin.h │ ├── Plugin.m │ └── Plugin.swift ├── PluginTests │ ├── Info.plist │ └── PluginTests.swift ├── Podfile └── Podfile.lock ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── cookie.ts ├── definitions.ts ├── index.ts ├── request.ts ├── utils.ts └── web.ts └── tsconfig.json /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "danielsogl", 10 | "name": "Daniel Sogl", 11 | "avatar_url": "https://avatars2.githubusercontent.com/u/15234844?v=4", 12 | "profile": "https://github.com/danielsogl", 13 | "contributions": [ 14 | "doc" 15 | ] 16 | }, 17 | { 18 | "login": "priyankpat", 19 | "name": "Priyank Patel", 20 | "avatar_url": "https://avatars3.githubusercontent.com/u/5585797?v=4", 21 | "profile": "http://priyankpatel.io", 22 | "contributions": [ 23 | "code" 24 | ] 25 | }, 26 | { 27 | "login": "mlynch", 28 | "name": "Max Lynch", 29 | "avatar_url": "https://avatars3.githubusercontent.com/u/11214?v=4", 30 | "profile": "http://ionicframework.com/", 31 | "contributions": [ 32 | "code" 33 | ] 34 | }, 35 | { 36 | "login": "pixelbucket-dev", 37 | "name": "Falk Schieber", 38 | "avatar_url": "https://avatars3.githubusercontent.com/u/12937991?v=4", 39 | "profile": "https://github.com/pixelbucket-dev", 40 | "contributions": [ 41 | "review" 42 | ] 43 | }, 44 | { 45 | "login": "andysousa", 46 | "name": "Andy Sousa", 47 | "avatar_url": "https://avatars0.githubusercontent.com/u/42151009?v=4", 48 | "profile": "https://github.com/andysousa", 49 | "contributions": [ 50 | "code" 51 | ] 52 | }, 53 | { 54 | "login": "thomasvidas", 55 | "name": "Thomas Vidas", 56 | "avatar_url": "https://avatars.githubusercontent.com/u/8182078?v=4", 57 | "profile": "https://github.com/thomasvidas", 58 | "contributions": [ 59 | "code", 60 | "maintenance" 61 | ] 62 | }, 63 | { 64 | "login": "emily-curry", 65 | "name": "Emily Curry", 66 | "avatar_url": "https://avatars.githubusercontent.com/u/20479454?v=4", 67 | "profile": "https://github.com/emily-curry", 68 | "contributions": [ 69 | "code" 70 | ] 71 | }, 72 | { 73 | "login": "graefenhain", 74 | "name": "graefenhain", 75 | "avatar_url": "https://avatars.githubusercontent.com/u/88032701?v=4", 76 | "profile": "https://github.com/graefenhain", 77 | "contributions": [ 78 | "code" 79 | ] 80 | }, 81 | { 82 | "login": "asztal", 83 | "name": "Lee Houghton", 84 | "avatar_url": "https://avatars.githubusercontent.com/u/68302?v=4", 85 | "profile": "https://github.com/asztal", 86 | "contributions": [ 87 | "bug" 88 | ] 89 | }, 90 | { 91 | "login": "FelixSchwarzmeier", 92 | "name": "Felix Schwarzmeier", 93 | "avatar_url": "https://avatars.githubusercontent.com/u/23665008?v=4", 94 | "profile": "https://github.com/FelixSchwarzmeier", 95 | "contributions": [ 96 | "code" 97 | ] 98 | }, 99 | { 100 | "login": "jkbz64", 101 | "name": "Kamil Jakubus", 102 | "avatar_url": "https://avatars.githubusercontent.com/u/13223538?v=4", 103 | "profile": "https://github.com/jkbz64", 104 | "contributions": [ 105 | "code" 106 | ] 107 | }, 108 | { 109 | "login": "joeflateau", 110 | "name": "Joe Flateau", 111 | "avatar_url": "https://avatars.githubusercontent.com/u/643331?v=4", 112 | "profile": "http://joeflateau.net/", 113 | "contributions": [ 114 | "bug" 115 | ] 116 | }, 117 | { 118 | "login": "Frank608", 119 | "name": "Frank608", 120 | "avatar_url": "https://avatars.githubusercontent.com/u/56638143?v=4", 121 | "profile": "https://github.com/Frank608", 122 | "contributions": [ 123 | "bug" 124 | ] 125 | }, 126 | { 127 | "login": "JoelNietoTec", 128 | "name": "Joel Nieto", 129 | "avatar_url": "https://avatars.githubusercontent.com/u/6298693?v=4", 130 | "profile": "https://github.com/JoelNietoTec", 131 | "contributions": [ 132 | "bug" 133 | ] 134 | }, 135 | { 136 | "login": "ultimate-tester", 137 | "name": "ultimate-tester", 138 | "avatar_url": "https://avatars.githubusercontent.com/u/580758?v=4", 139 | "profile": "https://github.com/ultimate-tester", 140 | "contributions": [ 141 | "code" 142 | ] 143 | }, 144 | { 145 | "login": "sgzadrian", 146 | "name": "Adrian Sanchez", 147 | "avatar_url": "https://avatars.githubusercontent.com/u/12704905?v=4", 148 | "profile": "https://github.com/sgzadrian", 149 | "contributions": [ 150 | "bug" 151 | ] 152 | }, 153 | { 154 | "login": "milanc", 155 | "name": "milanc", 156 | "avatar_url": "https://avatars.githubusercontent.com/u/8333458?v=4", 157 | "profile": "https://github.com/milanc", 158 | "contributions": [ 159 | "code" 160 | ] 161 | }, 162 | { 163 | "login": "herecoulbeyourname", 164 | "name": "herecoulbeyourname", 165 | "avatar_url": "https://avatars.githubusercontent.com/u/57253976?v=4", 166 | "profile": "https://github.com/herecoulbeyourname", 167 | "contributions": [ 168 | "code" 169 | ] 170 | }, 171 | { 172 | "login": "Landschaft", 173 | "name": "Landschaft", 174 | "avatar_url": "https://avatars.githubusercontent.com/u/10559398?v=4", 175 | "profile": "https://github.com/Landschaft", 176 | "contributions": [ 177 | "code" 178 | ] 179 | }, 180 | { 181 | "login": "stonewoodman", 182 | "name": "stonewoodman", 183 | "avatar_url": "https://avatars.githubusercontent.com/u/2945329?v=4", 184 | "profile": "https://github.com/stonewoodman", 185 | "contributions": [ 186 | "bug" 187 | ] 188 | }, 189 | { 190 | "login": "mghcs87", 191 | "name": "Héctor Cruz", 192 | "avatar_url": "https://avatars.githubusercontent.com/u/17180632?v=4", 193 | "profile": "https://github.com/mghcs87", 194 | "contributions": [ 195 | "bug" 196 | ] 197 | }, 198 | { 199 | "login": "patrickbussmann", 200 | "name": "Patrick Bußmann", 201 | "avatar_url": "https://avatars.githubusercontent.com/u/15617021?v=4", 202 | "profile": "https://github.com/patrickbussmann", 203 | "contributions": [ 204 | "code" 205 | ] 206 | }, 207 | { 208 | "login": "jesperbjerke", 209 | "name": "Jesper Bjerke", 210 | "avatar_url": "https://avatars.githubusercontent.com/u/5323483?v=4", 211 | "profile": "https://github.com/jesperbjerke", 212 | "contributions": [ 213 | "bug" 214 | ] 215 | } 216 | ], 217 | "badgeTemplate": "-orange?style=flat-square\" />", 218 | "contributorsPerLine": 7, 219 | "projectName": "http", 220 | "projectOwner": "capacitor-community", 221 | "repoType": "github", 222 | "repoHost": "https://github.com", 223 | "skipCi": true, 224 | "commitConvention": "none" 225 | } 226 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * mlynch 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this Capacitor Community project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities. 4 | 5 | Communication through this repository must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 6 | 7 | We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to this Capacitor Community project to do the same. 8 | 9 | If any member of the community violates this code of conduct, the maintainers of this Capacitor Community and/or the Ionic project may take action, including but not limited to removing issues, comments, and PRs or blocking accounts as deemed appropriate. 10 | 11 | If you are subject to or witness unacceptable behavior, or have any other concerns, please contact the maintainer of this repository or email hi@ionicframework.com. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Test and Build Plugin 2 | 3 | on: 4 | push: 5 | branches: [master, develop] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Install yarn 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: '14' 20 | 21 | - name: Install deps 22 | run: yarn install 23 | 24 | # - name: Run tests 25 | # run: yarn test 26 | 27 | - name: Run build 28 | run: yarn build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ============== 2 | # Android ignore 3 | # ============== 4 | 5 | # Built application files 6 | *.apk 7 | *.aar 8 | *.ap_ 9 | *.aab 10 | 11 | # Files for the ART/Dalvik VM 12 | *.dex 13 | 14 | # Java class files 15 | *.class 16 | 17 | # Generated files 18 | bin/ 19 | gen/ 20 | out/ 21 | # Uncomment the following line in case you need and you don't have the release build type files in your app 22 | # release/ 23 | 24 | # Gradle files 25 | .gradle/ 26 | build/ 27 | 28 | # Local configuration file (sdk path, etc) 29 | local.properties 30 | 31 | # Proguard folder generated by Eclipse 32 | proguard/ 33 | 34 | # Log Files 35 | *.log 36 | 37 | # Android Studio Navigation editor temp files 38 | .navigation/ 39 | 40 | # Android Studio captures folder 41 | captures/ 42 | 43 | # IntelliJ 44 | *.iml 45 | android/.idea/workspace.xml 46 | android/.idea/tasks.xml 47 | android/.idea/gradle.xml 48 | android/.idea/assetWizardSettings.xml 49 | android/.idea/dictionaries 50 | android/.idea/libraries 51 | android/.idea/jarRepositories.xml 52 | # Android Studio 3 in .gitignore file. 53 | android/.idea/caches 54 | android/.idea/modules.xml 55 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 56 | android/.idea/navEditor.xml 57 | 58 | # Keystore files 59 | # Uncomment the following lines if you do not want to check your keystore files in. 60 | #*.jks 61 | #*.keystore 62 | 63 | # External native build folder generated in Android Studio 2.2 and later 64 | .externalNativeBuild 65 | .cxx/ 66 | 67 | # Google Services (e.g. APIs or Firebase) 68 | # google-services.json 69 | 70 | # Freeline 71 | freeline.py 72 | freeline/ 73 | freeline_project_description.json 74 | 75 | # fastlane 76 | fastlane/report.xml 77 | fastlane/Preview.html 78 | fastlane/screenshots 79 | fastlane/test_output 80 | fastlane/readme.md 81 | 82 | # Version control 83 | vcs.xml 84 | 85 | # lint 86 | lint/intermediates/ 87 | lint/generated/ 88 | lint/outputs/ 89 | lint/tmp/ 90 | # lint/reports/ 91 | 92 | # Android Profiling 93 | *.hprof 94 | 95 | # ========== 96 | # iOS ignore 97 | # ========== 98 | # Xcode 99 | # 100 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 101 | 102 | ## User settings 103 | xcuserdata/ 104 | 105 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 106 | *.xcscmblueprint 107 | *.xccheckout 108 | 109 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 110 | build/ 111 | DerivedData/ 112 | *.moved-aside 113 | *.pbxuser 114 | !default.pbxuser 115 | *.mode1v3 116 | !default.mode1v3 117 | *.mode2v3 118 | !default.mode2v3 119 | *.perspectivev3 120 | !default.perspectivev3 121 | 122 | ## Obj-C/Swift specific 123 | *.hmap 124 | 125 | ## App packaging 126 | *.ipa 127 | *.dSYM.zip 128 | *.dSYM 129 | 130 | ## Playgrounds 131 | timeline.xctimeline 132 | playground.xcworkspace 133 | 134 | # Swift Package Manager 135 | # 136 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 137 | # Packages/ 138 | # Package.pins 139 | # Package.resolved 140 | # *.xcodeproj 141 | # 142 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 143 | # hence it is not needed unless you have added a package configuration file to your project 144 | # .swiftpm 145 | 146 | .build/ 147 | 148 | # CocoaPods 149 | # 150 | # We recommend against adding the Pods directory to your .gitignore. However 151 | # you should judge for yourself, the pros and cons are mentioned at: 152 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 153 | # 154 | Pods/ 155 | # 156 | # Add this line if you want to avoid checking in source code from the Xcode workspace 157 | # *.xcworkspace 158 | 159 | # Carthage 160 | # 161 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 162 | # Carthage/Checkouts 163 | 164 | Carthage/Build/ 165 | 166 | # Accio dependency management 167 | Dependencies/ 168 | .accio/ 169 | 170 | # fastlane 171 | # 172 | # It is recommended to not store the screenshots in the git repo. 173 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 174 | # For more information about the recommended setup visit: 175 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 176 | 177 | fastlane/report.xml 178 | fastlane/Preview.html 179 | fastlane/screenshots/**/*.png 180 | fastlane/test_output 181 | 182 | # Code Injection 183 | # 184 | # After new code Injection tools there's a generated folder /iOSInjectionProject 185 | # https://github.com/johnno1962/injectionforxcode 186 | 187 | iOSInjectionProject/ 188 | 189 | # ========== 190 | # Web ignore 191 | # ========== 192 | node_modules 193 | dist 194 | 195 | # ============== 196 | # Example ignore 197 | # ============== 198 | example/ios/App/App/public 199 | example/ios/capacitor-cordova-ios-plugins 200 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx pretty-quick --staged 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # node files 2 | dist/ 3 | node_modules/ 4 | 5 | # iOS files 6 | Pods 7 | Build 8 | xcuserdata 9 | 10 | # macOS files 11 | .DS_Store 12 | 13 | # Based on Android gitignore template: https://github.com/github/gitignore/blob/master/Android.gitignore 14 | 15 | # Built application files 16 | *.apk 17 | *.ap_ 18 | 19 | # Files for the ART/Dalvik VM 20 | *.dex 21 | 22 | # Java class files 23 | *.class 24 | 25 | # Generated files 26 | bin/ 27 | gen/ 28 | out/ 29 | 30 | # Gradle files 31 | .gradle/ 32 | build/ 33 | 34 | # Local configuration file (sdk path, etc) 35 | local.properties 36 | 37 | # Proguard folder generated by Eclipse 38 | proguard/ 39 | 40 | # Log Files 41 | *.log 42 | 43 | # Android Studio Navigation editor temp files 44 | .navigation/ 45 | 46 | # Android Studio captures folder 47 | captures/ 48 | 49 | # IntelliJ 50 | *.iml 51 | .idea 52 | 53 | # Keystore files 54 | # Uncomment the following line if you do not want to check your keystore files in. 55 | #*.jks 56 | 57 | # External native build folder generated in Android Studio 2.2 and later 58 | .externalNativeBuild 59 | -------------------------------------------------------------------------------- /CapacitorCommunityHttp.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = 'CapacitorCommunityHttp' 7 | s.version = package['version'] 8 | s.summary = package['description'] 9 | s.license = package['license'] 10 | s.homepage = package['repository']['url'] 11 | s.author = package['author'] 12 | s.source = { git: package['repository']['url'], tag: s.version.to_s } 13 | s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}' 14 | s.ios.deployment_target = '12.0' 15 | s.dependency 'Capacitor' 16 | s.swift_version = '5.1' 17 | end 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Drifty Co. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /android/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /android/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.12' 3 | androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.1' 4 | androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.2.0' 5 | } 6 | 7 | buildscript { 8 | repositories { 9 | google() 10 | jcenter() 11 | } 12 | dependencies { 13 | classpath 'com.android.tools.build:gradle:3.6.1' 14 | } 15 | } 16 | 17 | apply plugin: 'com.android.library' 18 | 19 | android { 20 | compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 29 21 | defaultConfig { 22 | minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 21 23 | targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 29 24 | versionCode 1 25 | versionName "1.0" 26 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 27 | } 28 | buildTypes { 29 | release { 30 | minifyEnabled false 31 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 32 | } 33 | } 34 | lintOptions { 35 | abortOnError false 36 | } 37 | } 38 | 39 | repositories { 40 | google() 41 | jcenter() 42 | mavenCentral() 43 | } 44 | 45 | 46 | dependencies { 47 | implementation fileTree(dir: 'libs', include: ['*.jar']) 48 | implementation project(':capacitor-android') 49 | testImplementation "junit:junit:$junitVersion" 50 | androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" 51 | androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" 52 | } 53 | -------------------------------------------------------------------------------- /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 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | 19 | # Supports AndroidX 20 | android.useAndroidX=true 21 | android.enableJetifier=true -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Dec 01 12:41:00 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':capacitor-android' 2 | project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') -------------------------------------------------------------------------------- /android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.android; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import android.content.Context; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | import androidx.test.platform.app.InstrumentationRegistry; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * @see Testing documentation 15 | */ 16 | @RunWith(AndroidJUnit4.class) 17 | public class ExampleInstrumentedTest { 18 | 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 23 | 24 | assertEquals("com.getcapacitor.android", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /android/src/main/java/com/getcapacitor/plugin/http/FilesystemUtils.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.plugin.http; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.os.Environment; 6 | import java.io.File; 7 | 8 | public class FilesystemUtils { 9 | 10 | public static final String DIRECTORY_DOCUMENTS = "DOCUMENTS"; 11 | public static final String DIRECTORY_APPLICATION = "APPLICATION"; 12 | public static final String DIRECTORY_DOWNLOADS = "DOWNLOADS"; 13 | public static final String DIRECTORY_DATA = "DATA"; 14 | public static final String DIRECTORY_CACHE = "CACHE"; 15 | public static final String DIRECTORY_EXTERNAL = "EXTERNAL"; 16 | public static final String DIRECTORY_EXTERNAL_STORAGE = "EXTERNAL_STORAGE"; 17 | 18 | public static File getFileObject(Context c, String path, String directory) { 19 | if (directory == null || path.startsWith("file://")) { 20 | Uri u = Uri.parse(path); 21 | if (u.getScheme() == null || u.getScheme().equals("file")) { 22 | return new File(u.getPath()); 23 | } 24 | } 25 | 26 | File androidDirectory = FilesystemUtils.getDirectory(c, directory); 27 | 28 | if (androidDirectory == null) { 29 | return null; 30 | } else { 31 | if (!androidDirectory.exists()) { 32 | androidDirectory.mkdir(); 33 | } 34 | } 35 | 36 | return new File(androidDirectory, path); 37 | } 38 | 39 | public static File getDirectory(Context c, String directory) { 40 | switch (directory) { 41 | case DIRECTORY_APPLICATION: 42 | case DIRECTORY_DATA: 43 | return c.getFilesDir(); 44 | case DIRECTORY_DOCUMENTS: 45 | return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS); 46 | case DIRECTORY_DOWNLOADS: 47 | return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); 48 | case DIRECTORY_CACHE: 49 | return c.getCacheDir(); 50 | case DIRECTORY_EXTERNAL: 51 | return c.getExternalFilesDir(null); 52 | case DIRECTORY_EXTERNAL_STORAGE: 53 | return Environment.getExternalStorageDirectory(); 54 | } 55 | return null; 56 | } 57 | 58 | /** 59 | * True if the given directory string is a public storage directory, which is accessible by the user or other apps. 60 | * @param directory the directory string. 61 | */ 62 | public static boolean isPublicDirectory(String directory) { 63 | return (DIRECTORY_DOCUMENTS.equals(directory) || DIRECTORY_DOWNLOADS.equals(directory) || "EXTERNAL_STORAGE".equals(directory)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /android/src/main/java/com/getcapacitor/plugin/http/FormUploader.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.plugin.http; 2 | 3 | import com.getcapacitor.JSObject; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.IOException; 7 | import java.io.OutputStream; 8 | import java.io.OutputStreamWriter; 9 | import java.io.PrintWriter; 10 | import java.net.HttpURLConnection; 11 | import java.net.URLConnection; 12 | import java.util.Iterator; 13 | import java.util.UUID; 14 | import org.json.JSONException; 15 | 16 | public class FormUploader { 17 | 18 | private final String LINE_FEED = "\r\n"; 19 | private final String boundary; 20 | private final String charset = "UTF-8"; 21 | private final OutputStream outputStream; 22 | private final PrintWriter prWriter; 23 | 24 | /** 25 | * This constructor initializes a new HTTP POST request with content type 26 | * is set to multipart/form-data 27 | * @param connection The HttpUrlConnection to use to upload a Form 28 | * @throws IOException Thrown if unable to parse the OutputStream of the connection 29 | */ 30 | public FormUploader(HttpURLConnection connection) throws IOException { 31 | UUID uuid = UUID.randomUUID(); 32 | boundary = uuid.toString(); 33 | 34 | connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); 35 | 36 | outputStream = connection.getOutputStream(); 37 | prWriter = new PrintWriter(new OutputStreamWriter(outputStream, charset), true); 38 | } 39 | 40 | /** 41 | * Adds a form field to the request 42 | * 43 | * @param name field name 44 | * @param value field value 45 | */ 46 | public void addFormField(String name, String value) { 47 | prWriter 48 | .append(LINE_FEED) 49 | .append("--") 50 | .append(boundary) 51 | .append(LINE_FEED) 52 | .append("Content-Disposition: form-data; name=\"") 53 | .append(name) 54 | .append("\"") 55 | .append(LINE_FEED) 56 | .append("Content-Type: text/plain; charset=") 57 | .append(charset) 58 | .append(LINE_FEED) 59 | .append(LINE_FEED) 60 | .append(value) 61 | .append(LINE_FEED) 62 | .append("--") 63 | .append(boundary) 64 | .append("--") 65 | .append(LINE_FEED); 66 | prWriter.flush(); 67 | } 68 | 69 | /** 70 | * Adds a form field to the prWriter 71 | * 72 | * @param name field name 73 | * @param value field value 74 | */ 75 | private void appendFieldToWriter(String name, String value) { 76 | prWriter 77 | .append(LINE_FEED) 78 | .append("--") 79 | .append(boundary) 80 | .append(LINE_FEED) 81 | .append("Content-Disposition: form-data; name=\"") 82 | .append(name) 83 | .append("\"") 84 | .append(LINE_FEED) 85 | .append("Content-Type: text/plain; charset=") 86 | .append(charset) 87 | .append(LINE_FEED) 88 | .append(LINE_FEED) 89 | .append(value); 90 | } 91 | 92 | /** 93 | * Adds a upload file section to the request 94 | * 95 | * @param fieldName name attribute in 96 | * @param uploadFile a File to be uploaded 97 | * @throws IOException Thrown if unable to parse the OutputStream of the connection 98 | */ 99 | public void addFilePart(String fieldName, File uploadFile, JSObject data) throws IOException { 100 | String fileName = uploadFile.getName(); 101 | prWriter 102 | .append(LINE_FEED) 103 | .append("--") 104 | .append(boundary) 105 | .append(LINE_FEED) 106 | .append("Content-Disposition: form-data; name=\"") 107 | .append(fieldName) 108 | .append("\"; filename=\"") 109 | .append(fileName) 110 | .append("\"") 111 | .append(LINE_FEED) 112 | .append("Content-Type: ") 113 | .append(URLConnection.guessContentTypeFromName(fileName)) 114 | .append(LINE_FEED) 115 | .append(LINE_FEED); 116 | prWriter.flush(); 117 | 118 | FileInputStream inputStream = new FileInputStream(uploadFile); 119 | byte[] buffer = new byte[4096]; 120 | int bytesRead; 121 | while ((bytesRead = inputStream.read(buffer)) != -1) { 122 | outputStream.write(buffer, 0, bytesRead); 123 | } 124 | outputStream.flush(); 125 | inputStream.close(); 126 | 127 | if (data != null) { 128 | Iterator keyIterator = data.keys(); 129 | while (keyIterator.hasNext()) { 130 | String key = keyIterator.next(); 131 | try { 132 | Object value = data.get(key); 133 | 134 | if (!(value instanceof String)) continue; 135 | 136 | appendFieldToWriter(key, value.toString()); 137 | } catch (JSONException e) { 138 | e.printStackTrace(); 139 | } 140 | } 141 | } 142 | 143 | prWriter.append(LINE_FEED).append("--").append(boundary).append("--").append(LINE_FEED); 144 | prWriter.flush(); 145 | } 146 | 147 | /** 148 | * Adds a header field to the request. 149 | * 150 | * @param name - name of the header field 151 | * @param value - value of the header field 152 | */ 153 | public void addHeaderField(String name, String value) { 154 | prWriter.append(name).append(": ").append(value).append(LINE_FEED); 155 | prWriter.flush(); 156 | } 157 | 158 | /** 159 | * Completes the request and receives response from the server. 160 | * returns a list of Strings as response in case the server returned 161 | * status OK, otherwise an exception is thrown. 162 | */ 163 | public void finish() { 164 | prWriter.append(LINE_FEED); 165 | prWriter.flush(); 166 | prWriter.append("--").append(boundary).append("--").append(LINE_FEED); 167 | prWriter.close(); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /android/src/main/java/com/getcapacitor/plugin/http/ICapacitorHttpUrlConnection.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.plugin.http; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | /** 7 | * This interface was extracted from {@link CapacitorHttpUrlConnection} to enable mocking that class. 8 | */ 9 | interface ICapacitorHttpUrlConnection { 10 | InputStream getErrorStream(); 11 | 12 | String getHeaderField(String name); 13 | 14 | InputStream getInputStream() throws IOException; 15 | } 16 | -------------------------------------------------------------------------------- /android/src/main/java/com/getcapacitor/plugin/http/JSValue.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.plugin.http; 2 | 3 | import com.getcapacitor.JSArray; 4 | import com.getcapacitor.JSObject; 5 | import com.getcapacitor.PluginCall; 6 | import org.json.JSONException; 7 | 8 | /** 9 | * Represents a single user-data value of any type on the capacitor PluginCall object. 10 | */ 11 | public class JSValue { 12 | 13 | private final Object value; 14 | 15 | /** 16 | * @param call The capacitor plugin call, used for accessing the value safely. 17 | * @param name The name of the property to access. 18 | */ 19 | public JSValue(PluginCall call, String name) { 20 | this.value = this.toValue(call, name); 21 | } 22 | 23 | /** 24 | * Returns the coerced but uncasted underlying value. 25 | */ 26 | public Object getValue() { 27 | return this.value; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return this.getValue().toString(); 33 | } 34 | 35 | /** 36 | * Returns the underlying value as a JSObject, or throwing if it cannot. 37 | * 38 | * @throws JSONException If the underlying value is not a JSObject. 39 | */ 40 | public JSObject toJSObject() throws JSONException { 41 | if (this.value instanceof JSObject) return (JSObject) this.value; 42 | throw new JSONException("JSValue could not be coerced to JSObject."); 43 | } 44 | 45 | /** 46 | * Returns the underlying value as a JSArray, or throwing if it cannot. 47 | * 48 | * @throws JSONException If the underlying value is not a JSArray. 49 | */ 50 | public JSArray toJSArray() throws JSONException { 51 | if (this.value instanceof JSArray) return (JSArray) this.value; 52 | throw new JSONException("JSValue could not be coerced to JSArray."); 53 | } 54 | 55 | /** 56 | * Returns the underlying value this object represents, coercing it into a capacitor-friendly object if supported. 57 | */ 58 | private Object toValue(PluginCall call, String name) { 59 | Object value = null; 60 | value = call.getArray(name, null); 61 | if (value != null) return value; 62 | value = call.getObject(name, null); 63 | if (value != null) return value; 64 | value = call.getString(name, null); 65 | if (value != null) return value; 66 | return call.getData().opt(name); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /android/src/main/java/com/getcapacitor/plugin/http/MimeType.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.plugin.http; 2 | 3 | enum MimeType { 4 | APPLICATION_JSON("application/json"), 5 | APPLICATION_VND_API_JSON("application/vnd.api+json"), // https://jsonapi.org 6 | TEXT_HTML("text/html"); 7 | 8 | private final String value; 9 | 10 | MimeType(String value) { 11 | this.value = value; 12 | } 13 | 14 | String getValue() { 15 | return value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /android/src/main/res/layout/bridge_layout_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /android/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Just a simple string 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/test/java/com/getcapacitor/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | 14 | @Test 15 | public void addition_isCorrect() throws Exception { 16 | assertEquals(4, 2 + 2); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android/src/test/java/com/getcapacitor/plugin/http/HttpRequestHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.plugin.http; 2 | 3 | import static com.getcapacitor.plugin.http.HttpRequestHandler.ResponseType.JSON; 4 | import static java.nio.charset.StandardCharsets.UTF_8; 5 | import static org.junit.Assert.assertEquals; 6 | 7 | import android.util.MutableBoolean; 8 | import com.getcapacitor.JSObject; 9 | import java.io.ByteArrayInputStream; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import org.json.JSONException; 15 | import org.json.JSONObject; 16 | import org.junit.Test; 17 | 18 | public class HttpRequestHandlerTest { 19 | 20 | @Test 21 | public void readData_error_with_HTML_message() throws IOException, JSONException { 22 | MutableBoolean isError = new MutableBoolean(false); 23 | String result = (String) HttpRequestHandler.readData(errorWithHtmlMessage("html-error"), JSON); 24 | 25 | assertEquals("html-error", result); 26 | } 27 | 28 | @Test 29 | public void readData_error_with_JSON() throws IOException, JSONException { 30 | JSObject jsonObject = new JSObject("{ 'message' : 'Hello world!' }"); 31 | 32 | JSObject result = (JSObject) HttpRequestHandler.readData(errorWithJson(jsonObject), JSON); 33 | 34 | assertEquals(jsonObject.toString(), result.toString()); 35 | } 36 | 37 | @Test 38 | public void readData_success_with_JSON() throws IOException, JSONException { 39 | JSObject jsonObject = new JSObject("{ 'message' : 'Hello world!' }"); 40 | 41 | JSObject result = (JSObject) HttpRequestHandler.readData(successWithJson(jsonObject), JSON); 42 | 43 | assertEquals(jsonObject.toString(), result.toString()); 44 | } 45 | 46 | @SuppressWarnings("SameParameterValue") 47 | private static CapacitorHttpUrlResponseMock errorWithHtmlMessage(String htmlErrorMessage) { 48 | return new CapacitorHttpUrlResponseMock( 49 | null, 50 | new ByteArrayInputStream(htmlErrorMessage.getBytes(UTF_8)), 51 | MimeType.TEXT_HTML.getValue() 52 | ); 53 | } 54 | 55 | private static CapacitorHttpUrlResponseMock errorWithJson(JSONObject jsonObject) { 56 | return new CapacitorHttpUrlResponseMock( 57 | null, 58 | new ByteArrayInputStream(jsonObject.toString().getBytes(UTF_8)), 59 | MimeType.APPLICATION_VND_API_JSON.getValue() 60 | ); 61 | } 62 | 63 | private static CapacitorHttpUrlResponseMock successWithJson(JSONObject jsonObject) { 64 | return new CapacitorHttpUrlResponseMock(new ByteArrayInputStream(jsonObject.toString().getBytes(UTF_8)), null, null); 65 | } 66 | 67 | private static class CapacitorHttpUrlResponseMock implements ICapacitorHttpUrlConnection { 68 | 69 | private final InputStream inputStream; 70 | private final InputStream errorStream; 71 | private final Map headerFields = new HashMap<>(); 72 | 73 | CapacitorHttpUrlResponseMock(InputStream inputStream, InputStream errorStream, String contentType) { 74 | this.inputStream = inputStream; 75 | this.errorStream = errorStream; 76 | if (contentType != null) { 77 | this.headerFields.put("Content-Type", contentType); 78 | } 79 | } 80 | 81 | @Override 82 | public InputStream getInputStream() { 83 | return inputStream; 84 | } 85 | 86 | @Override 87 | public InputStream getErrorStream() { 88 | return errorStream; 89 | } 90 | 91 | @Override 92 | public String getHeaderField(String name) { 93 | return headerFields.get(name); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /example/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | !www/favicon.ico 3 | www/ 4 | 5 | *~ 6 | *.sw[mnpcod] 7 | *.log 8 | *.lock 9 | *.tmp 10 | *.tmp.* 11 | log.txt 12 | *.sublime-project 13 | *.sublime-workspace 14 | 15 | .stencil/ 16 | .idea/ 17 | .vscode/ 18 | .sass-cache/ 19 | .versions/ 20 | node_modules/ 21 | $RECYCLE.BIN/ 22 | 23 | .DS_Store 24 | Thumbs.db 25 | UserInterfaceState.xcuserstate 26 | .env 27 | 28 | # Server Uploads/Downloads TODO: fix example to not use fixed path 29 | server/document.pdf 30 | server/uploads/* 31 | -------------------------------------------------------------------------------- /example/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSpacing": true, 4 | "jsxBracketSameLine": false, 5 | "jsxSingleQuote": false, 6 | "quoteProps": "consistent", 7 | "printWidth": 180, 8 | "semi": true, 9 | "singleQuote": true, 10 | "tabWidth": 2, 11 | "trailingComma": "all", 12 | "useTabs": false 13 | } 14 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM renames .gitignore to .npmignore 2 | # In order to prevent that, we remove the initial "." 3 | # And the CLI then renames it 4 | 5 | # Using Android gitignore template: https://github.com/github/gitignore/blob/master/Android.gitignore 6 | 7 | # Built application files 8 | *.apk 9 | *.ap_ 10 | *.aab 11 | 12 | # Files for the ART/Dalvik VM 13 | *.dex 14 | 15 | # Java class files 16 | *.class 17 | 18 | # Generated files 19 | bin/ 20 | gen/ 21 | out/ 22 | release/ 23 | 24 | # Gradle files 25 | .gradle/ 26 | build/ 27 | 28 | # Local configuration file (sdk path, etc) 29 | local.properties 30 | 31 | # Proguard folder generated by Eclipse 32 | proguard/ 33 | 34 | # Log Files 35 | *.log 36 | 37 | # Android Studio Navigation editor temp files 38 | .navigation/ 39 | 40 | # Android Studio captures folder 41 | captures/ 42 | 43 | # IntelliJ 44 | *.iml 45 | .idea/workspace.xml 46 | .idea/tasks.xml 47 | .idea/gradle.xml 48 | .idea/assetWizardSettings.xml 49 | .idea/dictionaries 50 | .idea/libraries 51 | # Android Studio 3 in .gitignore file. 52 | .idea/caches 53 | .idea/modules.xml 54 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 55 | .idea/navEditor.xml 56 | 57 | # Keystore files 58 | # Uncomment the following lines if you do not want to check your keystore files in. 59 | #*.jks 60 | #*.keystore 61 | 62 | # External native build folder generated in Android Studio 2.2 and later 63 | .externalNativeBuild 64 | 65 | # Freeline 66 | freeline.py 67 | freeline/ 68 | freeline_project_description.json 69 | 70 | # fastlane 71 | fastlane/report.xml 72 | fastlane/Preview.html 73 | fastlane/screenshots 74 | fastlane/test_output 75 | fastlane/readme.md 76 | 77 | # Version control 78 | vcs.xml 79 | 80 | # lint 81 | lint/intermediates/ 82 | lint/generated/ 83 | lint/outputs/ 84 | lint/tmp/ 85 | # lint/reports/ 86 | 87 | # Cordova plugins for Capacitor 88 | capacitor-cordova-android-plugins 89 | 90 | # Copied web assets 91 | app/src/main/assets/public 92 | -------------------------------------------------------------------------------- /example/android/app/.npmignore: -------------------------------------------------------------------------------- 1 | /build/* 2 | !/build/.npmkeep 3 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | defaultConfig { 6 | applicationId "com.example.app" 7 | minSdkVersion rootProject.ext.minSdkVersion 8 | targetSdkVersion rootProject.ext.targetSdkVersion 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | repositories { 22 | flatDir{ 23 | dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(include: ['*.jar'], dir: 'libs') 29 | implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" 30 | implementation project(':capacitor-android') 31 | testImplementation "junit:junit:$junitVersion" 32 | androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" 33 | androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" 34 | implementation project(':capacitor-cordova-android-plugins') 35 | } 36 | 37 | apply from: 'capacitor.build.gradle' 38 | 39 | try { 40 | def servicesJSON = file('google-services.json') 41 | if (servicesJSON.text) { 42 | apply plugin: 'com.google.gms.google-services' 43 | } 44 | } catch(Exception e) { 45 | logger.warn("google-services.json not found, google-services plugin not applied. Push Notifications won't work") 46 | } -------------------------------------------------------------------------------- /example/android/app/capacitor.build.gradle: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN 2 | 3 | android { 4 | compileOptions { 5 | sourceCompatibility JavaVersion.VERSION_1_8 6 | targetCompatibility JavaVersion.VERSION_1_8 7 | } 8 | } 9 | 10 | apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" 11 | dependencies { 12 | implementation project(':capacitor-community-http') 13 | 14 | } 15 | 16 | 17 | if (hasProperty('postBuildExtras')) { 18 | postBuildExtras() 19 | } 20 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /example/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.myapp; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import android.content.Context; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | import androidx.test.platform.app.InstrumentationRegistry; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * @see Testing documentation 15 | */ 16 | @RunWith(AndroidJUnit4.class) 17 | public class ExampleInstrumentedTest { 18 | 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 23 | 24 | assertEquals("com.getcapacitor.app", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /example/android/app/src/main/assets/capacitor.config.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /example/android/app/src/main/assets/capacitor.plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pkg": "@capacitor-community/http", 4 | "classpath": "com.getcapacitor.plugin.http.Http" 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.app; 2 | 3 | import android.os.Bundle; 4 | import com.getcapacitor.BridgeActivity; 5 | import com.getcapacitor.plugin.http.Http; 6 | 7 | public class MainActivity extends BridgeActivity { 8 | 9 | @Override 10 | public void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | registerPlugin(Http.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-land-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/drawable-land-hdpi/splash.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-land-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/drawable-land-mdpi/splash.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-land-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/drawable-land-xhdpi/splash.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-land-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/drawable-land-xxhdpi/splash.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-land-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/drawable-land-xxxhdpi/splash.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-port-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/drawable-port-hdpi/splash.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-port-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/drawable-port-mdpi/splash.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-port-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/drawable-port-xhdpi/splash.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-port-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/drawable-port-xxhdpi/splash.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-port-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/drawable-port-xxxhdpi/splash.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/drawable/splash.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/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/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/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/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/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/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/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/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | example 4 | example 5 | com.example.app 6 | com.example.app 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 17 | 18 | 19 | 22 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/xml/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.myapp; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | 14 | @Test 15 | public void addition_isCorrect() throws Exception { 16 | assertEquals(4, 2 + 2); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.6.1' 11 | classpath 'com.google.gms:google-services:4.3.3' 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | apply from: "variables.gradle" 19 | 20 | allprojects { 21 | repositories { 22 | google() 23 | jcenter() 24 | } 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/android/capacitor.settings.gradle: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN 2 | include ':capacitor-android' 3 | project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') 4 | 5 | include ':capacitor-community-http' 6 | project(':capacitor-community-http').projectDir = new File('../../android') 7 | -------------------------------------------------------------------------------- /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 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | 19 | # Supports AndroidX 20 | android.useAndroidX=true 21 | android.enableJetifier=true -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jan 30 13:14:22 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip 7 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | include ':capacitor-cordova-android-plugins' 3 | project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/') 4 | 5 | apply from: 'capacitor.settings.gradle' -------------------------------------------------------------------------------- /example/android/variables.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | minSdkVersion = 22 3 | compileSdkVersion = 30 4 | targetSdkVersion = 30 5 | androidxAppCompatVersion = '1.1.0' 6 | androidxCoreVersion = '1.2.0' 7 | androidxMaterialVersion = '1.1.0-rc02' 8 | androidxBrowserVersion = '1.2.0' 9 | androidxLocalbroadcastmanagerVersion = '1.0.0' 10 | firebaseMessagingVersion = '20.1.2' 11 | playServicesLocationVersion = '17.0.0' 12 | junitVersion = '4.12' 13 | androidxJunitVersion = '1.1.1' 14 | androidxEspressoCoreVersion = '3.2.0' 15 | cordovaAndroidVersion = '7.0.0' 16 | } 17 | -------------------------------------------------------------------------------- /example/ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/App/App.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/App/App/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Capacitor 3 | 4 | @UIApplicationMain 5 | class AppDelegate: UIResponder, UIApplicationDelegate { 6 | 7 | var window: UIWindow? 8 | 9 | 10 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 11 | // Override point for customization after application launch. 12 | return true 13 | } 14 | 15 | func applicationWillResignActive(_ application: UIApplication) { 16 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 17 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 18 | } 19 | 20 | func applicationDidEnterBackground(_ application: UIApplication) { 21 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 22 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 23 | } 24 | 25 | func applicationWillEnterForeground(_ application: UIApplication) { 26 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 27 | } 28 | 29 | func applicationDidBecomeActive(_ application: UIApplication) { 30 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 31 | } 32 | 33 | func applicationWillTerminate(_ application: UIApplication) { 34 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 35 | } 36 | 37 | func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { 38 | // Called when the app was launched with a url. Feel free to add additional processing here, 39 | // but if you want the App API to support tracking app url opens, make sure to keep this call 40 | return CAPBridge.handleOpenUrl(url, options) 41 | } 42 | 43 | func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { 44 | // Called when the app was launched with an activity, including Universal Links. 45 | // Feel free to add additional processing here, but if you want the App API to support 46 | // tracking app url opens, make sure to keep this call 47 | return CAPBridge.handleContinueActivity(userActivity, restorationHandler) 48 | } 49 | 50 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 51 | super.touchesBegan(touches, with: event) 52 | 53 | let statusBarRect = UIApplication.shared.statusBarFrame 54 | guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return } 55 | 56 | if statusBarRect.contains(touchPoint) { 57 | NotificationCenter.default.post(CAPBridge.statusBarTappedNotification) 58 | } 59 | } 60 | 61 | #if USE_PUSH 62 | 63 | func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { 64 | NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidRegisterForRemoteNotificationsWithDeviceToken.name()), object: deviceToken) 65 | } 66 | 67 | func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { 68 | NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidFailToRegisterForRemoteNotificationsWithError.name()), object: error) 69 | } 70 | 71 | #endif 72 | 73 | } 74 | 75 | -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "AppIcon-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "AppIcon-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "AppIcon-29x29@2x-1.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "AppIcon-29x29@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "AppIcon-40x40@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "AppIcon-40x40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "AppIcon-60x60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "AppIcon-60x60@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "AppIcon-20x20@1x.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "AppIcon-20x20@2x-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "AppIcon-29x29@1x.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "AppIcon-29x29@2x.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "AppIcon-40x40@1x.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "AppIcon-40x40@2x-1.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "AppIcon-76x76@1x.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "AppIcon-76x76@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "AppIcon-83.5x83.5@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "AppIcon-512@2x.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "splash-2732x2732-2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "splash-2732x2732-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "splash-2732x2732.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png -------------------------------------------------------------------------------- /example/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png -------------------------------------------------------------------------------- /example/ios/App/App/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /example/ios/App/App/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /example/ios/App/App/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | example 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 | 1.0 21 | CFBundleURLTypes 22 | 23 | 24 | CFBundleURLName 25 | com.getcapacitor.capacitor 26 | CFBundleURLSchemes 27 | 28 | capacitor 29 | 30 | 31 | 32 | CFBundleVersion 33 | 1 34 | LSRequiresIPhoneOS 35 | 36 | NSAppTransportSecurity 37 | 38 | NSAllowsArbitraryLoads 39 | 40 | 41 | NSCameraUsageDescription 42 | To Take Photos and Video 43 | NSLocationAlwaysUsageDescription 44 | Always allow Geolocation? 45 | NSLocationWhenInUseUsageDescription 46 | Allow Geolocation? 47 | NSMicrophoneUsageDescription 48 | To Record Audio With Video 49 | NSPhotoLibraryAddUsageDescription 50 | Store camera photos to camera 51 | NSPhotoLibraryUsageDescription 52 | To Pick Photos from Library 53 | UILaunchStoryboardName 54 | LaunchScreen 55 | UIMainStoryboardFile 56 | Main 57 | UIRequiredDeviceCapabilities 58 | 59 | armv7 60 | 61 | UISupportedInterfaceOrientations 62 | 63 | UIInterfaceOrientationPortrait 64 | UIInterfaceOrientationLandscapeLeft 65 | UIInterfaceOrientationLandscapeRight 66 | 67 | UISupportedInterfaceOrientations~ipad 68 | 69 | UIInterfaceOrientationPortrait 70 | UIInterfaceOrientationPortraitUpsideDown 71 | UIInterfaceOrientationLandscapeLeft 72 | UIInterfaceOrientationLandscapeRight 73 | 74 | UIViewControllerBasedStatusBarAppearance 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /example/ios/App/App/capacitor.config.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /example/ios/App/App/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /example/ios/App/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '12.0' 2 | use_frameworks! 3 | 4 | # workaround to avoid Xcode caching of Pods that requires 5 | # Product -> Clean Build Folder after new Cordova plugins installed 6 | # Requires CocoaPods 1.6 or newer 7 | install! 'cocoapods', :disable_input_output_paths => true 8 | 9 | def capacitor_pods 10 | pod 'Capacitor', :path => '../../node_modules/@capacitor/ios' 11 | pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios' 12 | pod 'CapacitorCommunityHttp', :path => '../../..' 13 | end 14 | 15 | target 'App' do 16 | capacitor_pods 17 | # Add your Pods here 18 | end 19 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "http-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@capacitor/android": "^3.3.2", 7 | "@capacitor/core": "^3.3.2", 8 | "@capacitor/ios": "^3.3.2", 9 | "@capacitor-community/http": "file:../", 10 | "@ionic/react": "^6.0.0-rc.3", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2" 13 | }, 14 | "devDependencies": { 15 | "@capacitor/cli": "^3.3.2", 16 | "@types/node": "^12.0.0", 17 | "@types/react": "^17.0.0", 18 | "@types/react-dom": "^17.0.0", 19 | "react-scripts": "4.0.3", 20 | "typescript": "^4.1.2" 21 | }, 22 | "scripts": { 23 | "start": "export PORT=8000 && react-scripts start", 24 | "build": "export BUILD_PATH='./www' && react-scripts build", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject" 27 | }, 28 | "eslintConfig": { 29 | "extends": [ 30 | "react-app", 31 | "react-app/jest" 32 | ] 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/public/favicon.ico -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Http Plugin Sample 10 | 11 | 12 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /example/readme.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `yarn start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `yarn test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `yarn build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `yarn eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /example/server/README.md: -------------------------------------------------------------------------------- 1 | # Example Server 2 | 3 | This example server can be used when testing the example Android and iOS applications in the `/example` folder of the `@capacitor-community/http` repo 4 | 5 | ## Routes 6 | 7 | There are 7 Http methods included for each route 8 | 9 | - GET 10 | - POST 11 | - PUT 12 | - PATCH 13 | - DELETE 14 | - OPTIONS 15 | - LINK 16 | 17 | `LINK` is a common, but non-standard, Http method. It is used in the example server as a way of checking support for any custom Http verb. If it works with `LINK` it should work with any Http method. 18 | 19 | ### Route List 20 | 21 | - `http://localhost:3000/` 22 | - Returns a 204 no content with no content-type header 23 | - `http://localhost:3000/content-type/image` 24 | - Returns a 200 with a test image and a content-type of `image/jpeg` 25 | - `http://localhost:3000/content-type/json` 26 | - Returns a 200 with a json body and a content-type of `application/json` 27 | - `http://localhost:3000/content-type/multipart-form` 28 | - Returns a 200 with a mutlipart form body and a content-type of `multipart/form-data` 29 | - `http://localhost:3000/content-type/octet-stream` 30 | - Returns a 200 with a binary body of `Hello World` and a content-type of `application/octet-stream` 31 | - `http://localhost:3000/content-type/pdf` 32 | - Returns a 200 with a test .pdf file and a content-type of `application/pdf` 33 | - `http://localhost:3000/content-type/plaintext` 34 | - Returns a 200 with an unparsed, but valid json body and a content-type of `text/plain; charset=utf-8` 35 | - `http://localhost:3000/content-type/video` 36 | - Returns a 200 with 10MB video and a content-type of `video/mp4` 37 | - `http://localhost:3000/content-type/xml` 38 | - Returns a 200 with a test .xml file and a content-type of `application/xml` 39 | - `http://localhost:3000/content-type/zip` 40 | - Returns a 200 with a test .zip file and a content-type of `application/zip` 41 | 42 | ## Insomnia 43 | 44 | [Insomnia](https://insomnia.rest/) is a tool for testing requests to and from a server. The `insomnia.json` file contains all of the routes for the server. It can be loaded in Insomnia so you can test the server manually. 45 | 46 | ## Testing 47 | 48 | Testing only works locally for now. To test, run the webserver with `npm start` and then in another terminal/process run `npm test` 49 | -------------------------------------------------------------------------------- /example/server/files/static/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/server/files/static/test.jpg -------------------------------------------------------------------------------- /example/server/files/static/test.jpg.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/server/files/static/test.jpg.zip -------------------------------------------------------------------------------- /example/server/files/static/test.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/server/files/static/test.mp4 -------------------------------------------------------------------------------- /example/server/files/static/test.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/server/files/static/test.pdf -------------------------------------------------------------------------------- /example/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "type": "module", 4 | "private": true, 5 | "version": "0.1.0", 6 | "description": "Example server for @capacitor-community/http with Hapi.js", 7 | "main": "server.mjs", 8 | "scripts": { 9 | "start": "node --experimental-specifier-resolution=node dist/server.js", 10 | "build": "tsc", 11 | "test": "uvu tests" 12 | }, 13 | "author": "Thomas Vidas ", 14 | "license": "MIT", 15 | "dependencies": { 16 | "@hapi/hapi": "^20.2.1", 17 | "@hapi/inert": "^6.0.4", 18 | "form-data": "^4.0.0" 19 | }, 20 | "devDependencies": { 21 | "@types/hapi__hapi": "^20.0.9", 22 | "@types/hapi__inert": "^5.2.3", 23 | "@types/node": "^16.11.7", 24 | "axios": "^0.24.0", 25 | "typescript": "^4.4.4", 26 | "uvu": "^0.5.2" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example/server/src/routes/basic/bool.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning a boolean for every standard route + 1 custom route (LINK) 4 | const output = true; 5 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 6 | 7 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 8 | return { 9 | method, 10 | path: '/bool', 11 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 12 | return output; 13 | }, 14 | } as Hapi.ServerRoute; 15 | }); 16 | 17 | export default routes; 18 | -------------------------------------------------------------------------------- /example/server/src/routes/basic/empty.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning an empty Http 204 for every standard route + 1 custom route (LINK) 4 | const output = null; 5 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 6 | 7 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 8 | return { 9 | method, 10 | path: '/', 11 | options: { 12 | cors: true, 13 | }, 14 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 15 | return output; 16 | }, 17 | } as Hapi.ServerRoute; 18 | }); 19 | 20 | export default routes; 21 | -------------------------------------------------------------------------------- /example/server/src/routes/basic/index.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | import EmptyRoutes from './empty'; 3 | import JsonRoutes from './json'; 4 | import StringRoutes from './string'; 5 | import NumberRoutes from './number'; 6 | import BooleanRoutes from './bool'; 7 | 8 | const output: Hapi.ServerRoute[] = []; 9 | output.push(...EmptyRoutes, ...JsonRoutes, ...StringRoutes, ...NumberRoutes, ...BooleanRoutes); 10 | export default output; 11 | -------------------------------------------------------------------------------- /example/server/src/routes/basic/json.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning a JSON object for every standard route + 1 custom route (LINK) 4 | 5 | const output = { 6 | 'stringValue': 'string value', 7 | 'numberValue': 1000, 8 | 'booleanValue': true, 9 | 'nullValue': null, 10 | 'string-with-hyphen': 'string-hyphenated value', 11 | 'nestedValue': { 12 | 'stringValue': 'string value', 13 | 'numberValue': 1000, 14 | 'booleanValue': true, 15 | 'nullValue': null, 16 | 'string-with-hyphen': 'string-hyphenated value', 17 | 'nestedValue': { 18 | 'stringValue': 'string value', 19 | 'numberValue': 1000, 20 | 'booleanValue': true, 21 | 'nullValue': null, 22 | 'string-with-hyphen': 'string-hyphenated value', 23 | }, 24 | }, 25 | }; 26 | 27 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 28 | 29 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 30 | return { 31 | method, 32 | path: '/json', 33 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 34 | return output; 35 | }, 36 | } as Hapi.ServerRoute; 37 | }); 38 | 39 | export default routes; 40 | -------------------------------------------------------------------------------- /example/server/src/routes/basic/number.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning a number for every standard route + 1 custom route (LINK) 4 | const output = 200; 5 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 6 | 7 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 8 | return { 9 | method, 10 | path: '/number', 11 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 12 | return output; 13 | }, 14 | } as Hapi.ServerRoute; 15 | }); 16 | 17 | export default routes; 18 | -------------------------------------------------------------------------------- /example/server/src/routes/basic/string.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning a string for every standard route + 1 custom route (LINK) 4 | const output = 'String Http Response. This should not break things!'; 5 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 6 | 7 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 8 | return { 9 | method, 10 | path: '/string', 11 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 12 | return output; 13 | }, 14 | } as Hapi.ServerRoute; 15 | }); 16 | 17 | export default routes; 18 | -------------------------------------------------------------------------------- /example/server/src/routes/content-type/image.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning an image with an image/jpg header for every standard route + 1 custom route (LINK) 4 | const output = { 'content-type': 'image/jpg' }; 5 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 6 | 7 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 8 | return { 9 | method, 10 | path: '/content-type/image', 11 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 12 | const response = toolkit.file(`${process.cwd()}/files/static/test.jpg`); 13 | response.header('Content-Type', 'image/jpg'); 14 | return response; 15 | }, 16 | } as Hapi.ServerRoute; 17 | }); 18 | 19 | export default routes; 20 | -------------------------------------------------------------------------------- /example/server/src/routes/content-type/index.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | import JsonRoutes from './json'; 3 | import PlaintextRoutes from './plaintext'; 4 | import MultipartFormRoutes from './multipart-form'; 5 | import ImageRoutes from './image'; 6 | import OctetStreamRoutes from './octet-stream'; 7 | import PdfRoutes from './pdf'; 8 | import VideoRoutes from './video'; 9 | import XmlRoutes from './xml'; 10 | import ZipRoutes from './zip'; 11 | 12 | const output: Hapi.ServerRoute[] = []; 13 | output.push(...JsonRoutes, ...PlaintextRoutes, ...ImageRoutes, ...OctetStreamRoutes, ...PdfRoutes, ...VideoRoutes, ...ZipRoutes, ...MultipartFormRoutes, ...XmlRoutes); 14 | export default output; 15 | -------------------------------------------------------------------------------- /example/server/src/routes/content-type/json.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning a JSON object for every standard route + 1 custom route (LINK) 4 | const output = { 'content-type': 'application/json' }; 5 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 6 | 7 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 8 | return { 9 | method, 10 | path: '/content-type/json', 11 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 12 | const response = toolkit.response(output); 13 | response.header('Content-Type', 'application/json'); 14 | return response; 15 | }, 16 | } as Hapi.ServerRoute; 17 | }); 18 | 19 | export default routes; 20 | -------------------------------------------------------------------------------- /example/server/src/routes/content-type/multipart-form.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | import FormData from 'form-data'; 3 | 4 | // Hapi route collection returning a multipart/form for every standard route + 1 custom route (LINK) 5 | const output = { 'content-type': 'multipart/form-data' }; 6 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 7 | 8 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 9 | return { 10 | method, 11 | path: '/content-type/multipart-form', 12 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 13 | const form = new FormData(); 14 | form.append('string', 'Hello World'); 15 | form.append('number', 500); 16 | form.append('buffer', Buffer.from('foobar')); 17 | 18 | const response = toolkit.response(form.getBuffer()); 19 | response.header('Content-Type', `multipart/form-data; boundary=${form.getBoundary()}`); 20 | return response; 21 | }, 22 | } as Hapi.ServerRoute; 23 | }); 24 | 25 | export default routes; 26 | -------------------------------------------------------------------------------- /example/server/src/routes/content-type/octet-stream.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning a Buffer octet-stream for every standard route + 1 custom route (LINK) 4 | const output = Buffer.from('Hello World'); 5 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 6 | 7 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 8 | return { 9 | method, 10 | path: '/content-type/octet', 11 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 12 | return output; 13 | }, 14 | } as Hapi.ServerRoute; 15 | }); 16 | 17 | export default routes; 18 | -------------------------------------------------------------------------------- /example/server/src/routes/content-type/pdf.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning a PDF with an application/pdf header for every standard route + 1 custom route (LINK) 4 | const output = { 'content-type': 'text/pdf' }; 5 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 6 | 7 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 8 | return { 9 | method, 10 | path: '/content-type/pdf', 11 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 12 | const response = toolkit.file(`${process.cwd()}/files/static/test.pdf`); 13 | response.header('Content-Type', 'application/pdf'); 14 | return response; 15 | }, 16 | } as Hapi.ServerRoute; 17 | }); 18 | 19 | export default routes; 20 | -------------------------------------------------------------------------------- /example/server/src/routes/content-type/plaintext.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning a JSON object with a text/plain header for every standard route + 1 custom route (LINK) 4 | const output = { 'content-type': 'text/plain' }; 5 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 6 | 7 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 8 | return { 9 | method, 10 | path: '/content-type/plaintext', 11 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 12 | const response = toolkit.response(output); 13 | response.header('Content-Type', 'text/plain'); 14 | return response; 15 | }, 16 | } as Hapi.ServerRoute; 17 | }); 18 | 19 | export default routes; 20 | -------------------------------------------------------------------------------- /example/server/src/routes/content-type/video.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning a video with an video/mp4 header for every standard route + 1 custom route (LINK) 4 | const output = { 'content-type': 'video/mp4' }; 5 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 6 | 7 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 8 | return { 9 | method, 10 | path: '/content-type/video', 11 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 12 | const response = toolkit.file(`${process.cwd()}/files/static/test.mp4`); 13 | response.header('Content-Type', 'video/mp4'); 14 | return response; 15 | }, 16 | } as Hapi.ServerRoute; 17 | }); 18 | 19 | export default routes; 20 | -------------------------------------------------------------------------------- /example/server/src/routes/content-type/x-www-form-urlencoded.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/server/src/routes/content-type/x-www-form-urlencoded.ts -------------------------------------------------------------------------------- /example/server/src/routes/content-type/xml.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning an image with an application/zip header for every standard route + 1 custom route (LINK) 4 | const output = { 'content-type': 'application/xml' }; 5 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 6 | 7 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 8 | return { 9 | method, 10 | path: '/content-type/xml', 11 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 12 | const response = toolkit.response('Test XML'); 13 | response.header('Content-Type', 'application/xml'); 14 | return response; 15 | }, 16 | } as Hapi.ServerRoute; 17 | }); 18 | 19 | export default routes; 20 | -------------------------------------------------------------------------------- /example/server/src/routes/content-type/zip.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | 3 | // Hapi route collection returning an image with an application/zip header for every standard route + 1 custom route (LINK) 4 | const output = { 'content-type': 'application/zip' }; 5 | const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'link']; 6 | 7 | const routes: Hapi.ServerRoute[] = httpMethods.map(method => { 8 | return { 9 | method, 10 | path: '/content-type/zip', 11 | handler: (request: Hapi.Request, toolkit: Hapi.ResponseToolkit, err: Error) => { 12 | const response = toolkit.file(`${process.cwd()}/files/static/test.jpg.zip`); 13 | response.header('Content-Type', 'application/zip'); 14 | return response; 15 | }, 16 | } as Hapi.ServerRoute; 17 | }); 18 | 19 | export default routes; 20 | -------------------------------------------------------------------------------- /example/server/src/routes/io/download.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/server/src/routes/io/download.ts -------------------------------------------------------------------------------- /example/server/src/routes/io/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/server/src/routes/io/index.ts -------------------------------------------------------------------------------- /example/server/src/routes/io/upload.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/example/server/src/routes/io/upload.ts -------------------------------------------------------------------------------- /example/server/src/server.ts: -------------------------------------------------------------------------------- 1 | import Hapi from '@hapi/hapi'; 2 | import Inert from '@hapi/inert'; 3 | import BasicRoutes from './routes/basic'; 4 | import ContentTypeRoutes from './routes/content-type'; 5 | 6 | const init = async () => { 7 | const server = Hapi.server({ 8 | port: 3000, 9 | host: 'localhost', 10 | routes: { 11 | cors: { 12 | origin: ['*'], // an array of origins or 'ignore' 13 | credentials: true, // boolean - 'Access-Control-Allow-Credentials' 14 | }, 15 | }, 16 | }); 17 | await server.register(Inert); 18 | 19 | server.route(BasicRoutes); 20 | server.route(ContentTypeRoutes); 21 | 22 | await server.start(); 23 | console.log('Server running on %s', server.info.uri); 24 | }; 25 | 26 | process.on('unhandledRejection', (err: any) => { 27 | console.log(err); 28 | process.exit(1); 29 | }); 30 | 31 | init(); 32 | 33 | /* 34 | import express from 'express' 35 | import compression from 'compression' 36 | import bodyParser from 'body-parser' 37 | import cors from 'cors' 38 | import cookieParser from 'cookie-parser' 39 | import multer from 'multer' 40 | import path from 'path' 41 | 42 | // __dirname workaround for .mjs file 43 | import { fileURLToPath } from 'url'; 44 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 45 | 46 | const app = express(); 47 | const upload = multer({ dest: 'uploads/' }) 48 | 49 | const staticPath = path.join(__dirname, '/public'); 50 | 51 | app.use(express.static(staticPath)); 52 | app.use(cors({ origin: true })); 53 | app.use(bodyParser.json()); 54 | app.use(bodyParser.urlencoded({ extended: true })); 55 | app.use(cookieParser()); 56 | 57 | app.listen(3455, function () { 58 | console.log('listening on port 3455'); 59 | }); 60 | 61 | app.get('/get', (req, res) => { 62 | const headers = req.headers; 63 | const params = req.query; 64 | console.log('Got headers', headers); 65 | console.log('Got params', params); 66 | console.log(req.url); 67 | res.status(200); 68 | res.send(); 69 | }); 70 | 71 | app.get('/get-gzip', compression({ filter: (req, res) => true, threshold: 1,}), (req, res) => { 72 | const headers = req.headers; 73 | const params = req.query; 74 | console.log('Got headers', headers); 75 | console.log('Got params', params); 76 | console.log(req.url); 77 | res.status(200); 78 | res.json({ 79 | data: 'compressed', 80 | }); 81 | }); 82 | 83 | app.get('/get-json', (req, res) => { 84 | res.status(200); 85 | res.json({ 86 | name: 'Max', 87 | superpower: 'Being Awesome', 88 | }); 89 | }); 90 | 91 | app.get('/get-html', (req, res) => { 92 | res.status(200); 93 | res.header('Content-Type', 'text/html'); 94 | res.send('

Hi

'); 95 | }); 96 | 97 | app.get('/head', (req, res) => { 98 | const headers = req.headers; 99 | console.log('HEAD'); 100 | console.log('Got headers', headers); 101 | res.status(200); 102 | res.send(); 103 | }); 104 | 105 | app.delete('/delete', (req, res) => { 106 | const headers = req.headers; 107 | console.log('DELETE'); 108 | console.log('Got headers', headers); 109 | res.status(200); 110 | res.send(); 111 | }); 112 | 113 | app.patch('/patch', (req, res) => { 114 | const headers = req.headers; 115 | console.log('PATCH'); 116 | console.log('Got headers', headers); 117 | res.status(200); 118 | res.send(); 119 | }); 120 | 121 | app.post('/post', (req, res) => { 122 | const headers = req.headers; 123 | console.log('POST'); 124 | console.log('Got headers', headers); 125 | res.status(200); 126 | res.send(); 127 | }); 128 | 129 | app.put('/put', (req, res) => { 130 | const headers = req.headers; 131 | console.log('PUT'); 132 | console.log('Got headers', headers); 133 | res.status(200); 134 | res.send(); 135 | }); 136 | 137 | app.get('/cookie', (req, res) => { 138 | console.log('COOKIE', req.cookies); 139 | res.status(200); 140 | res.send(); 141 | }); 142 | 143 | app.get('/download-pdf', (req, res) => { 144 | console.log('Sending PDF to request', +new Date()); 145 | res.download('document.pdf'); 146 | }); 147 | 148 | app.get('/set-cookies', (req, res) => { 149 | res.cookie('style', 'very cool'); 150 | res.send(); 151 | }); 152 | 153 | app.post('/upload-pdf', upload.single('myFile'), (req, res) => { 154 | console.log('Handling upload'); 155 | const file = req.file; 156 | console.log('Got file', file); 157 | 158 | res.status(200); 159 | res.send(); 160 | }); 161 | 162 | app.post('/form-data', (req, res) => { 163 | console.log('Got form data post', req.body); 164 | 165 | res.status(200); 166 | res.send(); 167 | }); 168 | 169 | app.post('/form-data-multi', upload.any(), (req, res) => { 170 | console.log('Got form data multipart post', req.body); 171 | 172 | console.log(req.files); 173 | 174 | res.status(200); 175 | res.send(); 176 | }); 177 | */ 178 | -------------------------------------------------------------------------------- /example/server/tests/basic.routes.js: -------------------------------------------------------------------------------- 1 | // tests/demo.js 2 | import { test } from 'uvu'; 3 | import * as assert from 'uvu/assert'; 4 | import axios from 'axios'; 5 | 6 | const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'LINK']; 7 | 8 | test('Is server running?', async () => { 9 | try { 10 | await axios.get('http://localhost:3000'); 11 | } catch (err) { 12 | assert.unreachable('Error! Is the example server running?'); 13 | } 14 | }); 15 | 16 | test('Basic Routes', async () => { 17 | for (const i in methods) { 18 | const method = methods[i]; 19 | const res = await axios.request({ method, url: 'http://localhost:3000/' }); 20 | 21 | // Empty requests should be fairly bare 22 | assert.is(res.status, 204); // Http 204 - No Content 23 | assert.not.ok(res.data); // Body should be empty 24 | } 25 | }); 26 | 27 | test.run(); 28 | -------------------------------------------------------------------------------- /example/server/tests/content-type.routes.js: -------------------------------------------------------------------------------- 1 | // tests/demo.js 2 | import { test } from 'uvu'; 3 | import * as assert from 'uvu/assert'; 4 | import axios from 'axios'; 5 | 6 | const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'LINK']; 7 | 8 | test('Is server running?', async () => { 9 | try { 10 | await axios.get('http://localhost:3000'); 11 | } catch (err) { 12 | assert.unreachable('Error! Is the example server running?'); 13 | } 14 | }); 15 | 16 | test('Content-Type -> Images', async () => { 17 | for (const i in methods) { 18 | const method = methods[i]; 19 | const res = await axios.request({ method, url: 'http://localhost:3000/content-type/image', responseType: 'blob' }); 20 | 21 | // Image request should return 200 with an image and proper headers 22 | assert.is(res.status, 200); // Http 200 - OK 23 | assert.is(res.headers['content-type'], 'image/jpg'); // Content-Type specifies image 24 | assert.ok(res.data); // Just check if it is a non-empty body 25 | } 26 | }); 27 | 28 | test('Content-Type -> JSON', async () => { 29 | for (const i in methods) { 30 | const method = methods[i]; 31 | const res = await axios.request({ method, url: 'http://localhost:3000/content-type/json' }); 32 | 33 | // JSON request should return 200 with parsed json and proper headers 34 | assert.is(res.status, 200); // Http 200 - OK 35 | assert.is(res.headers['content-type'], 'application/json; charset=utf-8'); // Content-Type specifies json 36 | assert.type(res.data, 'object'); // res.data['content-type'] should be parsed json 37 | assert.is(res.data['content-type'], 'application/json'); // res.data['content-type'] should be what we expect 38 | } 39 | }); 40 | 41 | test('Content-Type -> Multipart Form', async () => { 42 | for (const i in methods) { 43 | const method = methods[i]; 44 | const res = await axios.request({ method, url: 'http://localhost:3000/content-type/multipart-form' }); 45 | 46 | // Form request should return 200 with multipart form data and proper headers 47 | assert.is(res.status, 200); // Http 200 - OK 48 | assert.match(res.headers['content-type'], /multipart\/form-data; boundary/g); // Content-Type specifies multipart-form 49 | 50 | assert.type(res.data, 'string'); // res.data['content-type'] should a string that we'll match a bit 51 | assert.match(res.data, /Content-Disposition: form-data; name="string"/g); 52 | assert.match(res.data, /Content-Disposition: form-data; name="number"/g); 53 | assert.match(res.data, /Content-Disposition: form-data; name="buffer"/g); 54 | assert.match(res.data, /Content-Type: application\/octet-stream/g); 55 | } 56 | }); 57 | 58 | test('Content-Type -> Octet Stream', async () => { 59 | for (const i in methods) { 60 | const method = methods[i]; 61 | const res = await axios.request({ method, url: 'http://localhost:3000/content-type/octet', responseType: 'arraybuffer' }); 62 | 63 | // Octet request should return 200 with a blob of data and proper headers 64 | assert.is(res.status, 200); // Http 200 - OK 65 | assert.is(res.headers['content-type'], 'application/octet-stream'); // Content-Type specifies octet-stream 66 | 67 | // Response should be a buffer (based on response type so axios doesn't auto-parse it) 68 | assert.type(res.data, 'object'); 69 | 70 | // Convert buffer to string to make sure its what we expect 71 | const bufferString = res.data.toString(); 72 | assert.is(bufferString, 'Hello World'); 73 | } 74 | }); 75 | 76 | test('Content-Type -> PDF', async () => { 77 | for (const i in methods) { 78 | const method = methods[i]; 79 | const res = await axios.request({ method, url: 'http://localhost:3000/content-type/pdf', responseType: 'blob' }); 80 | 81 | // PDF request should return 200 with a pdf and proper headers 82 | assert.is(res.status, 200); // Http 200 - OK 83 | assert.is(res.headers['content-type'], 'application/pdf'); // Content-Type specifies PDF 84 | assert.ok(res.data); // Just check if it is a non-empty body 85 | } 86 | }); 87 | 88 | test('Content-Type -> Plain Text', async () => { 89 | for (const i in methods) { 90 | const method = methods[i]; 91 | const res = await axios.request({ method, url: 'http://localhost:3000/content-type/plaintext' }); 92 | 93 | // re-stringify data because axios parses it automatically without some hacks 94 | res.data = JSON.stringify(res.data); 95 | 96 | // JSON request should return 200 with a blob of data and proper headers 97 | assert.is(res.status, 200); // Http 200 - OK 98 | assert.is(res.headers['content-type'], 'text/plain; charset=utf-8'); // Content-Type specifies plaintext 99 | 100 | // Response should be a raw string 101 | assert.type(res.data, 'string'); 102 | 103 | // Convert string to JSON to make sure its what we expect 104 | const json = JSON.parse(res.data); 105 | assert.match(json['content-type'], 'text/plain'); 106 | } 107 | }); 108 | 109 | test('Content-Type -> Video', async () => { 110 | for (const i in methods) { 111 | const method = methods[i]; 112 | const res = await axios.request({ method, url: 'http://localhost:3000/content-type/video', responseType: 'blob' }); 113 | 114 | // Video request should return 200 with a video and proper headers 115 | assert.is(res.status, 200); // Http 200 - OK 116 | assert.is(res.headers['content-type'], 'video/mp4'); // Content-Type specifies video file 117 | assert.ok(res.data); // Just check if it is a non-empty body 118 | } 119 | }); 120 | 121 | test('Content-Type -> XML', async () => { 122 | for (const i in methods) { 123 | const method = methods[i]; 124 | const res = await axios.request({ method, url: 'http://localhost:3000/content-type/xml' }); 125 | 126 | // XML request should return 200 with proper XML and proper headers 127 | assert.is(res.status, 200); // Http 200 - OK 128 | assert.is(res.headers['content-type'], 'application/xml'); // Content-Type specifies xml 129 | assert.type(res.data, 'string'); 130 | assert.is(res.data, 'Test XML'); 131 | } 132 | }); 133 | 134 | test('Content-Type -> ZIP', async () => { 135 | for (const i in methods) { 136 | const method = methods[i]; 137 | const res = await axios.request({ method, url: 'http://localhost:3000/content-type/zip', responseType: 'blob' }); 138 | 139 | // Zip request should return 200 with a zip and proper headers 140 | assert.is(res.status, 200); // Http 200 - OK 141 | assert.is(res.headers['content-type'], 'application/zip'); // Content-Type specifies zip 142 | assert.ok(res.data); // Just check if it is a non-empty body 143 | } 144 | }); 145 | 146 | test.run(); 147 | -------------------------------------------------------------------------------- /example/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "skipLibCheck": true, 10 | "rootDir": "./src", 11 | "outDir": "./dist" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { IonApp } from '@ionic/react'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import App from './App'; 5 | 6 | /* Core CSS required for Ionic components to work properly */ 7 | import '@ionic/react/css/core.css'; 8 | 9 | /* Basic CSS for apps built with Ionic */ 10 | import '@ionic/react/css/normalize.css'; 11 | import '@ionic/react/css/structure.css'; 12 | import '@ionic/react/css/typography.css'; 13 | 14 | /* Optional CSS utils that can be commented out */ 15 | import '@ionic/react/css/padding.css'; 16 | import '@ionic/react/css/float-elements.css'; 17 | import '@ionic/react/css/text-alignment.css'; 18 | import '@ionic/react/css/text-transformation.css'; 19 | import '@ionic/react/css/flex-utils.css'; 20 | import '@ionic/react/css/display.css'; 21 | 22 | ReactDOM.render( 23 | 24 | 25 | 26 | 27 | , 28 | document.getElementById('root'), 29 | ); 30 | -------------------------------------------------------------------------------- /example/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"] 20 | } 21 | -------------------------------------------------------------------------------- /ios/Plugin.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Plugin.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 4 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ios/Plugin.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Plugin.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/http/27dc4c858f1eb742622bc71c590f8aa351ef8221/ios/Plugin.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ios/Plugin/CapacitorCookieManager.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Capacitor 3 | 4 | public class CapacitorCookieManager { 5 | public func encode(_ value: String) -> String { 6 | return value.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)! 7 | } 8 | 9 | public func decode(_ value: String) -> String { 10 | return value.removingPercentEncoding! 11 | } 12 | 13 | public func setCookie(_ url: URL, _ key: String, _ value: String) { 14 | let jar = HTTPCookieStorage.shared 15 | let field = ["Set-Cookie": "\(key)=\(value)"] 16 | let cookies = HTTPCookie.cookies(withResponseHeaderFields: field, for: url) 17 | jar.setCookies(cookies, for: url, mainDocumentURL: url) 18 | } 19 | 20 | public func getCookie(_ url: URL, _ key: String) -> HTTPCookie { 21 | let cookies = getCookies(url) 22 | for cookie in cookies { 23 | if (cookie.name == key) { 24 | return cookie 25 | } 26 | } 27 | return HTTPCookie() 28 | } 29 | 30 | public func getCookies(_ url: URL) -> [HTTPCookie] { 31 | let jar = HTTPCookieStorage.shared 32 | guard let cookies = jar.cookies(for: url) else { return [] } 33 | 34 | return cookies 35 | } 36 | 37 | public func deleteCookie(_ url: URL, _ key: String) { 38 | let jar = HTTPCookieStorage.shared 39 | let cookie = jar.cookies(for: url)?.first(where: { (cookie) -> Bool in 40 | return cookie.name == key 41 | }) 42 | 43 | if cookie != nil { jar.deleteCookie(cookie!) } 44 | } 45 | 46 | public func clearCookies(_ url: URL) { 47 | let jar = HTTPCookieStorage.shared 48 | jar.cookies(for: url)?.forEach({ (cookie) in jar.deleteCookie(cookie) }) 49 | } 50 | 51 | public func clearAllCookies() { 52 | let jar = HTTPCookieStorage.shared 53 | jar.cookies?.forEach({ (cookie) in jar.deleteCookie(cookie) }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ios/Plugin/CapacitorUrlRequest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Capacitor 3 | 4 | public class CapacitorUrlRequest: NSObject, URLSessionTaskDelegate { 5 | private var request: URLRequest; 6 | private var headers: [String:String]; 7 | 8 | enum CapacitorUrlRequestError: Error { 9 | case serializationError(String?) 10 | } 11 | 12 | init(_ url: URL, method: String) { 13 | request = URLRequest(url: url) 14 | request.httpMethod = method 15 | headers = [:] 16 | if let lang = Locale.autoupdatingCurrent.languageCode { 17 | if let country = Locale.autoupdatingCurrent.regionCode { 18 | headers["Accept-Language"] = "\(lang)-\(country),\(lang);q=0.5" 19 | } else { 20 | headers["Accept-Language"] = "\(lang);q=0.5" 21 | } 22 | request.addValue(headers["Accept-Language"]!, forHTTPHeaderField: "Accept-Language") 23 | } 24 | } 25 | 26 | private func getRequestDataAsJson(_ data: JSValue) throws -> Data? { 27 | // We need to check if the JSON is valid before attempting to serialize, as JSONSerialization.data will not throw an exception that can be caught, and will cause the application to crash if it fails. 28 | if JSONSerialization.isValidJSONObject(data) { 29 | return try JSONSerialization.data(withJSONObject: data) 30 | } else { 31 | throw CapacitorUrlRequest.CapacitorUrlRequestError.serializationError("[ data ] argument for request of content-type [ application/json ] must be serializable to JSON") 32 | } 33 | } 34 | 35 | private func getRequestDataAsFormUrlEncoded(_ data: JSValue) throws -> Data? { 36 | guard var components = URLComponents(url: request.url!, resolvingAgainstBaseURL: false) else { return nil } 37 | components.queryItems = [] 38 | 39 | guard let obj = data as? JSObject else { 40 | // Throw, other data types explicitly not supported 41 | throw CapacitorUrlRequestError.serializationError("[ data ] argument for request with content-type [ multipart/form-data ] may only be a plain javascript object") 42 | } 43 | 44 | obj.keys.forEach { (key: String) in 45 | components.queryItems?.append(URLQueryItem(name: key, value: "\(obj[key] ?? "")")) 46 | } 47 | 48 | 49 | if components.query != nil { 50 | return Data(components.query!.utf8) 51 | } 52 | 53 | return nil 54 | } 55 | 56 | private func getRequestDataAsMultipartFormData(_ data: JSValue) throws -> Data { 57 | guard let obj = data as? JSObject else { 58 | // Throw, other data types explicitly not supported. 59 | throw CapacitorUrlRequestError.serializationError("[ data ] argument for request with content-type [ application/x-www-form-urlencoded ] may only be a plain javascript object") 60 | } 61 | 62 | let strings: [String: String] = obj.compactMapValues { any in 63 | any as? String 64 | } 65 | 66 | var data = Data() 67 | let boundary = UUID().uuidString 68 | let contentType = "multipart/form-data; boundary=\(boundary)" 69 | request.setValue(contentType, forHTTPHeaderField: "Content-Type") 70 | headers["Content-Type"] = contentType 71 | 72 | strings.forEach { key, value in 73 | data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!) 74 | data.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!) 75 | data.append(value.data(using: .utf8)!) 76 | } 77 | data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!) 78 | 79 | return data 80 | } 81 | 82 | private func getRequestDataAsString(_ data: JSValue) throws -> Data { 83 | guard let stringData = data as? String else { 84 | throw CapacitorUrlRequestError.serializationError("[ data ] argument could not be parsed as string") 85 | } 86 | return Data(stringData.utf8) 87 | } 88 | 89 | func getRequestHeader(_ index: String) -> Any? { 90 | var normalized = [:] as [String:Any] 91 | self.headers.keys.forEach { (key: String) in 92 | normalized[key.lowercased()] = self.headers[key] 93 | } 94 | 95 | return normalized[index.lowercased()] 96 | } 97 | 98 | func getRequestData(_ body: JSValue, _ contentType: String) throws -> Data? { 99 | // If data can be parsed directly as a string, return that without processing. 100 | if let strVal = try? getRequestDataAsString(body) { 101 | return strVal 102 | } else if contentType.contains("application/json") { 103 | return try getRequestDataAsJson(body) 104 | } else if contentType.contains("application/x-www-form-urlencoded") { 105 | return try getRequestDataAsFormUrlEncoded(body) 106 | } else if contentType.contains("multipart/form-data") { 107 | return try getRequestDataAsMultipartFormData(body) 108 | } else { 109 | throw CapacitorUrlRequestError.serializationError("[ data ] argument could not be parsed for content type [ \(contentType) ]") 110 | } 111 | } 112 | 113 | public func setRequestHeaders(_ headers: [String: String]) { 114 | headers.keys.forEach { (key: String) in 115 | let value = headers[key] 116 | request.addValue(value!, forHTTPHeaderField: key) 117 | self.headers[key] = value 118 | } 119 | } 120 | 121 | public func setRequestBody(_ body: JSValue) throws { 122 | let contentType = self.getRequestHeader("Content-Type") as? String 123 | 124 | if contentType != nil { 125 | request.httpBody = try getRequestData(body, contentType!) 126 | } 127 | } 128 | 129 | public func setContentType(_ data: String?) { 130 | request.setValue(data, forHTTPHeaderField: "Content-Type") 131 | } 132 | 133 | public func setTimeout(_ timeout: TimeInterval) { 134 | request.timeoutInterval = timeout; 135 | } 136 | 137 | public func getUrlRequest() -> URLRequest { 138 | return request; 139 | } 140 | 141 | public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { 142 | completionHandler(nil) 143 | } 144 | 145 | public func getUrlSession(_ call: CAPPluginCall) -> URLSession { 146 | let disableRedirects = call.getBool("disableRedirects") ?? false 147 | if (!disableRedirects) { 148 | return URLSession.shared 149 | } 150 | return URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /ios/Plugin/FilesystemUtils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import MobileCoreServices 3 | 4 | enum FilesystemError: Error { 5 | case fileNotFound(String) 6 | case invalidPath(String) 7 | case parentFolderNotExists(String) 8 | case saveError(String) 9 | } 10 | 11 | class FilesystemUtils { 12 | /** 13 | * Get the SearchPathDirectory corresponding to the JS string 14 | */ 15 | static func getDirectory(directory: String) -> FileManager.SearchPathDirectory { 16 | switch directory { 17 | case "DOCUMENTS": 18 | return .documentDirectory 19 | case "APPLICATION": 20 | return .applicationDirectory 21 | case "CACHE": 22 | return .cachesDirectory 23 | case "DOWNLOADS": 24 | return .downloadsDirectory 25 | default: 26 | return .documentDirectory 27 | } 28 | } 29 | 30 | /** 31 | * Get the URL for this file, supporting file:// paths and 32 | * files with directory mappings. 33 | */ 34 | static func getFileUrl(_ path: String, _ directoryOption: String) -> URL? { 35 | if path.starts(with: "file://") { 36 | return URL(string: path) 37 | } 38 | 39 | let directory = FilesystemUtils.getDirectory(directory: directoryOption) 40 | 41 | guard let dir = FileManager.default.urls(for: directory, in: .userDomainMask).first else { 42 | return nil 43 | } 44 | 45 | return dir.appendingPathComponent(path) 46 | } 47 | 48 | static func createDirectoryForFile(_ fileUrl: URL, _ recursive: Bool) throws { 49 | if !FileManager.default.fileExists(atPath: fileUrl.deletingLastPathComponent().absoluteString) { 50 | if recursive { 51 | try FileManager.default.createDirectory(at: fileUrl.deletingLastPathComponent(), withIntermediateDirectories: recursive, attributes: nil) 52 | } else { 53 | throw FilesystemError.parentFolderNotExists("Parent folder doesn't exist") 54 | } 55 | } 56 | } 57 | 58 | /** 59 | * Read a file as a string at the given directory and with the given encoding 60 | */ 61 | static func readFileString(_ path: String, _ directory: String, _ encoding: String?) throws -> String { 62 | guard let fileUrl = FilesystemUtils.getFileUrl(path, directory) else { 63 | throw FilesystemError.fileNotFound("No such file exists") 64 | } 65 | if encoding != nil { 66 | let data = try String(contentsOf: fileUrl, encoding: .utf8) 67 | return data 68 | } else { 69 | let data = try Data(contentsOf: fileUrl) 70 | return data.base64EncodedString() 71 | } 72 | } 73 | 74 | static func writeFileString(_ path: String, _ directory: String, _ encoding: String?, _ data: String, _ recursive: Bool = false) throws -> URL { 75 | 76 | guard let fileUrl = FilesystemUtils.getFileUrl(path, directory) else { 77 | throw FilesystemError.invalidPath("Invlid path") 78 | } 79 | 80 | if !FileManager.default.fileExists(atPath: fileUrl.deletingLastPathComponent().absoluteString) { 81 | if recursive { 82 | try FileManager.default.createDirectory(at: fileUrl.deletingLastPathComponent(), withIntermediateDirectories: recursive, attributes: nil) 83 | } else { 84 | throw FilesystemError.parentFolderNotExists("Parent folder doesn't exist") 85 | } 86 | } 87 | 88 | if encoding != nil { 89 | try data.write(to: fileUrl, atomically: false, encoding: .utf8) 90 | } else { 91 | let cleanData = getCleanBase64Data(data) 92 | if let base64Data = Data(base64Encoded: cleanData) { 93 | try base64Data.write(to: fileUrl) 94 | } else { 95 | throw FilesystemError.saveError("Unable to save file") 96 | } 97 | } 98 | 99 | return fileUrl 100 | } 101 | 102 | 103 | static func getCleanBase64Data(_ data: String) -> String { 104 | let dataParts = data.split(separator: ",") 105 | var cleanData = data 106 | if dataParts.count > 0 { 107 | cleanData = String(dataParts.last!) 108 | } 109 | return cleanData 110 | } 111 | 112 | static func mimeTypeForPath(path: String) -> String { 113 | let url = NSURL(fileURLWithPath: path) 114 | let pathExtension = url.pathExtension 115 | if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension! as NSString, nil)?.takeRetainedValue() { 116 | if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() { 117 | return mimetype as String 118 | } 119 | } 120 | return "application/octet-stream" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Plugin/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios/Plugin/Plugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | //! Project version number for Plugin. 4 | FOUNDATION_EXPORT double PluginVersionNumber; 5 | 6 | //! Project version string for Plugin. 7 | FOUNDATION_EXPORT const unsigned char PluginVersionString[]; 8 | 9 | // In this header, you should import all the public headers of your framework using statements like #import 10 | -------------------------------------------------------------------------------- /ios/Plugin/Plugin.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | CAP_PLUGIN(HttpPlugin, "Http", 5 | CAP_PLUGIN_METHOD(request, CAPPluginReturnPromise); 6 | CAP_PLUGIN_METHOD(get, CAPPluginReturnPromise); 7 | CAP_PLUGIN_METHOD(post, CAPPluginReturnPromise); 8 | CAP_PLUGIN_METHOD(put, CAPPluginReturnPromise); 9 | CAP_PLUGIN_METHOD(patch, CAPPluginReturnPromise); 10 | CAP_PLUGIN_METHOD(del, CAPPluginReturnPromise); 11 | CAP_PLUGIN_METHOD(setCookie, CAPPluginReturnPromise); 12 | CAP_PLUGIN_METHOD(getCookiesMap, CAPPluginReturnPromise); 13 | CAP_PLUGIN_METHOD(getCookies, CAPPluginReturnPromise); 14 | CAP_PLUGIN_METHOD(getCookie, CAPPluginReturnPromise); 15 | CAP_PLUGIN_METHOD(deleteCookie, CAPPluginReturnPromise); 16 | CAP_PLUGIN_METHOD(clearCookies, CAPPluginReturnPromise); 17 | CAP_PLUGIN_METHOD(clearAllCookies, CAPPluginReturnPromise); 18 | CAP_PLUGIN_METHOD(downloadFile, CAPPluginReturnPromise); 19 | CAP_PLUGIN_METHOD(uploadFile, CAPPluginReturnPromise); 20 | ) 21 | -------------------------------------------------------------------------------- /ios/Plugin/Plugin.swift: -------------------------------------------------------------------------------- 1 | import Capacitor 2 | import Foundation 3 | 4 | @objc(HttpPlugin) public class HttpPlugin: CAPPlugin { 5 | var cookieManager: CapacitorCookieManager? = nil 6 | var capConfig: InstanceConfiguration? = nil 7 | 8 | private func getServerUrl(_ call: CAPPluginCall) -> URL? { 9 | guard let urlString = call.getString("url") else { 10 | call.reject("Invalid URL. Check that \"url\" is passed in correctly") 11 | return nil 12 | } 13 | 14 | let url = URL(string: urlString) 15 | return url; 16 | } 17 | 18 | @objc override public func load() { 19 | cookieManager = CapacitorCookieManager() 20 | capConfig = bridge?.config 21 | } 22 | 23 | @objc func http(_ call: CAPPluginCall, _ httpMethod: String?) { 24 | // Protect against bad values from JS before calling request 25 | guard let u = call.getString("url") else { return call.reject("Must provide a URL"); } 26 | guard let _ = httpMethod ?? call.getString("method") else { return call.reject("Must provide an HTTP Method"); } 27 | guard var _ = URL(string: u) else { return call.reject("Invalid URL"); } 28 | 29 | do { 30 | try HttpRequestHandler.request(call, httpMethod) 31 | } catch let e { 32 | call.reject(e.localizedDescription) 33 | } 34 | } 35 | 36 | @objc func request(_ call: CAPPluginCall) { 37 | http(call, nil) 38 | } 39 | 40 | @objc func get(_ call: CAPPluginCall) { 41 | http(call, "GET") 42 | } 43 | 44 | @objc func post(_ call: CAPPluginCall) { 45 | http(call, "POST") 46 | } 47 | 48 | @objc func put(_ call: CAPPluginCall) { 49 | http(call, "PUT") 50 | } 51 | 52 | @objc func patch(_ call: CAPPluginCall) { 53 | http(call, "PATCH") 54 | } 55 | 56 | @objc func del(_ call: CAPPluginCall) { 57 | http(call, "DELETE") 58 | } 59 | 60 | @objc func downloadFile(_ call: CAPPluginCall) { 61 | // Protect against bad values from JS before calling request 62 | guard let u = call.getString("url") else { return call.reject("Must provide a URL") } 63 | guard let _ = call.getString("filePath") else { return call.reject("Must provide a file path to download the file to") } 64 | guard let _ = URL(string: u) else { return call.reject("Invalid URL") } 65 | 66 | let progressEmitter: HttpRequestHandler.ProgressEmitter = {bytes, contentLength in 67 | self.notifyListeners("progress", data: [ 68 | "type": "DOWNLOAD", 69 | "url": u, 70 | "bytes": bytes, 71 | "contentLength": contentLength 72 | ]) 73 | } 74 | 75 | do { 76 | try HttpRequestHandler.download(call, updateProgress: progressEmitter) 77 | } catch let e { 78 | call.reject(e.localizedDescription) 79 | } 80 | } 81 | 82 | @objc func uploadFile(_ call: CAPPluginCall) { 83 | // Protect against bad values from JS before calling request 84 | let fd = call.getString("fileDirectory") ?? "DOCUMENTS" 85 | guard let u = call.getString("url") else { return call.reject("Must provide a URL") } 86 | guard let fp = call.getString("filePath") else { return call.reject("Must provide a file path to download the file to") } 87 | guard let _ = URL(string: u) else { return call.reject("Invalid URL") } 88 | guard let _ = FilesystemUtils.getFileUrl(fp, fd) else { return call.reject("Unable to get file URL") } 89 | 90 | do { 91 | try HttpRequestHandler.upload(call) 92 | } catch let e { 93 | call.reject(e.localizedDescription) 94 | } 95 | } 96 | 97 | @objc func setCookie(_ call: CAPPluginCall) { 98 | guard let key = call.getString("key") else { return call.reject("Must provide key") } 99 | guard let value = call.getString("value") else { return call.reject("Must provide value") } 100 | 101 | let url = getServerUrl(call) 102 | if url != nil { 103 | cookieManager!.setCookie(url!, key, cookieManager!.encode(value)) 104 | call.resolve() 105 | } 106 | } 107 | 108 | @objc func getCookiesMap(_ call: CAPPluginCall) { 109 | let url = getServerUrl(call) 110 | if url != nil { 111 | let cookies = cookieManager!.getCookies(url!) 112 | var cookiesMap: [String: String] = [:] 113 | for cookie in cookies { 114 | cookiesMap[cookie.name] = cookie.value 115 | } 116 | call.resolve(cookiesMap) 117 | } 118 | } 119 | 120 | @objc func getCookies(_ call: CAPPluginCall) { 121 | let url = getServerUrl(call) 122 | if url != nil { 123 | let cookies = cookieManager!.getCookies(url!) 124 | let output = cookies.map { (cookie: HTTPCookie) -> [String: String] in 125 | return [ 126 | "key": cookie.name, 127 | "value": cookie.value, 128 | ] 129 | } 130 | call.resolve([ 131 | "cookies": output 132 | ]) 133 | } 134 | } 135 | 136 | @objc func getCookie(_ call: CAPPluginCall) { 137 | guard let key = call.getString("key") else { return call.reject("Must provide key") } 138 | let url = getServerUrl(call) 139 | if url != nil { 140 | let cookie = cookieManager!.getCookie(url!, key) 141 | call.resolve([ 142 | "key": cookie.name, 143 | "value": cookieManager!.decode(cookie.value) 144 | ]) 145 | } 146 | } 147 | 148 | @objc func deleteCookie(_ call: CAPPluginCall) { 149 | guard let key = call.getString("key") else { return call.reject("Must provide key") } 150 | let url = getServerUrl(call) 151 | if url != nil { 152 | let jar = HTTPCookieStorage.shared 153 | 154 | let cookie = jar.cookies(for: url!)?.first(where: { (cookie) -> Bool in 155 | return cookie.name == key 156 | }) 157 | 158 | if cookie != nil { 159 | jar.deleteCookie(cookie!) 160 | } 161 | 162 | call.resolve() 163 | } 164 | } 165 | 166 | @objc func clearCookies(_ call: CAPPluginCall) { 167 | let url = getServerUrl(call) 168 | if url != nil { 169 | cookieManager!.clearCookies(url!) 170 | call.resolve() 171 | } 172 | } 173 | 174 | @objc func clearAllCookies(_ call: CAPPluginCall) { 175 | cookieManager!.clearAllCookies() 176 | call.resolve() 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /ios/PluginTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ios/PluginTests/PluginTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Capacitor 3 | @testable import Plugin 4 | 5 | class PluginTests: XCTestCase { 6 | 7 | override func setUp() { 8 | super.setUp() 9 | // Put setup code here. This method is called before the invocation of each test method in the class. 10 | } 11 | 12 | override func tearDown() { 13 | // Put teardown code here. This method is called after the invocation of each test method in the class. 14 | super.tearDown() 15 | } 16 | 17 | func testEcho() { 18 | // This is an example of a functional test case for a plugin. 19 | // Use XCTAssert and related functions to verify your tests produce the correct results. 20 | 21 | let value = "Hello, World!" 22 | let plugin = MyPlugin() 23 | 24 | let call = CAPPluginCall(callbackId: "test", options: [ 25 | "value": value 26 | ], success: { (result, call) in 27 | let resultValue = result!.data["value"] as? String 28 | XCTAssertEqual(value, resultValue) 29 | }, error: { (err) in 30 | XCTFail("Error shouldn't have been called") 31 | }) 32 | 33 | plugin.echo(call!) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '12.0' 2 | 3 | def capacitor_pods 4 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 5 | use_frameworks! 6 | pod 'Capacitor', path: '../node_modules/@capacitor/ios' 7 | pod 'CapacitorCordova', path: '../node_modules/@capacitor/ios' 8 | end 9 | 10 | target 'Plugin' do 11 | capacitor_pods 12 | end 13 | 14 | target 'PluginTests' do 15 | capacitor_pods 16 | end 17 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Capacitor (3.0.0): 3 | - CapacitorCordova 4 | - CapacitorCordova (3.0.0) 5 | 6 | DEPENDENCIES: 7 | - "Capacitor (from `../node_modules/@capacitor/ios`)" 8 | - "CapacitorCordova (from `../node_modules/@capacitor/ios`)" 9 | 10 | EXTERNAL SOURCES: 11 | Capacitor: 12 | :path: "../node_modules/@capacitor/ios" 13 | CapacitorCordova: 14 | :path: "../node_modules/@capacitor/ios" 15 | 16 | SPEC CHECKSUMS: 17 | Capacitor: 06cd8cd01340f5b162e9528bf5569d87a6f29009 18 | CapacitorCordova: 183c8d1af3851da6d7a371f273462c8e64e15464 19 | 20 | PODFILE CHECKSUM: b7b76f9a39066f9f3276fd95a6e5ca9625f20b3f 21 | 22 | COCOAPODS: 1.11.0 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@capacitor-community/http", 3 | "version": "1.4.1", 4 | "description": "A native HTTP plugin for CORS-free requests and file transfers", 5 | "main": "dist/plugin.cjs.js", 6 | "module": "dist/esm/index.js", 7 | "types": "dist/esm/index.d.ts", 8 | "scripts": { 9 | "build": "npm run clean && tsc && rollup -c rollup.config.js", 10 | "ios:build": "cd ios && pod install && cd ..", 11 | "docs": "typedoc src/web.ts src/definitions.ts", 12 | "clean": "rimraf ./dist", 13 | "watch": "tsc --watch", 14 | "fmt": "npm run prettier -- --write", 15 | "prettier": "prettier \"**/*.{css,html,ts,js,java}\"", 16 | "prepublishOnly": "npm run build && npm run ios:build", 17 | "prepare": "husky install" 18 | }, 19 | "author": "Max Lynch , Thomas Vidas ", 20 | "license": "MIT", 21 | "dependencies": { 22 | "@capacitor/android": "^3.0.0", 23 | "@capacitor/core": "^3.0.0", 24 | "@capacitor/filesystem": "^1.0.0", 25 | "@capacitor/ios": "^3.0.0" 26 | }, 27 | "devDependencies": { 28 | "@ionic/prettier-config": "^1.0.1", 29 | "all-contributors-cli": "^6.20.0", 30 | "husky": "^6.0.0", 31 | "prettier": "^2.3.0", 32 | "prettier-plugin-java": "^1.1.1", 33 | "pretty-quick": "^3.1.0", 34 | "rimraf": "^3.0.2", 35 | "rollup": "^2.50.0", 36 | "typedoc": "^0.20.36", 37 | "typescript": "^4.2.4" 38 | }, 39 | "husky": { 40 | "hooks": { 41 | "pre-commit": "pretty-quick --staged" 42 | } 43 | }, 44 | "files": [ 45 | "dist/", 46 | "ios/", 47 | "android/", 48 | "CapacitorCommunityHttp.podspec" 49 | ], 50 | "keywords": [ 51 | "capacitor", 52 | "plugin", 53 | "native" 54 | ], 55 | "prettier": "@ionic/prettier-config", 56 | "capacitor": { 57 | "ios": { 58 | "src": "ios" 59 | }, 60 | "android": { 61 | "src": "android" 62 | } 63 | }, 64 | "repository": { 65 | "type": "git", 66 | "url": "https://github.com/capacitor-community/http" 67 | }, 68 | "bugs": { 69 | "url": "https://github.com/capacitor-community/http/issues" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | input: 'dist/esm/index.js', 3 | output: [ 4 | { 5 | file: 'dist/plugin.js', 6 | format: 'iife', 7 | name: 'capacitorCommunityHttp', 8 | globals: { 9 | '@capacitor/core': 'capacitorExports', 10 | }, 11 | sourcemap: true, 12 | inlineDynamicImports: true, 13 | }, 14 | { 15 | file: 'dist/plugin.cjs.js', 16 | format: 'cjs', 17 | sourcemap: true, 18 | inlineDynamicImports: true, 19 | }, 20 | ], 21 | external: ['@capacitor/core'], 22 | }; 23 | -------------------------------------------------------------------------------- /src/cookie.ts: -------------------------------------------------------------------------------- 1 | import { HttpCookie, HttpCookieOptions } from './definitions'; 2 | import { encode, decode } from './utils'; 3 | 4 | /** 5 | * Set a cookie 6 | * @param key The key to set 7 | * @param value The value to set 8 | * @param options Optional additional parameters 9 | */ 10 | export const setCookie = ( 11 | key: string, 12 | value: any, 13 | options: HttpCookieOptions = {}, 14 | ): void => { 15 | // Safely Encoded Key/Value 16 | const encodedKey = encode(key); 17 | const encodedValue = encode(value); 18 | 19 | // Clean & sanitize options 20 | const expires = `; expires=${(options.expires || '').replace( 21 | 'expires=', 22 | '', 23 | )}`; // Default is "; expires=" 24 | const path = (options.path || '/').replace('path=', ''); // Default is "path=/" 25 | 26 | document.cookie = `${encodedKey}=${ 27 | encodedValue || '' 28 | }${expires}; path=${path}`; 29 | }; 30 | 31 | /** 32 | * Gets all HttpCookies 33 | */ 34 | export const getCookies = (): HttpCookie[] => { 35 | const output: HttpCookie[] = []; 36 | const map: any = {}; 37 | if (!document.cookie) { 38 | return output; 39 | } 40 | 41 | const cookies = document.cookie.split(';') || []; 42 | for (const cookie of cookies) { 43 | // Replace first "=" with CAP_COOKIE to prevent splitting on additional "=" 44 | let [k, v] = cookie.replace(/=/, 'CAP_COOKIE').split('CAP_COOKIE'); 45 | k = decode(k).trim(); 46 | v = decode(v).trim(); 47 | map[k] = v; 48 | } 49 | 50 | const entries: [string, any][] = Object.entries(map); 51 | for (const [key, value] of entries) { 52 | output.push({ 53 | key, 54 | value, 55 | }); 56 | } 57 | 58 | return output; 59 | }; 60 | 61 | /** 62 | * Gets a single HttpCookie given a key 63 | */ 64 | export const getCookie = (key: string): HttpCookie => { 65 | const cookies = getCookies(); 66 | for (const cookie of cookies) { 67 | if (cookie.key === key) { 68 | return cookie; 69 | } 70 | } 71 | 72 | return { 73 | key, 74 | value: '', 75 | }; 76 | }; 77 | 78 | /** 79 | * Deletes a cookie given a key 80 | * @param key The key of the cookie to delete 81 | */ 82 | export const deleteCookie = (key: string): void => { 83 | document.cookie = `${key}=; Max-Age=0`; 84 | }; 85 | 86 | /** 87 | * Clears out cookies by setting them to expire immediately 88 | */ 89 | export const clearCookies = (): void => { 90 | const cookies = document.cookie.split(';') || []; 91 | for (const cookie of cookies) { 92 | document.cookie = cookie 93 | .replace(/^ +/, '') 94 | .replace(/=.*/, `=;expires=${new Date().toUTCString()};path=/`); 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /src/definitions.ts: -------------------------------------------------------------------------------- 1 | import type { PluginListenerHandle } from '@capacitor/core'; 2 | import { Directory } from '@capacitor/filesystem'; 3 | 4 | type HttpResponseType = 'arraybuffer' | 'blob' | 'json' | 'text' | 'document'; 5 | 6 | export interface HttpPlugin { 7 | request(options: HttpOptions): Promise; 8 | get(options: HttpOptions): Promise; 9 | post(options: HttpOptions): Promise; 10 | put(options: HttpOptions): Promise; 11 | patch(options: HttpOptions): Promise; 12 | del(options: HttpOptions): Promise; 13 | 14 | setCookie(options: HttpSetCookieOptions): Promise; 15 | getCookie(options: HttpSingleCookieOptions): Promise; 16 | getCookies(options: HttpMultiCookiesOptions): Promise; 17 | getCookiesMap(options: HttpMultiCookiesOptions): Promise; 18 | clearCookies(options: HttpMultiCookiesOptions): Promise; 19 | clearAllCookies(): Promise; 20 | deleteCookie(options: HttpSingleCookieOptions): Promise; 21 | 22 | uploadFile(options: HttpUploadFileOptions): Promise; 23 | downloadFile( 24 | options: HttpDownloadFileOptions, 25 | ): Promise; 26 | 27 | addListener( 28 | eventName: 'progress', 29 | listenerFunc: HttpProgressListener, 30 | ): Promise & PluginListenerHandle; 31 | 32 | removeAllListeners(): Promise; 33 | } 34 | 35 | export interface HttpOptions { 36 | url: string; 37 | method?: string; 38 | params?: HttpParams; 39 | data?: any; 40 | headers?: HttpHeaders; 41 | /** 42 | * How long to wait to read additional data. Resets each time new 43 | * data is received 44 | */ 45 | readTimeout?: number; 46 | /** 47 | * How long to wait for the initial connection. 48 | */ 49 | connectTimeout?: number; 50 | /** 51 | * Sets whether automatic HTTP redirects should be disabled 52 | */ 53 | disableRedirects?: boolean; 54 | /** 55 | * Extra arguments for fetch when running on the web 56 | */ 57 | webFetchExtra?: RequestInit; 58 | /** 59 | * This is used to parse the response appropriately before returning it to 60 | * the requestee. If the response content-type is "json", this value is ignored. 61 | */ 62 | responseType?: HttpResponseType; 63 | /** 64 | * Use this option if you need to keep the URL unencoded in certain cases 65 | * (already encoded, azure/firebase testing, etc.). The default is _true_. 66 | */ 67 | shouldEncodeUrlParams?: boolean; 68 | } 69 | 70 | export interface HttpParams { 71 | [key: string]: string | string[]; 72 | } 73 | 74 | export interface HttpHeaders { 75 | [key: string]: string; 76 | } 77 | 78 | export interface HttpResponse { 79 | data: any; 80 | status: number; 81 | headers: HttpHeaders; 82 | url: string; 83 | } 84 | 85 | export interface HttpDownloadFileOptions extends HttpOptions { 86 | /** 87 | * The path the downloaded file should be moved to 88 | */ 89 | filePath: string; 90 | /** 91 | * Optionally, the directory to put the file in 92 | * 93 | * If this option is used, filePath can be a relative path rather than absolute 94 | */ 95 | fileDirectory?: Directory; 96 | /** 97 | * Optionally, the switch that enables notifying listeners about downloaded progress 98 | * 99 | * If this option is used, progress event should be dispatched on every chunk received 100 | */ 101 | progress?: Boolean; 102 | } 103 | 104 | export interface HttpUploadFileOptions extends HttpOptions { 105 | /** 106 | * The URL to upload the file to 107 | */ 108 | url: string; 109 | /** 110 | * The field name to upload the file with 111 | */ 112 | name: string; 113 | /** 114 | * For uploading a file on the web, a JavaScript Blob to upload 115 | */ 116 | blob?: Blob; 117 | /** 118 | * For uploading a file natively, the path to the file on disk to upload 119 | */ 120 | filePath?: string; 121 | /** 122 | * Optionally, the directory to look for the file in. 123 | * 124 | * If this option is used, filePath can be a relative path rather than absolute 125 | */ 126 | fileDirectory?: Directory; 127 | } 128 | 129 | export interface HttpCookie { 130 | key: string; 131 | value: string; 132 | } 133 | 134 | export interface HttpCookieMap { 135 | [key: string]: any; 136 | } 137 | 138 | export interface HttpCookieOptions { 139 | url?: string; 140 | path?: string; 141 | expires?: string; 142 | } 143 | 144 | export interface HttpSingleCookieOptions { 145 | url: string; 146 | key: string; 147 | } 148 | 149 | export interface HttpSetCookieOptions { 150 | url: string; 151 | key: string; 152 | value: string; 153 | path?: string; 154 | expires?: string; 155 | } 156 | 157 | export interface HttpMultiCookiesOptions { 158 | url: string; 159 | } 160 | 161 | export interface HttpCookieExtraOptions { 162 | path?: string; 163 | expires?: string; 164 | } 165 | 166 | export interface HttpGetCookiesResult { 167 | cookies: HttpCookie[]; 168 | } 169 | 170 | export interface HttpDownloadFileResult { 171 | path?: string; 172 | blob?: Blob; 173 | } 174 | 175 | export interface HttpUploadFileResult extends HttpResponse {} 176 | 177 | export type ProgressType = 'DOWNLOAD' | 'UPLOAD'; 178 | 179 | export interface ProgressStatus { 180 | type: ProgressType; 181 | url: string; 182 | bytes: number; 183 | contentLength: number; 184 | } 185 | 186 | export type HttpProgressListener = (progress: ProgressStatus) => void; 187 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { registerPlugin } from '@capacitor/core'; 2 | import type { HttpPlugin } from './definitions'; 3 | 4 | const Http = registerPlugin('Http', { 5 | web: () => import('./web').then(m => new m.HttpWeb()), 6 | electron: () => import('./web').then(m => new m.HttpWeb()), 7 | }); 8 | 9 | export * from './definitions'; 10 | export { Http }; 11 | -------------------------------------------------------------------------------- /src/request.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | HttpOptions, 3 | HttpResponse, 4 | HttpParams, 5 | HttpHeaders, 6 | } from './definitions'; 7 | import { readBlobAsBase64 } from './utils'; 8 | 9 | /** 10 | * Normalize an HttpHeaders map by lowercasing all of the values 11 | * @param headers The HttpHeaders object to normalize 12 | */ 13 | const normalizeHttpHeaders = (headers: HttpHeaders = {}): HttpHeaders => { 14 | const originalKeys = Object.keys(headers); 15 | const loweredKeys = Object.keys(headers).map(k => k.toLocaleLowerCase()); 16 | const normalized = loweredKeys.reduce((acc, key, index) => { 17 | acc[key] = headers[originalKeys[index]]; 18 | return acc; 19 | }, {}); 20 | return normalized; 21 | }; 22 | 23 | /** 24 | * Builds a string of url parameters that 25 | * @param params A map of url parameters 26 | * @param shouldEncode true if you should encodeURIComponent() the values (true by default) 27 | */ 28 | const buildUrlParams = ( 29 | params?: HttpParams, 30 | shouldEncode: boolean = true, 31 | ): string | null => { 32 | if (!params) return null; 33 | 34 | const output = Object.entries(params).reduce((accumulator, entry) => { 35 | const [key, value] = entry; 36 | 37 | let encodedValue: string; 38 | let item: string; 39 | if (Array.isArray(value)) { 40 | item = ''; 41 | value.forEach(str => { 42 | encodedValue = shouldEncode ? encodeURIComponent(str) : str; 43 | item += `${key}=${encodedValue}&`; 44 | }); 45 | // last character will always be "&" so slice it off 46 | item.slice(0, -1); 47 | } else { 48 | encodedValue = shouldEncode ? encodeURIComponent(value) : value; 49 | item = `${key}=${encodedValue}`; 50 | } 51 | 52 | return `${accumulator}&${item}`; 53 | }, ''); 54 | 55 | // Remove initial "&" from the reduce 56 | return output.substr(1); 57 | }; 58 | 59 | /** 60 | * Build the RequestInit object based on the options passed into the initial request 61 | * @param options The Http plugin options 62 | * @param extra Any extra RequestInit values 63 | */ 64 | export const buildRequestInit = ( 65 | options: HttpOptions, 66 | extra: RequestInit = {}, 67 | ): RequestInit => { 68 | const output: RequestInit = { 69 | method: options.method || 'GET', 70 | headers: options.headers, 71 | ...extra, 72 | }; 73 | 74 | // Get the content-type 75 | const headers = normalizeHttpHeaders(options.headers); 76 | const type = headers['content-type'] || ''; 77 | 78 | // If body is already a string, then pass it through as-is. 79 | if (typeof options.data === 'string') { 80 | output.body = options.data; 81 | } 82 | // Build request initializers based off of content-type 83 | else if (type.includes('application/x-www-form-urlencoded')) { 84 | const params = new URLSearchParams(); 85 | for (const [key, value] of Object.entries(options.data || {})) { 86 | params.set(key, value as any); 87 | } 88 | output.body = params.toString(); 89 | } else if (type.includes('multipart/form-data')) { 90 | const form = new FormData(); 91 | if (options.data instanceof FormData) { 92 | options.data.forEach((value, key) => { 93 | form.append(key, value); 94 | }); 95 | } else { 96 | for (let key of Object.keys(options.data)) { 97 | form.append(key, options.data[key]); 98 | } 99 | } 100 | output.body = form; 101 | const headers = new Headers(output.headers); 102 | headers.delete('content-type'); // content-type will be set by `window.fetch` to includy boundary 103 | output.headers = headers; 104 | } else if ( 105 | type.includes('application/json') || 106 | typeof options.data === 'object' 107 | ) { 108 | output.body = JSON.stringify(options.data); 109 | } 110 | 111 | return output; 112 | }; 113 | 114 | /** 115 | * Perform an Http request given a set of options 116 | * @param options Options to build the HTTP request 117 | */ 118 | export const request = async (options: HttpOptions): Promise => { 119 | const requestInit = buildRequestInit(options, options.webFetchExtra); 120 | const urlParams = buildUrlParams( 121 | options.params, 122 | options.shouldEncodeUrlParams, 123 | ); 124 | const url = urlParams ? `${options.url}?${urlParams}` : options.url; 125 | 126 | const response = await fetch(url, requestInit); 127 | const contentType = response.headers.get('content-type') || ''; 128 | 129 | // Default to 'text' responseType so no parsing happens 130 | let { responseType = 'text' } = response.ok ? options : {}; 131 | 132 | // If the response content-type is json, force the response to be json 133 | if (contentType.includes('application/json')) { 134 | responseType = 'json'; 135 | } 136 | 137 | let data: any; 138 | switch (responseType) { 139 | case 'arraybuffer': 140 | case 'blob': 141 | const blob = await response.blob(); 142 | data = await readBlobAsBase64(blob); 143 | break; 144 | case 'json': 145 | data = await response.json(); 146 | break; 147 | case 'document': 148 | case 'text': 149 | default: 150 | data = await response.text(); 151 | } 152 | 153 | // Convert fetch headers to Capacitor HttpHeaders 154 | const headers = {} as HttpHeaders; 155 | response.headers.forEach((value: string, key: string) => { 156 | headers[key] = value; 157 | }); 158 | 159 | return { 160 | data, 161 | headers, 162 | status: response.status, 163 | url: response.url, 164 | }; 165 | }; 166 | 167 | /** 168 | * Perform an Http GET request given a set of options 169 | * @param options Options to build the HTTP request 170 | */ 171 | export const get = async (options: HttpOptions): Promise => 172 | request({ ...options, method: 'GET' }); 173 | 174 | /** 175 | * Perform an Http POST request given a set of options 176 | * @param options Options to build the HTTP request 177 | */ 178 | export const post = async (options: HttpOptions): Promise => 179 | request({ ...options, method: 'POST' }); 180 | 181 | /** 182 | * Perform an Http PUT request given a set of options 183 | * @param options Options to build the HTTP request 184 | */ 185 | export const put = async (options: HttpOptions): Promise => 186 | request({ ...options, method: 'PUT' }); 187 | 188 | /** 189 | * Perform an Http PATCH request given a set of options 190 | * @param options Options to build the HTTP request 191 | */ 192 | export const patch = async (options: HttpOptions): Promise => 193 | request({ ...options, method: 'PATCH' }); 194 | 195 | /** 196 | * Perform an Http DELETE request given a set of options 197 | * @param options Options to build the HTTP request 198 | */ 199 | export const del = async (options: HttpOptions): Promise => 200 | request({ ...options, method: 'DELETE' }); 201 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Read in a Blob value and return it as a base64 string 3 | * @param blob The blob value to convert to a base64 string 4 | */ 5 | export const readBlobAsBase64 = async (blob: Blob): Promise => 6 | new Promise((resolve, reject) => { 7 | const reader = new FileReader(); 8 | reader.onload = () => { 9 | const base64String = reader.result as string; 10 | const base64StringWithoutTags = base64String.substr( 11 | base64String.indexOf(',') + 1, 12 | ); // remove prefix "data:application/pdf;base64," 13 | resolve(base64StringWithoutTags); 14 | }; 15 | reader.onerror = (error: any) => reject(error); 16 | reader.readAsDataURL(blob); 17 | }); 18 | 19 | /** 20 | * Safely web encode a string value (inspired by js-cookie) 21 | * @param str The string value to encode 22 | */ 23 | export const encode = (str: string) => 24 | encodeURIComponent(str) 25 | .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent) 26 | .replace(/[()]/g, escape); 27 | 28 | /** 29 | * Safely web decode a string value (inspired by js-cookie) 30 | * @param str The string value to decode 31 | */ 32 | export const decode = (str: string): string => 33 | str.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent); 34 | -------------------------------------------------------------------------------- /src/web.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | HttpPlugin, 3 | HttpOptions, 4 | HttpResponse, 5 | HttpDownloadFileOptions, 6 | HttpDownloadFileResult, 7 | HttpUploadFileOptions, 8 | HttpUploadFileResult, 9 | HttpCookie, 10 | HttpCookieMap, 11 | HttpGetCookiesResult, 12 | HttpSetCookieOptions, 13 | HttpMultiCookiesOptions, 14 | HttpSingleCookieOptions, 15 | ProgressStatus, 16 | } from './definitions'; 17 | import { WebPlugin } from '@capacitor/core'; 18 | import * as Cookie from './cookie'; 19 | import * as Request from './request'; 20 | 21 | export class HttpWeb extends WebPlugin implements HttpPlugin { 22 | constructor() { 23 | super(); 24 | } 25 | 26 | /** 27 | * Perform an Http request given a set of options 28 | * @param options Options to build the HTTP request 29 | */ 30 | public request = async (options: HttpOptions): Promise => 31 | Request.request(options); 32 | 33 | /** 34 | * Perform an Http GET request given a set of options 35 | * @param options Options to build the HTTP request 36 | */ 37 | public get = async (options: HttpOptions): Promise => 38 | Request.get(options); 39 | 40 | /** 41 | * Perform an Http POST request given a set of options 42 | * @param options Options to build the HTTP request 43 | */ 44 | public post = async (options: HttpOptions): Promise => 45 | Request.post(options); 46 | 47 | /** 48 | * Perform an Http PUT request given a set of options 49 | * @param options Options to build the HTTP request 50 | */ 51 | public put = async (options: HttpOptions): Promise => 52 | Request.put(options); 53 | 54 | /** 55 | * Perform an Http PATCH request given a set of options 56 | * @param options Options to build the HTTP request 57 | */ 58 | public patch = async (options: HttpOptions): Promise => 59 | Request.patch(options); 60 | 61 | /** 62 | * Perform an Http DELETE request given a set of options 63 | * @param options Options to build the HTTP request 64 | */ 65 | public del = async (options: HttpOptions): Promise => 66 | Request.del(options); 67 | 68 | /** 69 | * Gets all HttpCookies as a Map 70 | */ 71 | public getCookiesMap = async ( 72 | // @ts-ignore 73 | options: HttpMultiCookiesOptions, 74 | ): Promise => { 75 | const cookies = Cookie.getCookies(); 76 | const output: HttpCookieMap = {}; 77 | 78 | for (const cookie of cookies) { 79 | output[cookie.key] = cookie.value; 80 | } 81 | 82 | return output; 83 | }; 84 | 85 | /** 86 | * Get all HttpCookies as an object with the values as an HttpCookie[] 87 | */ 88 | public getCookies = async ( 89 | options: HttpMultiCookiesOptions, 90 | ): Promise => { 91 | // @ts-ignore 92 | const { url } = options; 93 | 94 | const cookies = Cookie.getCookies(); 95 | return { cookies }; 96 | }; 97 | 98 | /** 99 | * Set a cookie 100 | * @param key The key to set 101 | * @param value The value to set 102 | * @param options Optional additional parameters 103 | */ 104 | public setCookie = async (options: HttpSetCookieOptions): Promise => { 105 | const { key, value, expires = '', path = '' } = options; 106 | Cookie.setCookie(key, value, { expires, path }); 107 | }; 108 | 109 | /** 110 | * Gets all cookie values unless a key is specified, then return only that value 111 | * @param key The key of the cookie value to get 112 | */ 113 | public getCookie = async ( 114 | options: HttpSingleCookieOptions, 115 | ): Promise => Cookie.getCookie(options.key); 116 | 117 | /** 118 | * Deletes a cookie given a key 119 | * @param key The key of the cookie to delete 120 | */ 121 | public deleteCookie = async ( 122 | options: HttpSingleCookieOptions, 123 | ): Promise => Cookie.deleteCookie(options.key); 124 | 125 | /** 126 | * Clears out cookies by setting them to expire immediately 127 | */ 128 | public clearCookies = async ( 129 | // @ts-ignore 130 | options: HttpMultiCookiesOptions, 131 | ): Promise => Cookie.clearCookies(); 132 | 133 | /** 134 | * Clears out cookies by setting them to expire immediately 135 | */ 136 | public clearAllCookies = async (): Promise => Cookie.clearCookies(); 137 | 138 | /** 139 | * Uploads a file through a POST request 140 | * @param options TODO 141 | */ 142 | public uploadFile = async ( 143 | options: HttpUploadFileOptions, 144 | ): Promise => { 145 | const formData = new FormData(); 146 | formData.append(options.name, options.blob || 'undefined'); 147 | const fetchOptions = { 148 | ...options, 149 | body: formData, 150 | method: 'POST', 151 | }; 152 | 153 | return this.post(fetchOptions); 154 | }; 155 | 156 | /** 157 | * Downloads a file 158 | * @param options TODO 159 | */ 160 | public downloadFile = async ( 161 | options: HttpDownloadFileOptions, 162 | ): Promise => { 163 | const requestInit = Request.buildRequestInit( 164 | options, 165 | options.webFetchExtra, 166 | ); 167 | const response = await fetch(options.url, requestInit); 168 | let blob: Blob; 169 | 170 | if (!options?.progress) blob = await response.blob(); 171 | else if (!response?.body) blob = new Blob(); 172 | else { 173 | const reader = response.body.getReader(); 174 | 175 | let bytes: number = 0; 176 | let chunks: Array = []; 177 | 178 | const contentType: string | null = response.headers.get('content-type'); 179 | const contentLength: number = parseInt( 180 | response.headers.get('content-length') || '0', 181 | 10, 182 | ); 183 | 184 | while (true) { 185 | const { done, value } = await reader.read(); 186 | 187 | if (done) break; 188 | 189 | chunks.push(value); 190 | bytes += value?.length || 0; 191 | 192 | const status: ProgressStatus = { 193 | type: 'DOWNLOAD', 194 | url: options.url, 195 | bytes, 196 | contentLength, 197 | }; 198 | 199 | this.notifyListeners('progress', status); 200 | } 201 | 202 | let allChunks = new Uint8Array(bytes); 203 | let position: number = 0; 204 | for (const chunk of chunks) { 205 | if (typeof chunk === 'undefined') continue; 206 | 207 | allChunks.set(chunk, position); 208 | position += chunk.length; 209 | } 210 | 211 | blob = new Blob([allChunks.buffer], { type: contentType || undefined }); 212 | } 213 | 214 | return { 215 | blob, 216 | }; 217 | }; 218 | } 219 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowUnreachableCode": false, 4 | "declaration": true, 5 | "esModuleInterop": true, 6 | "lib": ["dom", "es2017"], 7 | "module": "esnext", 8 | "moduleResolution": "node", 9 | "noFallthroughCasesInSwitch": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "outDir": "dist/esm", 13 | "pretty": true, 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017" 17 | }, 18 | "files": ["src/index.ts"] 19 | } 20 | --------------------------------------------------------------------------------