├── .aegir.js ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ └── open_an_issue.md ├── config.yml ├── dependabot.yml └── workflows │ ├── generated-pr.yml │ ├── js-test-and-release.yml │ ├── stale.yml │ └── test-android.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── package.json ├── rn-test.config.js ├── rn-test.require.js ├── src ├── env.js ├── fetch.browser.js ├── fetch.js ├── fetch.rn.js ├── files │ ├── glob-source.js │ └── url-source.js ├── http.js ├── http │ ├── error.js │ ├── fetch.browser.js │ ├── fetch.js │ ├── fetch.node.js │ └── fetch.rn.js ├── index.js ├── path-join.browser.js ├── path-join.js ├── supports.js ├── temp-dir.browser.js ├── temp-dir.js └── types.d.ts ├── test ├── env.spec.js ├── files │ ├── glob-source.spec.js │ └── url-source.spec.js ├── fixtures │ ├── another-dir │ │ ├── another-nested-dir │ │ │ └── other.txt │ │ └── hello.txt │ ├── dir │ │ ├── .hidden.txt │ │ ├── file-1.txt │ │ ├── file-2.js │ │ ├── file-3.css │ │ └── nested-dir │ │ │ └── other.txt │ └── file-0.html ├── http.spec.js └── supports.spec.js └── tsconfig.json /.aegir.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EchoServer = require('aegir/utils/echo-server') 4 | const { format } =require('iso-url') 5 | 6 | /** @type {import('aegir').Options["build"]["config"]} */ 7 | const esbuild = { 8 | plugins: [ 9 | { 10 | name: 'node built ins', 11 | setup (build) { 12 | build.onResolve({ filter: /^stream$/ }, () => { 13 | return { path: require.resolve('readable-stream') } 14 | }) 15 | } 16 | } 17 | ] 18 | } 19 | 20 | /** @type {import('aegir').PartialOptions} */ 21 | module.exports = { 22 | build: { 23 | config: esbuild 24 | }, 25 | test: { 26 | browser: { 27 | config: { 28 | buildConfig: esbuild 29 | } 30 | }, 31 | async before (options) { 32 | let echoServer = new EchoServer() 33 | await echoServer.start() 34 | const { address, port } = echoServer.server.address() 35 | let hostname = address 36 | if(options.runner === 'react-native-android') { 37 | hostname = '10.0.2.2' 38 | } 39 | return { 40 | echoServer, 41 | env: { ECHO_SERVER : format({ protocol: 'http:', hostname, port })} 42 | } 43 | }, 44 | async after (options, before) { 45 | await before.echoServer.stop() 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Getting Help on IPFS 4 | url: https://ipfs.io/help 5 | about: All information about how and where to get help on IPFS. 6 | - name: IPFS Official Forum 7 | url: https://discuss.ipfs.io 8 | about: Please post general questions, support requests, and discussions here. 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/open_an_issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Open an issue 3 | about: Only for actionable issues relevant to this repository. 4 | title: '' 5 | labels: need/triage 6 | assignees: '' 7 | 8 | --- 9 | 20 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | # Configuration for welcome - https://github.com/behaviorbot/welcome 2 | 3 | # Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome 4 | # Comment to be posted to on first time issues 5 | newIssueWelcomeComment: > 6 | Thank you for submitting your first issue to this repository! A maintainer 7 | will be here shortly to triage and review. 8 | 9 | In the meantime, please double-check that you have provided all the 10 | necessary information to make this process easy! Any information that can 11 | help save additional round trips is useful! We currently aim to give 12 | initial feedback within **two business days**. If this does not happen, feel 13 | free to leave a comment. 14 | 15 | Please keep an eye on how this issue will be labeled, as labels give an 16 | overview of priorities, assignments and additional actions requested by the 17 | maintainers: 18 | 19 | - "Priority" labels will show how urgent this is for the team. 20 | - "Status" labels will show if this is ready to be worked on, blocked, or in progress. 21 | - "Need" labels will indicate if additional input or analysis is required. 22 | 23 | Finally, remember to use https://discuss.ipfs.io if you just need general 24 | support. 25 | 26 | # Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome 27 | # Comment to be posted to on PRs from first time contributors in your repository 28 | newPRWelcomeComment: > 29 | Thank you for submitting this PR! 30 | 31 | A maintainer will be here shortly to review it. 32 | 33 | We are super grateful, but we are also overloaded! Help us by making sure 34 | that: 35 | 36 | * The context for this PR is clear, with relevant discussion, decisions 37 | and stakeholders linked/mentioned. 38 | 39 | * Your contribution itself is clear (code comments, self-review for the 40 | rest) and in its best form. Follow the [code contribution 41 | guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md#code-contribution-guidelines) 42 | if they apply. 43 | 44 | Getting other community members to do a review would be great help too on 45 | complex PRs (you can ask in the chats/forums). If you are unsure about 46 | something, just leave us a comment. 47 | 48 | Next steps: 49 | 50 | * A maintainer will triage and assign priority to this PR, commenting on 51 | any missing things and potentially assigning a reviewer for high 52 | priority items. 53 | 54 | * The PR gets reviews, discussed and approvals as needed. 55 | 56 | * The PR is merged by maintainers when it has been approved and comments addressed. 57 | 58 | We currently aim to provide initial feedback/triaging within **two business 59 | days**. Please keep an eye on any labelling actions, as these will indicate 60 | priorities and status of your contribution. 61 | 62 | We are very grateful for your contribution! 63 | 64 | 65 | # Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge 66 | # Comment to be posted to on pull requests merged by a first time user 67 | # Currently disabled 68 | #firstPRMergeComment: "" 69 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | commit-message: 10 | prefix: "deps" 11 | prefix-development: "deps(dev)" 12 | -------------------------------------------------------------------------------- /.github/workflows/generated-pr.yml: -------------------------------------------------------------------------------- 1 | name: Close Generated PRs 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | stale: 14 | uses: ipdxco/unified-github-workflows/.github/workflows/reusable-generated-pr.yml@v1 15 | -------------------------------------------------------------------------------- /.github/workflows/js-test-and-release.yml: -------------------------------------------------------------------------------- 1 | name: test & maybe release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | permissions: 11 | contents: write 12 | id-token: write 13 | packages: write 14 | pull-requests: write 15 | 16 | concurrency: 17 | group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | js-test-and-release: 22 | uses: ipdxco/unified-github-workflows/.github/workflows/js-test-and-release.yml@v1.0 23 | secrets: 24 | DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} 25 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 26 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 27 | UCI_GITHUB_TOKEN: ${{ secrets.UCI_GITHUB_TOKEN }} 28 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 29 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close Stale Issues 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | stale: 14 | uses: ipdxco/unified-github-workflows/.github/workflows/reusable-stale-issue.yml@v1 15 | -------------------------------------------------------------------------------- /.github/workflows/test-android.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | env: 3 | CI: true 4 | FORCE_COLOR: 1 5 | on: 6 | push: 7 | branches: 8 | - master 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | test-react-native-android: 15 | runs-on: macos-latest 16 | needs: check 17 | continue-on-error: true 18 | steps: 19 | - uses: actions/checkout@v2 20 | - run: npm install 21 | - uses: reactivecircus/android-emulator-runner@v2 22 | with: 23 | api-level: 29 24 | target: default 25 | arch: x86_64 26 | profile: pixel 27 | avd-name: aegir-android-29 28 | script: | 29 | npx aegir test -t react-native-android 30 | # test-react-native-ios: 31 | # runs-on: macos-latest 32 | # steps: 33 | # - uses: actions/checkout@v2 34 | # - run: npm install 35 | # - name: Create and run iOS simulator 36 | # run: | 37 | # SIMULATOR_RUNTIME=$(echo "iOS 14.4" | sed 's/[ \.]/-/g') 38 | # SIMULATOR_ID=$(xcrun simctl create "iPhone 11" com.apple.CoreSimulator.SimDeviceType.iPhone-11 com.apple.CoreSimulator.SimRuntime.$SIMULATOR_RUNTIME) 39 | # echo "IOS_SIMULATOR=$SIMULATOR_ID" >> $GITHUB_ENV 40 | # xcrun simctl boot $SIMULATOR_ID & 41 | # - run: npx rn-test --platform ios --simulator 'iPhone 11 (14.4)' --rn 0.62.0 'test/**/*.spec.js' 42 | # - name: Shutdown iOS simulator 43 | # run: | 44 | # xcrun simctl shutdown $IOS_SIMULATOR 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | yarn.lock 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 29 | node_modules 30 | 31 | dist 32 | docs 33 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### [9.0.14](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.13...v9.0.14) (2023-01-13) 2 | 3 | 4 | ### Dependencies 5 | 6 | * update node-fetch to 2.6.8 ([#244](https://github.com/ipfs/js-ipfs-utils/issues/244)) ([ebd0f45](https://github.com/ipfs/js-ipfs-utils/commit/ebd0f45d0fc6e9b2a2ac0658cbce1a8aee442f9e)) 7 | 8 | ### [9.0.13](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.12...v9.0.13) (2023-01-11) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * restore old stream conversion ([d7495e4](https://github.com/ipfs/js-ipfs-utils/commit/d7495e4a66714f9e9df0246af7a50f2266b97287)) 14 | 15 | ### [9.0.12](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.11...v9.0.12) (2023-01-11) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * downgrade nanoid ([db87fb5](https://github.com/ipfs/js-ipfs-utils/commit/db87fb51cf791f7b59e5405505fa5430a93a73e0)) 21 | 22 | ### [9.0.11](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.10...v9.0.11) (2023-01-11) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * add missing it-all dependency ([6679755](https://github.com/ipfs/js-ipfs-utils/commit/66797553c0783b1b9683acf1d6877fe6b72a8ecd)) 28 | 29 | ### [9.0.10](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.9...v9.0.10) (2023-01-11) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * specify duplex option ([#239](https://github.com/ipfs/js-ipfs-utils/issues/239)) ([a1168f9](https://github.com/ipfs/js-ipfs-utils/commit/a1168f9ad12c45f23f5f5e39015377e05fc7c1f7)) 35 | 36 | 37 | ### Trivial Changes 38 | 39 | * use semantic-release for releases ([3267e99](https://github.com/ipfs/js-ipfs-utils/commit/3267e99cd0640210a511a4f3f9c9cc0408f21f45)) 40 | 41 | ### [9.0.9](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.8...v9.0.9) (2022-11-18) 42 | 43 | 44 | ### Bug Fixes 45 | 46 | * only export one thing from glob source ([#223](https://github.com/ipfs/js-ipfs-utils/issues/223)) ([fbbf8f0](https://github.com/ipfs/js-ipfs-utils/commit/fbbf8f06c4660d8280c4d3959ed91a74de3c4475)) 47 | 48 | ### [9.0.8](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.7...v9.0.8) (2022-11-18) 49 | 50 | 51 | ### Bug Fixes 52 | 53 | * fix unixfs import ([#222](https://github.com/ipfs/js-ipfs-utils/issues/222)) ([1dc939f](https://github.com/ipfs/js-ipfs-utils/commit/1dc939fc44391542d0594497c17e22eb6f74d2f3)) 54 | 55 | 56 | ### Trivial Changes 57 | 58 | * Update .github/workflows/stale.yml [skip ci] ([9229386](https://github.com/ipfs/js-ipfs-utils/commit/922938670da3de75e75f2122146a5e38802ea5ff)) 59 | 60 | ### [9.0.7](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.6...v9.0.7) (2022-06-23) 61 | 62 | 63 | ### Bug Fixes 64 | 65 | * support vite ([#193](https://github.com/ipfs/js-ipfs-utils/issues/193)) ([c4c2ab1](https://github.com/ipfs/js-ipfs-utils/commit/c4c2ab1ad283b9ca89cc9bf8d03590d5abb6cb5c)) 66 | 67 | ### [9.0.6](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.5...v9.0.6) (2022-04-19) 68 | 69 | 70 | ### Bug Fixes 71 | 72 | * swap [@yields](https://github.com/yields) for [@returns](https://github.com/returns) ([#178](https://github.com/ipfs/js-ipfs-utils/issues/178)) ([13e2c9b](https://github.com/ipfs/js-ipfs-utils/commit/13e2c9ba90f8f34efc13fb2c3ea91affd606c5a3)) 73 | 74 | ### [9.0.5](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.4...v9.0.5) (2022-03-01) 75 | 76 | 77 | ### Bug Fixes 78 | 79 | * detect process before use ([#172](https://github.com/ipfs/js-ipfs-utils/issues/172)) ([7aee4aa](https://github.com/ipfs/js-ipfs-utils/commit/7aee4aa6bb269a938565930a80dc81be5d7a1aef)) 80 | 81 | ### [9.0.4](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.3...v9.0.4) (2022-01-14) 82 | 83 | 84 | ### Trivial Changes 85 | 86 | * add semantic release config ([#167](https://github.com/ipfs/js-ipfs-utils/issues/167)) ([748b6a9](https://github.com/ipfs/js-ipfs-utils/commit/748b6a9a2df01d810350495d00f1acf44a66e366)) 87 | * fix release branch ([#168](https://github.com/ipfs/js-ipfs-utils/issues/168)) ([d91c9f6](https://github.com/ipfs/js-ipfs-utils/commit/d91c9f6b5004690c93fc93bad2724067f4e6bfd9)) 88 | 89 | ## [9.0.2](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.1...v9.0.2) (2021-09-28) 90 | 91 | 92 | 93 | ## [9.0.1](https://github.com/ipfs/js-ipfs-utils/compare/v9.0.0...v9.0.1) (2021-09-23) 94 | 95 | 96 | 97 | # [9.0.0](https://github.com/ipfs/js-ipfs-utils/compare/v8.1.6...v9.0.0) (2021-09-23) 98 | 99 | 100 | ### Features 101 | 102 | * support glob patterns ([#151](https://github.com/ipfs/js-ipfs-utils/issues/151)) ([d626de1](https://github.com/ipfs/js-ipfs-utils/commit/d626de18baabc91bafafaa8ac5a08ffdf83a3796)) 103 | 104 | 105 | ### BREAKING CHANGES 106 | 107 | * the globSource call signature has changed and no longer supports the recursive or ignore options 108 | 109 | 110 | 111 | ## [8.1.6](https://github.com/ipfs/js-ipfs-utils/compare/v8.1.5...v8.1.6) (2021-08-27) 112 | 113 | 114 | ### Bug Fixes 115 | 116 | * change dependency override ([#136](https://github.com/ipfs/js-ipfs-utils/issues/136)) ([7ad2f73](https://github.com/ipfs/js-ipfs-utils/commit/7ad2f73a1e88005d8de6631fbab2693d93d61595)) 117 | 118 | 119 | 120 | ## [8.1.5](https://github.com/ipfs/js-ipfs-utils/compare/v8.1.4...v8.1.5) (2021-08-19) 121 | 122 | 123 | 124 | ## [8.1.4](https://github.com/ipfs/js-ipfs-utils/compare/v8.1.3...v8.1.4) (2021-07-09) 125 | 126 | 127 | 128 | ## [8.1.3](https://github.com/ipfs/js-ipfs-utils/compare/v8.1.2...v8.1.3) (2021-06-18) 129 | 130 | 131 | ### Bug Fixes 132 | 133 | * request body type ([#129](https://github.com/ipfs/js-ipfs-utils/issues/129)) ([1e8261a](https://github.com/ipfs/js-ipfs-utils/commit/1e8261a001da5e41a66bb14328f031f5f99f9d6b)) 134 | 135 | 136 | 137 | ## [8.1.2](https://github.com/ipfs/js-ipfs-utils/compare/v8.1.1...v8.1.2) (2021-06-01) 138 | 139 | 140 | 141 | ## [8.1.1](https://github.com/ipfs/js-ipfs-utils/compare/v8.1.0...v8.1.1) (2021-05-28) 142 | 143 | 144 | ### Bug Fixes 145 | 146 | * use node-fetch fork with fix for stream closed prematurely bug ([#127](https://github.com/ipfs/js-ipfs-utils/issues/127)) ([4ad105a](https://github.com/ipfs/js-ipfs-utils/commit/4ad105a14300fe59d3b93c4522a1a67efb3c288f)) 147 | 148 | 149 | 150 | # [8.1.0](https://github.com/ipfs/js-ipfs-utils/compare/v8.0.0...v8.1.0) (2021-05-21) 151 | 152 | 153 | ### Features 154 | 155 | * add React Native android support ([#91](https://github.com/ipfs/js-ipfs-utils/issues/91)) ([b2b02a5](https://github.com/ipfs/js-ipfs-utils/commit/b2b02a5722f516b24813a54b3184c888935d31da)) 156 | 157 | 158 | 159 | # [8.0.0](https://github.com/ipfs/js-ipfs-utils/compare/v7.0.0...v8.0.0) (2021-05-10) 160 | 161 | 162 | ### chore 163 | 164 | * remove text encoder ([#116](https://github.com/ipfs/js-ipfs-utils/issues/116)) ([686c745](https://github.com/ipfs/js-ipfs-utils/commit/686c745d2c9c00134d72da309825502edb3ea977)) 165 | 166 | 167 | ### BREAKING CHANGES 168 | 169 | * text encoder/decoder files have been removed 170 | 171 | 172 | 173 | # [7.0.0](https://github.com/ipfs/js-ipfs-utils/compare/v6.0.8...v7.0.0) (2021-05-04) 174 | 175 | 176 | ### Bug Fixes 177 | 178 | * remove retyping of modules with types ([#125](https://github.com/ipfs/js-ipfs-utils/issues/125)) ([f6406c5](https://github.com/ipfs/js-ipfs-utils/commit/f6406c5af4abb83fa11113202f785e8a7c9f6941)), closes [#109](https://github.com/ipfs/js-ipfs-utils/issues/109) 179 | 180 | 181 | ### BREAKING CHANGES 182 | 183 | * `ResponseWithURL` type is not exported any more as it uses a private name and causes an inconsistency between node and the browser 184 | 185 | 186 | 187 | ## [6.0.8](https://github.com/ipfs/js-ipfs-utils/compare/v6.0.7...v6.0.8) (2021-04-30) 188 | 189 | 190 | ### Bug Fixes 191 | 192 | * exclude glob-source.js in browser build ([#121](https://github.com/ipfs/js-ipfs-utils/issues/121)) ([c4071d9](https://github.com/ipfs/js-ipfs-utils/commit/c4071d902d65c7c76e934f43e1f80a209113eac5)) 193 | 194 | 195 | 196 | ## [6.0.7](https://github.com/ipfs/js-ipfs-utils/compare/v6.0.6...v6.0.7) (2021-04-14) 197 | 198 | 199 | 200 | ## [6.0.6](https://github.com/ipfs/js-ipfs-utils/compare/v6.0.5...v6.0.6) (2021-04-06) 201 | 202 | 203 | 204 | ## [6.0.5](https://github.com/ipfs/js-ipfs-utils/compare/v6.0.4...v6.0.5) (2021-04-01) 205 | 206 | 207 | ### Bug Fixes 208 | 209 | * do not use electron-fetch types ([#113](https://github.com/ipfs/js-ipfs-utils/issues/113)) ([658a725](https://github.com/ipfs/js-ipfs-utils/commit/658a725148715b3c6e3653e01d8ed6dab53d8401)) 210 | 211 | 212 | 213 | ## [6.0.4](https://github.com/ipfs/js-ipfs-utils/compare/v6.0.3...v6.0.4) (2021-03-22) 214 | 215 | 216 | ### Bug Fixes 217 | 218 | * allow all supported unixfs time types ([#111](https://github.com/ipfs/js-ipfs-utils/issues/111)) ([72e0097](https://github.com/ipfs/js-ipfs-utils/commit/72e0097accb58eb01480bb47085edfe25ea7bf9a)) 219 | 220 | 221 | 222 | ## [6.0.3](https://github.com/ipfs/js-ipfs-utils/compare/v6.0.2...v6.0.3) (2021-03-15) 223 | 224 | 225 | 226 | ## [6.0.2](https://github.com/ipfs/js-ipfs-utils/compare/v6.0.1...v6.0.2) (2021-03-15) 227 | 228 | 229 | 230 | ## [6.0.1](https://github.com/ipfs/js-ipfs-utils/compare/v6.0.0...v6.0.1) (2021-02-07) 231 | 232 | 233 | 234 | # [6.0.0](https://github.com/ipfs/js-ipfs-utils/compare/v5.0.1...v6.0.0) (2021-01-15) 235 | 236 | 237 | ### Features 238 | 239 | * add types, gh actions and remove unused methods ([#89](https://github.com/ipfs/js-ipfs-utils/issues/89)) ([707174c](https://github.com/ipfs/js-ipfs-utils/commit/707174c131a0651677200329583bf7dca652504a)) 240 | * make urlSource compatible with new ipfs.add ([#53](https://github.com/ipfs/js-ipfs-utils/issues/53)) ([2b3ebbe](https://github.com/ipfs/js-ipfs-utils/commit/2b3ebbe86df409c1bc89d7855bf427446748b073)), closes [ipfs/js-ipfs#3195](https://github.com/ipfs/js-ipfs/issues/3195) 241 | 242 | 243 | ### BREAKING CHANGES 244 | 245 | * removed globalThis and normalise-input, format-mode and format-mtime 246 | 247 | 248 | 249 | ## [5.0.1](https://github.com/ipfs/js-ipfs-utils/compare/v5.0.0...v5.0.1) (2020-11-25) 250 | 251 | 252 | 253 | # [5.0.0](https://github.com/ipfs/js-ipfs-utils/compare/v4.0.1...v5.0.0) (2020-11-16) 254 | 255 | 256 | ### Features 257 | 258 | * add onUploadProgress handler ([#60](https://github.com/ipfs/js-ipfs-utils/issues/60)) ([a2e88e2](https://github.com/ipfs/js-ipfs-utils/commit/a2e88e23f16bd0da57a67c02e8c875a2705bb28f)) 259 | 260 | 261 | 262 | ## [4.0.1](https://github.com/ipfs/js-ipfs-utils/compare/v4.0.0...v4.0.1) (2020-11-09) 263 | 264 | 265 | ### Bug Fixes 266 | 267 | * fetch takes a string, not a URL ([#75](https://github.com/ipfs/js-ipfs-utils/issues/75)) ([15576b2](https://github.com/ipfs/js-ipfs-utils/commit/15576b28495caee7410fda51d8cb5aa2c8e6d106)), closes [#74](https://github.com/ipfs/js-ipfs-utils/issues/74) 268 | 269 | 270 | 271 | 272 | # [4.0.0](https://github.com/ipfs/js-ipfs-utils/compare/v3.0.0...v4.0.0) (2020-10-10) 273 | 274 | 275 | ### Bug Fixes 276 | 277 | * use native fetch if available ([#62](https://github.com/ipfs/js-ipfs-utils/issues/62)) ([9b0ff2f](https://github.com/ipfs/js-ipfs-utils/commit/9b0ff2f)) 278 | 279 | 280 | 281 | 282 | # [3.0.0](https://github.com/ipfs/js-ipfs-utils/compare/v2.4.0...v3.0.0) (2020-08-18) 283 | 284 | 285 | ### Bug Fixes 286 | 287 | * revert "feat: http upload/download progress handlers" ([#58](https://github.com/ipfs/js-ipfs-utils/issues/58)) ([1bbe957](https://github.com/ipfs/js-ipfs-utils/commit/1bbe957)) 288 | 289 | 290 | 291 | 292 | # [2.4.0](https://github.com/ipfs/js-ipfs-utils/compare/v2.3.1...v2.4.0) (2020-08-12) 293 | 294 | 295 | ### Features 296 | 297 | * detect support for WebRTC data channels ([#56](https://github.com/ipfs/js-ipfs-utils/issues/56)) ([78ad2d2](https://github.com/ipfs/js-ipfs-utils/commit/78ad2d2)), closes [#50](https://github.com/ipfs/js-ipfs-utils/issues/50) 298 | * http upload/download progress handlers ([#54](https://github.com/ipfs/js-ipfs-utils/issues/54)) ([d30be96](https://github.com/ipfs/js-ipfs-utils/commit/d30be96)), closes [#52](https://github.com/ipfs/js-ipfs-utils/issues/52) [#52](https://github.com/ipfs/js-ipfs-utils/issues/52) 299 | 300 | 301 | 302 | 303 | ## [2.3.1](https://github.com/ipfs/js-ipfs-utils/compare/v2.3.0...v2.3.1) (2020-06-18) 304 | 305 | 306 | ### Features 307 | 308 | * add webrtc to supports ([#38](https://github.com/ipfs/js-ipfs-utils/issues/38)) ([8cca85d](https://github.com/ipfs/js-ipfs-utils/commit/8cca85d)) 309 | 310 | 311 | 312 | 313 | # [2.3.0](https://github.com/ipfs/js-ipfs-utils/compare/v2.2.2...v2.3.0) (2020-06-10) 314 | 315 | 316 | ### Bug Fixes 317 | 318 | * text encoder / decoder exports ([c4792e1](https://github.com/ipfs/js-ipfs-utils/commit/c4792e1)) 319 | 320 | 321 | 322 | 323 | ## [2.2.2](https://github.com/ipfs/js-ipfs-utils/compare/v2.2.1...v2.2.2) (2020-05-05) 324 | 325 | 326 | ### Bug Fixes 327 | 328 | * fix headers and abort signals ([#41](https://github.com/ipfs/js-ipfs-utils/issues/41)) ([ad977a9](https://github.com/ipfs/js-ipfs-utils/commit/ad977a9)) 329 | * **ci:** add empty commit to fix lint checks on master ([ad2fdc4](https://github.com/ipfs/js-ipfs-utils/commit/ad2fdc4)) 330 | 331 | 332 | 333 | 334 | ## [2.2.1](https://github.com/ipfs/js-ipfs-utils/compare/v2.2.0...v2.2.1) (2020-05-01) 335 | 336 | 337 | ### Bug Fixes 338 | 339 | * make timeouts stricter ([#40](https://github.com/ipfs/js-ipfs-utils/issues/40)) ([bbcd1eb](https://github.com/ipfs/js-ipfs-utils/commit/bbcd1eb)) 340 | 341 | 342 | ### Features 343 | 344 | * pass in Options Object to http Constructor ([#37](https://github.com/ipfs/js-ipfs-utils/issues/37)) ([727f28d](https://github.com/ipfs/js-ipfs-utils/commit/727f28d)) 345 | 346 | 347 | 348 | 349 | # [2.2.0](https://github.com/ipfs/js-ipfs-utils/compare/v2.1.0...v2.2.0) (2020-04-14) 350 | 351 | 352 | ### Features 353 | 354 | * add json option to http ([#34](https://github.com/ipfs/js-ipfs-utils/issues/34)) ([070a456](https://github.com/ipfs/js-ipfs-utils/commit/070a456)) 355 | 356 | 357 | 358 | 359 | # [2.1.0](https://github.com/ipfs/js-ipfs-utils/compare/v2.0.0...v2.1.0) (2020-04-13) 360 | 361 | 362 | ### Features 363 | 364 | * add iterator method to response ([#36](https://github.com/ipfs/js-ipfs-utils/issues/36)) ([8f7c96c](https://github.com/ipfs/js-ipfs-utils/commit/8f7c96c)) 365 | 366 | 367 | 368 | 369 | # [2.0.0](https://github.com/ipfs/js-ipfs-utils/compare/v1.2.4...v2.0.0) (2020-04-09) 370 | 371 | 372 | ### Bug Fixes 373 | 374 | * simplify http client ([#35](https://github.com/ipfs/js-ipfs-utils/issues/35)) ([05c4c5d](https://github.com/ipfs/js-ipfs-utils/commit/05c4c5d)) 375 | 376 | 377 | ### BREAKING CHANGES 378 | 379 | * - The `.ndjson`, `.stream` and `.iterator` methods have been removed 380 | - An `.ndjson` async generator function has been added to the response which 381 | does the same thing the `.ndjson` instance method used to 382 | 383 | Old: 384 | 385 | ```javascript 386 | for await (const datum of http.ndjson('http://...')) { 387 | 388 | } 389 | ``` 390 | 391 | New: 392 | 393 | ```javascript 394 | const response = await http.post('http://...') 395 | 396 | for await (const datum of response.ndjson()) { 397 | 398 | } 399 | ``` 400 | 401 | 402 | 403 | 404 | ## [1.2.4](https://github.com/ipfs/js-ipfs-utils/compare/v1.2.3...v1.2.4) (2020-04-08) 405 | 406 | 407 | ### Bug Fixes 408 | 409 | * detect node stream with hasOwnProperty ([#33](https://github.com/ipfs/js-ipfs-utils/issues/33)) ([1c1d894](https://github.com/ipfs/js-ipfs-utils/commit/1c1d894)) 410 | 411 | 412 | 413 | 414 | ## [1.2.3](https://github.com/ipfs/js-ipfs-utils/compare/v1.2.2...v1.2.3) (2020-04-07) 415 | 416 | 417 | ### Bug Fixes 418 | 419 | * destroy request body when we are aborting it midway though r… ([#31](https://github.com/ipfs/js-ipfs-utils/issues/31)) ([1f7506d](https://github.com/ipfs/js-ipfs-utils/commit/1f7506d)) 420 | 421 | 422 | 423 | 424 | ## [1.2.2](https://github.com/ipfs/js-ipfs-utils/compare/v1.2.1...v1.2.2) (2020-04-06) 425 | 426 | 427 | ### Bug Fixes 428 | 429 | * avoid Identifier 'global' has already been declared ([#30](https://github.com/ipfs/js-ipfs-utils/issues/30)) ([f468065](https://github.com/ipfs/js-ipfs-utils/commit/f468065)) 430 | 431 | 432 | 433 | 434 | ## [1.2.1](https://github.com/ipfs/js-ipfs-utils/compare/v1.2.0...v1.2.1) (2020-03-31) 435 | 436 | 437 | ### Bug Fixes 438 | 439 | * fix path join swap ([b538ee4](https://github.com/ipfs/js-ipfs-utils/commit/b538ee4)) 440 | 441 | 442 | 443 | 444 | # [1.2.0](https://github.com/ipfs/js-ipfs-utils/compare/v1.1.0...v1.2.0) (2020-03-31) 445 | 446 | 447 | ### Features 448 | 449 | * add path join ([a535e42](https://github.com/ipfs/js-ipfs-utils/commit/a535e42)) 450 | 451 | 452 | 453 | 454 | # [1.1.0](https://github.com/ipfs/js-ipfs-utils/compare/v1.0.0...v1.1.0) (2020-03-26) 455 | 456 | 457 | ### Bug Fixes 458 | 459 | * fix error code param ([dd73a33](https://github.com/ipfs/js-ipfs-utils/commit/dd73a33)) 460 | 461 | 462 | ### Features 463 | 464 | * add temp dir function ([7714c66](https://github.com/ipfs/js-ipfs-utils/commit/7714c66)) 465 | 466 | 467 | 468 | 469 | # [1.0.0](https://github.com/ipfs/js-ipfs-utils/compare/v0.7.2...v1.0.0) (2020-03-20) 470 | 471 | 472 | ### Features 473 | 474 | * import code from monorepo ([#25](https://github.com/ipfs/js-ipfs-utils/issues/25)) ([dcd6f77](https://github.com/ipfs/js-ipfs-utils/commit/dcd6f77)) 475 | 476 | 477 | 478 | 479 | ## [0.7.2](https://github.com/ipfs/js-ipfs-utils/compare/v0.7.1...v0.7.2) (2020-02-10) 480 | 481 | 482 | ### Bug Fixes 483 | 484 | * number is not a valid mtime value ([#24](https://github.com/ipfs/js-ipfs-utils/issues/24)) ([bb2d841](https://github.com/ipfs/js-ipfs-utils/commit/bb2d841)), closes [/github.com/ipfs/js-ipfs-unixfs/blob/master/src/index.js#L104-L106](https://github.com//github.com/ipfs/js-ipfs-unixfs/blob/master/src/index.js/issues/L104-L106) 485 | 486 | 487 | 488 | 489 | ## [0.7.1](https://github.com/ipfs/js-ipfs-utils/compare/v0.7.0...v0.7.1) (2020-01-23) 490 | 491 | 492 | ### Bug Fixes 493 | 494 | * downgrade to ky 15 ([#22](https://github.com/ipfs/js-ipfs-utils/issues/22)) ([5dd7570](https://github.com/ipfs/js-ipfs-utils/commit/5dd7570)) 495 | 496 | 497 | 498 | 499 | # [0.7.0](https://github.com/ipfs/js-ipfs-utils/compare/v0.6.0...v0.7.0) (2020-01-23) 500 | 501 | 502 | ### Features 503 | 504 | * accept browser readable streams as input ([#21](https://github.com/ipfs/js-ipfs-utils/issues/21)) ([0902067](https://github.com/ipfs/js-ipfs-utils/commit/0902067)) 505 | 506 | 507 | 508 | 509 | # [0.6.0](https://github.com/ipfs/js-ipfs-utils/compare/v0.5.0...v0.6.0) (2020-01-09) 510 | 511 | 512 | ### Bug Fixes 513 | 514 | * dependency badge URL ([#16](https://github.com/ipfs/js-ipfs-utils/issues/16)) ([5d93881](https://github.com/ipfs/js-ipfs-utils/commit/5d93881)) 515 | * format mtime as timespec ([#20](https://github.com/ipfs/js-ipfs-utils/issues/20)) ([a68f8b1](https://github.com/ipfs/js-ipfs-utils/commit/a68f8b1)) 516 | 517 | 518 | 519 | 520 | # [0.5.0](https://github.com/ipfs/js-ipfs-utils/compare/v0.4.0...v0.5.0) (2019-12-06) 521 | 522 | 523 | ### Features 524 | 525 | * convert to async iterators ([#15](https://github.com/ipfs/js-ipfs-utils/issues/15)) ([251eff0](https://github.com/ipfs/js-ipfs-utils/commit/251eff0)) 526 | * support unixfs metadata and formatting it ([#14](https://github.com/ipfs/js-ipfs-utils/issues/14)) ([173e4bf](https://github.com/ipfs/js-ipfs-utils/commit/173e4bf)) 527 | 528 | 529 | ### BREAKING CHANGES 530 | 531 | * In order to support metadata on intermediate directories, globSource in this module will now emit directories and files where previously it only emitted files. 532 | * Support for Node.js streams and Pull Streams has been removed 533 | 534 | 535 | 536 | 537 | # [0.4.0](https://github.com/ipfs/js-ipfs-utils/compare/v0.3.0...v0.4.0) (2019-09-19) 538 | 539 | 540 | ### Features 541 | 542 | * add isElectronMain env test ([#13](https://github.com/ipfs/js-ipfs-utils/issues/13)) ([9072c90](https://github.com/ipfs/js-ipfs-utils/commit/9072c90)) 543 | 544 | 545 | 546 | 547 | # [0.3.0](https://github.com/ipfs/js-ipfs-utils/compare/v0.2.0...v0.3.0) (2019-09-15) 548 | 549 | 550 | ### Features 551 | 552 | * support old school streams ([#12](https://github.com/ipfs/js-ipfs-utils/issues/12)) ([18cfa86](https://github.com/ipfs/js-ipfs-utils/commit/18cfa86)) 553 | 554 | 555 | 556 | 557 | # [0.2.0](https://github.com/ipfs/js-ipfs-utils/compare/v0.1.0...v0.2.0) (2019-09-06) 558 | 559 | 560 | ### Features 561 | 562 | * env/isTest ([#10](https://github.com/ipfs/js-ipfs-utils/issues/10)) ([481aab1](https://github.com/ipfs/js-ipfs-utils/commit/481aab1)) 563 | 564 | 565 | 566 | 567 | # [0.1.0](https://github.com/ipfs/js-ipfs-utils/compare/v0.0.4...v0.1.0) (2019-09-04) 568 | 569 | 570 | ### Bug Fixes 571 | 572 | * write after end ([#7](https://github.com/ipfs/js-ipfs-utils/issues/7)) ([b30d7a3](https://github.com/ipfs/js-ipfs-utils/commit/b30d7a3)) 573 | 574 | 575 | ### Features 576 | 577 | * add glob-source from js-ipfs to be shared ([#9](https://github.com/ipfs/js-ipfs-utils/issues/9)) ([0a95ef8](https://github.com/ipfs/js-ipfs-utils/commit/0a95ef8)) 578 | * add normalise input function ([#5](https://github.com/ipfs/js-ipfs-utils/issues/5)) ([b22b8de](https://github.com/ipfs/js-ipfs-utils/commit/b22b8de)), closes [#8](https://github.com/ipfs/js-ipfs-utils/issues/8) 579 | 580 | 581 | 582 | 583 | ## [0.0.4](https://github.com/ipfs/js-ipfs-utils/compare/v0.0.3...v0.0.4) (2019-07-18) 584 | 585 | 586 | ### Features 587 | 588 | * add globalThis polyfill ([f0c7c42](https://github.com/ipfs/js-ipfs-utils/commit/f0c7c42)) 589 | 590 | 591 | 592 | 593 | ## [0.0.3](https://github.com/ipfs/js-ipfs-utils/compare/v0.0.2...v0.0.3) (2019-05-16) 594 | 595 | 596 | 597 | 598 | ## 0.0.2 (2019-05-16) 599 | 600 | 601 | ### Bug Fixes 602 | 603 | * use is-buffer ([bbf5baf](https://github.com/ipfs/js-ipfs-utils/commit/bbf5baf)) 604 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This project is dual licensed under MIT and Apache-2.0. 2 | 3 | MIT: https://www.opensource.org/licenses/mit 4 | Apache-2.0: https://www.apache.org/licenses/license-2.0 5 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 2 | 3 | http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 6 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ipfs-utils 2 | 3 | [![ipfs.tech](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.tech) 4 | [![Discuss](https://img.shields.io/discourse/https/discuss.ipfs.tech/posts.svg?style=flat-square)](https://discuss.ipfs.tech) 5 | [![codecov](https://img.shields.io/codecov/c/github/ipfs/js-ipfs-utils.svg?style=flat-square)](https://codecov.io/gh/ipfs/js-ipfs-utils) 6 | [![CI](https://img.shields.io/github/actions/workflow/status/ipfs/js-ipfs-utils/js-test-and-release.yml?branch=master\&style=flat-square)](https://github.com/ipfs/js-ipfs-utils/actions/workflows/js-test-and-release.yml?query=branch%3Amaster) 7 | 8 | > Package to aggregate shared logic and dependencies for the IPFS ecosystem 9 | 10 | ## Table of contents 11 | 12 | - [Install](#install) 13 | - [Browser ` 31 | ``` 32 | 33 | `ipfs-utils` aims to provide single function default export per file (with a few exceptions) scoped in 3 general categories: 34 | 35 | - General use 36 | - Data structs wrangling (arrays, objects, streams, etc) 37 | - IPFS core subsystems 38 | 39 | *General use* and *Data structs wrangling* should try to be just re-exports of community packages. 40 | 41 | The IPFS ecosystem has lots of repos with it comes several problems like: 42 | 43 | - Domain logic dedupe - all interface-core implementations shared a lot of logic like validation, streams handling, etc. 44 | - Dependencies management - it's really easy with so many repos for dependencies to go out of control, they become outdated, different repos use different modules to do the same thing (like merging defaults options), browser bundles ends up with multiple versions of the same package, bumping versions is cumbersome to do because we need to go through several repos, etc. 45 | 46 | These problems are the motivation for this package, having shared logic in this package avoids creating cyclic dependencies, centralizes common use modules/functions (exactly like aegir does for the tooling), semantic versioning for 3rd party dependencies is handled in one single place (a good example is going from streams 2 to 3) and maintainers should only care about having `ipfs-utils` updated. 47 | 48 | ## Usage 49 | 50 | Each function should be imported directly. 51 | 52 | ```js 53 | const validateAddInput = require('ipfs-utils/src/files/add-input-validation') 54 | 55 | validateAddInput(Buffer.from('test')) 56 | // true 57 | ``` 58 | 59 | ## API Docs 60 | 61 | - 62 | 63 | ## License 64 | 65 | Licensed under either of 66 | 67 | - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) 68 | - MIT ([LICENSE-MIT](LICENSE-MIT) / ) 69 | 70 | ## Contribute 71 | 72 | Contributions welcome! Please check out [the issues](https://github.com/ipfs/js-ipfs-utils/issues). 73 | 74 | Also see our [contributing document](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md) for more information on how we work, and about contributing in general. 75 | 76 | Please be aware that all interactions related to this repo are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). 77 | 78 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 79 | 80 | [![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ipfs-utils", 3 | "version": "9.0.14", 4 | "description": "Package to aggregate shared logic and dependencies for the IPFS ecosystem", 5 | "author": "Hugo Dias ", 6 | "license": "Apache-2.0 OR MIT", 7 | "homepage": "https://github.com/ipfs/js-ipfs-utils#readme", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/ipfs/js-ipfs-utils.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/ipfs/js-ipfs-utils/issues" 14 | }, 15 | "engines": { 16 | "node": ">=16.0.0", 17 | "npm": ">=7.0.0" 18 | }, 19 | "main": "src/index.js", 20 | "types": "dist/src/index.d.ts", 21 | "typesVersions": { 22 | "*": { 23 | "*": [ 24 | "*", 25 | "dist/*", 26 | "dist/src/*" 27 | ], 28 | "src/*": [ 29 | "*", 30 | "dist/*", 31 | "dist/src/*" 32 | ] 33 | } 34 | }, 35 | "files": [ 36 | "src", 37 | "dist" 38 | ], 39 | "eslintConfig": { 40 | "extends": "ipfs", 41 | "env": { 42 | "worker": true 43 | } 44 | }, 45 | "release": { 46 | "branches": [ 47 | "master" 48 | ], 49 | "plugins": [ 50 | [ 51 | "@semantic-release/commit-analyzer", 52 | { 53 | "preset": "conventionalcommits", 54 | "releaseRules": [ 55 | { 56 | "breaking": true, 57 | "release": "major" 58 | }, 59 | { 60 | "revert": true, 61 | "release": "patch" 62 | }, 63 | { 64 | "type": "feat", 65 | "release": "minor" 66 | }, 67 | { 68 | "type": "fix", 69 | "release": "patch" 70 | }, 71 | { 72 | "type": "docs", 73 | "release": "patch" 74 | }, 75 | { 76 | "type": "test", 77 | "release": "patch" 78 | }, 79 | { 80 | "type": "deps", 81 | "release": "patch" 82 | }, 83 | { 84 | "scope": "no-release", 85 | "release": false 86 | } 87 | ] 88 | } 89 | ], 90 | [ 91 | "@semantic-release/release-notes-generator", 92 | { 93 | "preset": "conventionalcommits", 94 | "presetConfig": { 95 | "types": [ 96 | { 97 | "type": "feat", 98 | "section": "Features" 99 | }, 100 | { 101 | "type": "fix", 102 | "section": "Bug Fixes" 103 | }, 104 | { 105 | "type": "chore", 106 | "section": "Trivial Changes" 107 | }, 108 | { 109 | "type": "docs", 110 | "section": "Documentation" 111 | }, 112 | { 113 | "type": "deps", 114 | "section": "Dependencies" 115 | }, 116 | { 117 | "type": "test", 118 | "section": "Tests" 119 | } 120 | ] 121 | } 122 | } 123 | ], 124 | "@semantic-release/changelog", 125 | "@semantic-release/npm", 126 | "@semantic-release/github", 127 | "@semantic-release/git" 128 | ] 129 | }, 130 | "scripts": { 131 | "clean": "aegir clean", 132 | "lint": "aegir lint", 133 | "dep-check": "aegir dep-check", 134 | "build": "aegir build", 135 | "test": "aegir test", 136 | "test:chrome": "npm run test -- -t browser --cov", 137 | "test:chrome-webworker": "npm run test -- -t webworker", 138 | "test:firefox": "npm run test -- -t browser -- --browser firefox", 139 | "test:firefox-webworker": "npm run test -- -t webworker -- --browser firefox", 140 | "test:node": "npm run test -- -t node --cov", 141 | "test:electron-main": "npm run test -- -t electron-main", 142 | "release": "semantic-release", 143 | "docs": "aegir docs" 144 | }, 145 | "dependencies": { 146 | "any-signal": "^3.0.0", 147 | "browser-readablestream-to-it": "^1.0.0", 148 | "buffer": "^6.0.1", 149 | "electron-fetch": "^1.7.2", 150 | "err-code": "^3.0.1", 151 | "is-electron": "^2.2.0", 152 | "iso-url": "^1.1.5", 153 | "it-all": "^1.0.4", 154 | "it-glob": "^1.0.1", 155 | "it-to-stream": "^1.0.0", 156 | "merge-options": "^3.0.4", 157 | "nanoid": "^3.1.20", 158 | "native-fetch": "^3.0.0", 159 | "node-fetch": "^2.6.8", 160 | "react-native-fetch-api": "^3.0.0", 161 | "stream-to-it": "^0.2.2" 162 | }, 163 | "devDependencies": { 164 | "aegir": "^36.1.1", 165 | "delay": "^5.0.0", 166 | "events": "^3.3.0", 167 | "ipfs-unixfs": "^6.0.4", 168 | "it-drain": "^1.0.3", 169 | "it-last": "^1.0.4", 170 | "it-to-buffer": "^2.0.0", 171 | "react-native-polyfill-globals": "^3.0.0", 172 | "readable-stream": "^4.3.0", 173 | "uint8arrays": "^3.0.0", 174 | "util": "^0.12.3" 175 | }, 176 | "browser": { 177 | "./src/http/fetch.js": "./src/http/fetch.browser.js", 178 | "./src/temp-dir.js": "./src/temp-dir.browser.js", 179 | "./src/path-join.js": "./src/path-join.browser.js", 180 | "./src/fetch.js": "./src/fetch.browser.js", 181 | "./src/files/glob-source.js": false, 182 | "./test/files/glob-source.spec.js": false, 183 | "electron-fetch": false, 184 | "node-fetch": false, 185 | "fs": false 186 | }, 187 | "react-native": { 188 | "./src/fetch.js": "./src/fetch.rn.js", 189 | "./src/http/fetch.js": "./src/http/fetch.rn.js" 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /rn-test.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | require: require.resolve('./rn-test.require.js'), 5 | runner: 'mocha', 6 | modules: [ 7 | 'react-native-url-polyfill', 8 | 'web-streams-polyfill', 9 | 'text-encoding' 10 | ], 11 | patches: [{ 12 | path: require.resolve('react-native-polyfill-globals/patches/react-native+0.63.3.patch') 13 | }] 14 | } 15 | -------------------------------------------------------------------------------- /rn-test.require.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { polyfill: polyfillReadableStream } = require('react-native-polyfill-globals/src/readable-stream') 4 | const { polyfill: polyfillURL } = require('react-native-polyfill-globals/src/url') 5 | const { polyfill: polyfillEncoding } = require('react-native-polyfill-globals/src/encoding') 6 | 7 | polyfillURL() 8 | polyfillReadableStream() 9 | polyfillEncoding() 10 | -------------------------------------------------------------------------------- /src/env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const isElectron = require('is-electron') 3 | 4 | const IS_ENV_WITH_DOM = typeof window === 'object' && typeof document === 'object' && document.nodeType === 9 5 | // @ts-ignore 6 | const IS_ELECTRON = isElectron() 7 | const IS_BROWSER = IS_ENV_WITH_DOM && !IS_ELECTRON 8 | const IS_ELECTRON_MAIN = IS_ELECTRON && !IS_ENV_WITH_DOM 9 | const IS_ELECTRON_RENDERER = IS_ELECTRON && IS_ENV_WITH_DOM 10 | const IS_NODE = typeof require === 'function' && typeof process !== 'undefined' && typeof process.release !== 'undefined' && process.release.name === 'node' && !IS_ELECTRON 11 | // @ts-ignore - we either ignore worker scope or dom scope 12 | const IS_WEBWORKER = typeof importScripts === 'function' && typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope 13 | const IS_TEST = typeof process !== 'undefined' && typeof process.env !== 'undefined' && process.env.NODE_ENV === 'test' 14 | const IS_REACT_NATIVE = typeof navigator !== 'undefined' && navigator.product === 'ReactNative' 15 | 16 | module.exports = { 17 | isTest: IS_TEST, 18 | isElectron: IS_ELECTRON, 19 | isElectronMain: IS_ELECTRON_MAIN, 20 | isElectronRenderer: IS_ELECTRON_RENDERER, 21 | isNode: IS_NODE, 22 | /** 23 | * Detects browser main thread **NOT** web worker or service worker 24 | */ 25 | isBrowser: IS_BROWSER, 26 | isWebWorker: IS_WEBWORKER, 27 | isEnvWithDom: IS_ENV_WITH_DOM, 28 | isReactNative: IS_REACT_NATIVE 29 | } 30 | -------------------------------------------------------------------------------- /src/fetch.browser.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * @typedef {globalThis.Headers} Headers 5 | * @typedef {globalThis.Request} Request 6 | * @typedef {globalThis.Response} Response 7 | */ 8 | 9 | // use window.fetch if it is available, fall back to node-fetch if not 10 | module.exports = require('native-fetch') 11 | -------------------------------------------------------------------------------- /src/fetch.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * @typedef {globalThis.Headers} Headers 5 | * @typedef {globalThis.Request} Request 6 | * @typedef {globalThis.Response} Response 7 | */ 8 | 9 | const { isElectronMain } = require('./env') 10 | 11 | // use window.fetch if it is available, fall back to node-fetch if not 12 | let impl = 'native-fetch' 13 | 14 | if (isElectronMain) { 15 | impl = 'electron-fetch' 16 | } 17 | 18 | module.exports = require(impl) 19 | -------------------------------------------------------------------------------- /src/fetch.rn.js: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | 'use strict' 3 | // @ts-ignore 4 | const { Headers, Request, Response, fetch } = require('react-native-fetch-api') 5 | 6 | /** @type {import('electron-fetch').default} */ 7 | const rnFetch = fetch 8 | /** @type {import('electron-fetch').Headers} */ 9 | const rnHeaders = Headers 10 | /** @type {import('electron-fetch').Request} */ 11 | const rnRequest = Request 12 | /** @type {import('electron-fetch').Response} */ 13 | const rnResponse = Response 14 | module.exports = rnFetch 15 | module.exports.Headers = rnHeaders 16 | module.exports.Request = rnRequest 17 | module.exports.Response = rnResponse 18 | module.exports.default = rnFetch 19 | -------------------------------------------------------------------------------- /src/files/glob-source.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fsp = require('fs').promises 4 | const fs = require('fs') 5 | const glob = require('it-glob') 6 | const Path = require('path') 7 | const errCode = require('err-code') 8 | 9 | /** 10 | * Create an async iterator that yields paths that match requested glob pattern 11 | * 12 | * @param {string} cwd - The directory to start matching the pattern in 13 | * @param {string} pattern - Glob pattern to match 14 | * @param {import('../types').GlobSourceOptions} [options] - Optional options 15 | * @returns {AsyncGenerator} File objects that match glob 16 | */ 17 | module.exports = async function * globSource (cwd, pattern, options) { 18 | options = options || {} 19 | 20 | if (typeof pattern !== 'string') { 21 | throw errCode( 22 | new Error('Pattern must be a string'), 23 | 'ERR_INVALID_PATH', 24 | { pattern } 25 | ) 26 | } 27 | 28 | if (!Path.isAbsolute(cwd)) { 29 | cwd = Path.resolve(process.cwd(), cwd) 30 | } 31 | 32 | const globOptions = Object.assign({}, { 33 | nodir: false, 34 | realpath: false, 35 | absolute: true, 36 | dot: Boolean(options.hidden), 37 | follow: options.followSymlinks != null ? options.followSymlinks : true 38 | }) 39 | 40 | for await (const p of glob(cwd, pattern, globOptions)) { 41 | const stat = await fsp.stat(p) 42 | 43 | let mode = options.mode 44 | 45 | if (options.preserveMode) { 46 | mode = stat.mode 47 | } 48 | 49 | let mtime = options.mtime 50 | 51 | if (options.preserveMtime) { 52 | mtime = stat.mtime 53 | } 54 | 55 | yield { 56 | path: toPosix(p.replace(cwd, '')), 57 | content: stat.isFile() ? fs.createReadStream(p) : undefined, 58 | mode, 59 | mtime 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * @param {string} path 66 | */ 67 | const toPosix = path => path.replace(/\\/g, '/') 68 | -------------------------------------------------------------------------------- /src/files/url-source.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const HTTP = require('../http') 4 | 5 | /** 6 | * 7 | * @param {string} url 8 | * @param {import("../types").HTTPOptions} [options] 9 | * @returns {{ path: string; content?: AsyncIterable }} 10 | */ 11 | const urlSource = (url, options) => { 12 | return { 13 | path: decodeURIComponent(new URL(url).pathname.split('/').pop() || ''), 14 | content: readURLContent(url, options) 15 | } 16 | } 17 | 18 | /** 19 | * 20 | * @param {string} url 21 | * @param {import("../types").HTTPOptions} [options] 22 | * @returns {AsyncIterable} 23 | */ 24 | async function * readURLContent (url, options) { 25 | const http = new HTTP() 26 | const response = await http.get(url, options) 27 | 28 | yield * response.iterator() 29 | } 30 | 31 | module.exports = urlSource 32 | -------------------------------------------------------------------------------- /src/http.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | 'use strict' 3 | 4 | const { fetch, Request, Headers } = require('./http/fetch') 5 | const { TimeoutError, HTTPError } = require('./http/error') 6 | const merge = require('merge-options').bind({ ignoreUndefined: true }) 7 | const { URL, URLSearchParams } = require('iso-url') 8 | const anySignal = require('any-signal') 9 | const browserReableStreamToIt = require('browser-readablestream-to-it') 10 | const { isBrowser, isWebWorker } = require('./env') 11 | const all = require('it-all') 12 | 13 | /** 14 | * @typedef {import('stream').Readable} NodeReadableStream 15 | * @typedef {import('./types').HTTPOptions} HTTPOptions 16 | * @typedef {import('./types').ExtendedResponse} ExtendedResponse 17 | */ 18 | 19 | /** 20 | * @template TResponse 21 | * @param {Promise} promise 22 | * @param {number | undefined} ms 23 | * @param {AbortController} abortController 24 | * @returns {Promise} 25 | */ 26 | const timeout = (promise, ms, abortController) => { 27 | if (ms === undefined) { 28 | return promise 29 | } 30 | 31 | const start = Date.now() 32 | 33 | const timedOut = () => { 34 | const time = Date.now() - start 35 | 36 | return time >= ms 37 | } 38 | 39 | return new Promise((resolve, reject) => { 40 | const timeoutID = setTimeout(() => { 41 | if (timedOut()) { 42 | reject(new TimeoutError()) 43 | abortController.abort() 44 | } 45 | }, ms) 46 | 47 | /** 48 | * @param {(value: any) => void } next 49 | */ 50 | const after = (next) => { 51 | /** 52 | * @param {any} res 53 | */ 54 | const fn = (res) => { 55 | clearTimeout(timeoutID) 56 | 57 | if (timedOut()) { 58 | reject(new TimeoutError()) 59 | return 60 | } 61 | 62 | next(res) 63 | } 64 | return fn 65 | } 66 | 67 | promise 68 | .then(after(resolve), after(reject)) 69 | }) 70 | } 71 | 72 | const defaults = { 73 | throwHttpErrors: true, 74 | credentials: 'same-origin' 75 | } 76 | 77 | class HTTP { 78 | /** 79 | * 80 | * @param {HTTPOptions} options 81 | */ 82 | constructor (options = {}) { 83 | /** @type {HTTPOptions} */ 84 | this.opts = merge(defaults, options) 85 | } 86 | 87 | /** 88 | * Fetch 89 | * 90 | * @param {string | Request} resource 91 | * @param {HTTPOptions} options 92 | * @returns {Promise} 93 | */ 94 | async fetch (resource, options = {}) { 95 | /** @type {HTTPOptions} */ 96 | const opts = merge(this.opts, options) 97 | // @ts-expect-error 98 | const headers = new Headers(opts.headers) 99 | 100 | // validate resource type 101 | // @ts-expect-error 102 | if (typeof resource !== 'string' && !(resource instanceof URL || resource instanceof Request)) { 103 | throw new TypeError('`resource` must be a string, URL, or Request') 104 | } 105 | 106 | const url = new URL(resource.toString(), opts.base) 107 | 108 | const { 109 | searchParams, 110 | transformSearchParams, 111 | json 112 | } = opts 113 | 114 | if (searchParams) { 115 | if (typeof transformSearchParams === 'function') { 116 | // @ts-ignore 117 | url.search = transformSearchParams(new URLSearchParams(opts.searchParams)) 118 | } else { 119 | // @ts-ignore 120 | url.search = new URLSearchParams(opts.searchParams) 121 | } 122 | } 123 | 124 | if (json) { 125 | opts.body = JSON.stringify(opts.json) 126 | headers.set('content-type', 'application/json') 127 | } 128 | 129 | const abortController = new AbortController() 130 | // @ts-ignore 131 | const signal = anySignal([abortController.signal, opts.signal]) 132 | 133 | if (globalThis.ReadableStream != null && opts.body instanceof globalThis.ReadableStream && (isBrowser || isWebWorker)) { 134 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1387483 135 | opts.body = new Blob(await all(browserReableStreamToIt(opts.body))) 136 | } 137 | 138 | /** @type {ExtendedResponse} */ 139 | // @ts-expect-error additional fields are assigned below 140 | const response = await timeout( 141 | fetch( 142 | url.toString(), 143 | { 144 | ...opts, 145 | signal, 146 | // @ts-expect-error non-browser fetch implementations may take extra options 147 | timeout: undefined, 148 | headers, 149 | 150 | // https://fetch.spec.whatwg.org/#dom-requestinit-duplex 151 | // https://github.com/whatwg/fetch/issues/1254 152 | duplex: 'half' 153 | } 154 | ), 155 | opts.timeout, 156 | abortController 157 | ) 158 | 159 | if (!response.ok && opts.throwHttpErrors) { 160 | if (opts.handleError) { 161 | await opts.handleError(response) 162 | } 163 | throw new HTTPError(response) 164 | } 165 | 166 | response.iterator = async function * () { 167 | yield * fromStream(response.body) 168 | } 169 | 170 | response.ndjson = async function * () { 171 | for await (const chunk of ndjson(response.iterator())) { 172 | if (options.transform) { 173 | yield options.transform(chunk) 174 | } else { 175 | yield chunk 176 | } 177 | } 178 | } 179 | 180 | return response 181 | } 182 | 183 | /** 184 | * @param {string | Request} resource 185 | * @param {HTTPOptions} options 186 | */ 187 | post (resource, options = {}) { 188 | return this.fetch(resource, { ...options, method: 'POST' }) 189 | } 190 | 191 | /** 192 | * @param {string | Request} resource 193 | * @param {HTTPOptions} options 194 | */ 195 | get (resource, options = {}) { 196 | return this.fetch(resource, { ...options, method: 'GET' }) 197 | } 198 | 199 | /** 200 | * @param {string | Request} resource 201 | * @param {HTTPOptions} options 202 | */ 203 | put (resource, options = {}) { 204 | return this.fetch(resource, { ...options, method: 'PUT' }) 205 | } 206 | 207 | /** 208 | * @param {string | Request} resource 209 | * @param {HTTPOptions} options 210 | */ 211 | delete (resource, options = {}) { 212 | return this.fetch(resource, { ...options, method: 'DELETE' }) 213 | } 214 | 215 | /** 216 | * @param {string | Request} resource 217 | * @param {HTTPOptions} options 218 | */ 219 | options (resource, options = {}) { 220 | return this.fetch(resource, { ...options, method: 'OPTIONS' }) 221 | } 222 | } 223 | 224 | /** 225 | * Parses NDJSON chunks from an iterator 226 | * 227 | * @param {AsyncIterable} source 228 | * @returns {AsyncIterable} 229 | */ 230 | const ndjson = async function * (source) { 231 | const decoder = new TextDecoder() 232 | let buf = '' 233 | 234 | for await (const chunk of source) { 235 | buf += decoder.decode(chunk, { stream: true }) 236 | const lines = buf.split(/\r?\n/) 237 | 238 | for (let i = 0; i < lines.length - 1; i++) { 239 | const l = lines[i].trim() 240 | if (l.length > 0) { 241 | yield JSON.parse(l) 242 | } 243 | } 244 | buf = lines[lines.length - 1] 245 | } 246 | buf += decoder.decode() 247 | buf = buf.trim() 248 | if (buf.length !== 0) { 249 | yield JSON.parse(buf) 250 | } 251 | } 252 | 253 | /** 254 | * Stream to AsyncIterable 255 | * 256 | * @template TChunk 257 | * @param {ReadableStream | NodeReadableStream | null} source 258 | * @returns {AsyncIterable} 259 | */ 260 | const fromStream = (source) => { 261 | if (isAsyncIterable(source)) { 262 | return source 263 | } 264 | 265 | // Workaround for https://github.com/node-fetch/node-fetch/issues/766 266 | if (isNodeReadableStream(source)) { 267 | const iter = source[Symbol.asyncIterator]() 268 | return { 269 | [Symbol.asyncIterator] () { 270 | return { 271 | next: iter.next.bind(iter), 272 | return (value) { 273 | source.destroy() 274 | if (typeof iter.return === 'function') { 275 | return iter.return() 276 | } 277 | return Promise.resolve({ done: true, value }) 278 | } 279 | } 280 | } 281 | } 282 | } 283 | 284 | if (isWebReadableStream(source)) { 285 | const reader = source.getReader() 286 | return (async function * () { 287 | try { 288 | while (true) { 289 | // Read from the stream 290 | const { done, value } = await reader.read() 291 | // Exit if we're done 292 | if (done) return 293 | // Else yield the chunk 294 | if (value) { 295 | yield value 296 | } 297 | } 298 | } finally { 299 | reader.releaseLock() 300 | } 301 | })() 302 | } 303 | 304 | throw new TypeError('Body can\'t be converted to AsyncIterable') 305 | } 306 | 307 | /** 308 | * Check if it's an AsyncIterable 309 | * 310 | * @template {unknown} TChunk 311 | * @template {any} Other 312 | * @param {Other|AsyncIterable} value 313 | * @returns {value is AsyncIterable} 314 | */ 315 | const isAsyncIterable = (value) => { 316 | return typeof value === 'object' && 317 | value !== null && 318 | typeof /** @type {any} */(value)[Symbol.asyncIterator] === 'function' 319 | } 320 | 321 | /** 322 | * Check for web readable stream 323 | * 324 | * @template {unknown} TChunk 325 | * @template {any} Other 326 | * @param {Other|ReadableStream} value 327 | * @returns {value is ReadableStream} 328 | */ 329 | const isWebReadableStream = (value) => { 330 | return value && typeof /** @type {any} */(value).getReader === 'function' 331 | } 332 | 333 | /** 334 | * @param {any} value 335 | * @returns {value is NodeReadableStream} 336 | */ 337 | const isNodeReadableStream = (value) => 338 | Object.prototype.hasOwnProperty.call(value, 'readable') && 339 | Object.prototype.hasOwnProperty.call(value, 'writable') 340 | 341 | HTTP.HTTPError = HTTPError 342 | HTTP.TimeoutError = TimeoutError 343 | HTTP.streamToAsyncIterator = fromStream 344 | 345 | /** 346 | * @param {string | Request} resource 347 | * @param {HTTPOptions} [options] 348 | */ 349 | HTTP.post = (resource, options) => new HTTP(options).post(resource, options) 350 | 351 | /** 352 | * @param {string | Request} resource 353 | * @param {HTTPOptions} [options] 354 | */ 355 | HTTP.get = (resource, options) => new HTTP(options).get(resource, options) 356 | 357 | /** 358 | * @param {string | Request} resource 359 | * @param {HTTPOptions} [options] 360 | */ 361 | HTTP.put = (resource, options) => new HTTP(options).put(resource, options) 362 | 363 | /** 364 | * @param {string | Request} resource 365 | * @param {HTTPOptions} [options] 366 | */ 367 | HTTP.delete = (resource, options) => new HTTP(options).delete(resource, options) 368 | 369 | /** 370 | * @param {string | Request} resource 371 | * @param {HTTPOptions} [options] 372 | */ 373 | HTTP.options = (resource, options) => new HTTP(options).options(resource, options) 374 | 375 | module.exports = HTTP 376 | -------------------------------------------------------------------------------- /src/http/error.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | class TimeoutError extends Error { 4 | constructor (message = 'Request timed out') { 5 | super(message) 6 | this.name = 'TimeoutError' 7 | } 8 | } 9 | exports.TimeoutError = TimeoutError 10 | 11 | class AbortError extends Error { 12 | constructor (message = 'The operation was aborted.') { 13 | super(message) 14 | this.name = 'AbortError' 15 | } 16 | } 17 | exports.AbortError = AbortError 18 | 19 | class HTTPError extends Error { 20 | /** 21 | * @param {Response} response 22 | */ 23 | constructor (response) { 24 | super(response.statusText) 25 | this.name = 'HTTPError' 26 | this.response = response 27 | } 28 | } 29 | exports.HTTPError = HTTPError 30 | -------------------------------------------------------------------------------- /src/http/fetch.browser.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { TimeoutError, AbortError } = require('./error') 4 | // @ts-expect-error 5 | const { Response, Request, Headers, default: fetch } = require('../fetch') 6 | 7 | /** 8 | * @typedef {import('../types').FetchOptions} FetchOptions 9 | * @typedef {import('../types').ProgressFn} ProgressFn 10 | */ 11 | 12 | /** 13 | * Fetch with progress 14 | * 15 | * @param {string | Request} url 16 | * @param {FetchOptions} [options] 17 | * @returns {Promise} 18 | */ 19 | const fetchWithProgress = (url, options = {}) => { 20 | const request = new XMLHttpRequest() 21 | request.open(options.method || 'GET', url.toString(), true) 22 | 23 | const { timeout, headers } = options 24 | 25 | if (timeout && timeout > 0 && timeout < Infinity) { 26 | request.timeout = timeout 27 | } 28 | 29 | if (options.overrideMimeType != null) { 30 | request.overrideMimeType(options.overrideMimeType) 31 | } 32 | 33 | if (headers) { 34 | for (const [name, value] of new Headers(headers)) { 35 | request.setRequestHeader(name, value) 36 | } 37 | } 38 | 39 | if (options.signal) { 40 | options.signal.onabort = () => request.abort() 41 | } 42 | 43 | if (options.onUploadProgress) { 44 | request.upload.onprogress = options.onUploadProgress 45 | } 46 | 47 | // Note: Need to use `arraybuffer` here instead of `blob` because `Blob` 48 | // instances coming from JSDOM are not compatible with `Response` from 49 | // node-fetch (which is the setup we get when testing with jest because 50 | // it uses JSDOM which does not provide a global fetch 51 | // https://github.com/jsdom/jsdom/issues/1724) 52 | request.responseType = 'arraybuffer' 53 | 54 | return new Promise((resolve, reject) => { 55 | /** 56 | * @param {Event} event 57 | */ 58 | const handleEvent = (event) => { 59 | switch (event.type) { 60 | case 'error': { 61 | resolve(Response.error()) 62 | break 63 | } 64 | case 'load': { 65 | resolve( 66 | new ResponseWithURL(request.responseURL, request.response, { 67 | status: request.status, 68 | statusText: request.statusText, 69 | headers: parseHeaders(request.getAllResponseHeaders()) 70 | }) 71 | ) 72 | break 73 | } 74 | case 'timeout': { 75 | reject(new TimeoutError()) 76 | break 77 | } 78 | case 'abort': { 79 | reject(new AbortError()) 80 | break 81 | } 82 | default: { 83 | break 84 | } 85 | } 86 | } 87 | request.onerror = handleEvent 88 | request.onload = handleEvent 89 | request.ontimeout = handleEvent 90 | request.onabort = handleEvent 91 | 92 | // @ts-expect-error options.body can be a node readable stream, which isn't compatible with XHR, but this 93 | // file is a browser override so you won't get a node readable stream so ignore the error 94 | request.send(options.body) 95 | }) 96 | } 97 | 98 | const fetchWithStreaming = fetch 99 | 100 | /** 101 | * @param {string | Request} url 102 | * @param {FetchOptions} options 103 | */ 104 | const fetchWith = (url, options = {}) => 105 | (options.onUploadProgress != null) 106 | ? fetchWithProgress(url, options) 107 | : fetchWithStreaming(url, options) 108 | 109 | /** 110 | * Parse Headers from a XMLHttpRequest 111 | * 112 | * @param {string} input 113 | * @returns {Headers} 114 | */ 115 | const parseHeaders = (input) => { 116 | const headers = new Headers() 117 | for (const line of input.trim().split(/[\r\n]+/)) { 118 | const index = line.indexOf(': ') 119 | if (index > 0) { 120 | headers.set(line.slice(0, index), line.slice(index + 1)) 121 | } 122 | } 123 | 124 | return headers 125 | } 126 | 127 | class ResponseWithURL extends Response { 128 | /** 129 | * @param {string} url 130 | * @param {BodyInit} body 131 | * @param {ResponseInit} options 132 | */ 133 | constructor (url, body, options) { 134 | super(body, options) 135 | Object.defineProperty(this, 'url', { value: url }) 136 | } 137 | } 138 | 139 | module.exports = { 140 | fetch: fetchWith, 141 | Request, 142 | Headers 143 | } 144 | -------------------------------------------------------------------------------- /src/http/fetch.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * @typedef {object} fetchImpl 5 | * @property {globalThis.fetch} fetchImpl.fetch 6 | * @property {globalThis.Request} fetchImpl.Request 7 | * @property {globalThis.Response} fetchImpl.Response 8 | * @property {globalThis.Headers} fetchImpl.Headers 9 | */ 10 | 11 | let implName = './fetch.node' 12 | 13 | if (typeof XMLHttpRequest === 'function') { 14 | // Electron has `XMLHttpRequest` and should get the browser implementation 15 | // instead of node. 16 | implName = './fetch.browser' 17 | } 18 | 19 | /** @type {fetchImpl} */ 20 | const fetch = require(implName) 21 | 22 | module.exports = fetch 23 | -------------------------------------------------------------------------------- /src/http/fetch.node.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // @ts-expect-error Request, Response and Headers are global types but concrete in implementations 3 | const { Request, Response, Headers, default: defaultFetch, fetch: fetchFetch } = require('../fetch') 4 | // @ts-ignore 5 | const toStream = require('it-to-stream') 6 | const { Buffer } = require('buffer') 7 | /** 8 | * @typedef {import('stream').Readable} NodeReadableStream 9 | * 10 | * @typedef {import('../types').FetchOptions} FetchOptions 11 | * @typedef {import('../types').ProgressFn} ProgressFn 12 | */ 13 | 14 | // undici and node-fetch have different exports 15 | const nativeFetch = defaultFetch ?? fetchFetch 16 | 17 | /** 18 | * @param {string|Request} url 19 | * @param {FetchOptions} [options] 20 | * @returns {Promise} 21 | */ 22 | const fetch = (url, options = {}) => 23 | // @ts-ignore 24 | nativeFetch(url, withUploadProgress(options)) 25 | 26 | /** 27 | * Takes fetch options and wraps request body to track upload progress if 28 | * `onUploadProgress` is supplied. Otherwise returns options as is. 29 | * 30 | * @param {FetchOptions} options 31 | * @returns {FetchOptions} 32 | */ 33 | const withUploadProgress = (options) => { 34 | const { onUploadProgress, body } = options 35 | if (onUploadProgress && body) { 36 | // This works around the fact that electron-fetch serializes `Uint8Array`s 37 | // and `ArrayBuffer`s to strings. 38 | const content = normalizeBody(body) 39 | 40 | // @ts-expect-error this is node-fetch 41 | const rsp = new Response(content) 42 | // @ts-expect-error this is node-fetch 43 | const source = iterateBodyWithProgress(/** @type {NodeReadableStream} */(rsp.body), onUploadProgress) 44 | return { 45 | ...options, 46 | body: toStream.readable(source) 47 | } 48 | } else { 49 | return options 50 | } 51 | } 52 | 53 | /** 54 | * @param {BodyInit | NodeReadableStream} input 55 | */ 56 | const normalizeBody = (input) => { 57 | if (input instanceof ArrayBuffer) { 58 | return Buffer.from(input) 59 | } else if (ArrayBuffer.isView(input)) { 60 | return Buffer.from(input.buffer, input.byteOffset, input.byteLength) 61 | } else if (typeof input === 'string') { 62 | return Buffer.from(input) 63 | } 64 | return input 65 | } 66 | 67 | /** 68 | * Takes body from native-fetch response as body and `onUploadProgress` handler 69 | * and returns async iterable that emits body chunks and emits 70 | * `onUploadProgress`. 71 | * 72 | * @param {NodeReadableStream | null} body 73 | * @param {ProgressFn} onUploadProgress 74 | * @returns {AsyncIterable} 75 | */ 76 | const iterateBodyWithProgress = async function * (body, onUploadProgress) { 77 | if (body == null) { 78 | onUploadProgress({ total: 0, loaded: 0, lengthComputable: true }) 79 | } else if (Buffer.isBuffer(body)) { 80 | const total = body.byteLength 81 | const lengthComputable = true 82 | yield body 83 | onUploadProgress({ total, loaded: total, lengthComputable }) 84 | } else { 85 | const total = 0 86 | const lengthComputable = false 87 | let loaded = 0 88 | for await (const chunk of body) { 89 | loaded += chunk.byteLength 90 | yield chunk 91 | onUploadProgress({ total, loaded, lengthComputable }) 92 | } 93 | } 94 | } 95 | 96 | module.exports = { 97 | fetch, 98 | Request, 99 | Headers 100 | } 101 | -------------------------------------------------------------------------------- /src/http/fetch.rn.js: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | 'use strict' 3 | 4 | const { TimeoutError, AbortError } = require('./error') 5 | const { Response, Request, Headers, default: fetch } = require('../fetch') 6 | 7 | /** 8 | * @typedef {import('../types').FetchOptions} FetchOptions 9 | * @typedef {import('../types').ProgressFn} ProgressFn 10 | */ 11 | 12 | /** 13 | * Fetch with progress 14 | * 15 | * @param {string | Request} url 16 | * @param {FetchOptions} [options] 17 | * @returns {Promise} 18 | */ 19 | const fetchWithProgress = (url, options = {}) => { 20 | const request = new XMLHttpRequest() 21 | request.open(options.method || 'GET', url.toString(), true) 22 | 23 | const { timeout, headers } = options 24 | 25 | if (timeout && timeout > 0 && timeout < Infinity) { 26 | request.timeout = timeout 27 | } 28 | 29 | if (options.overrideMimeType != null) { 30 | request.overrideMimeType(options.overrideMimeType) 31 | } 32 | 33 | if (headers) { 34 | for (const [name, value] of new Headers(headers)) { 35 | request.setRequestHeader(name, value) 36 | } 37 | } 38 | 39 | if (options.signal) { 40 | options.signal.onabort = () => request.abort() 41 | } 42 | 43 | if (options.onUploadProgress) { 44 | request.upload.onprogress = options.onUploadProgress 45 | } 46 | 47 | request.responseType = 'blob' 48 | 49 | return new Promise((resolve, reject) => { 50 | /** 51 | * @param {Event} event 52 | */ 53 | const handleEvent = (event) => { 54 | switch (event.type) { 55 | case 'error': { 56 | resolve(Response.error()) 57 | break 58 | } 59 | case 'load': { 60 | resolve( 61 | new ResponseWithURL(request.responseURL, request.response, { 62 | status: request.status, 63 | statusText: request.statusText, 64 | headers: parseHeaders(request.getAllResponseHeaders()) 65 | }) 66 | ) 67 | break 68 | } 69 | case 'timeout': { 70 | reject(new TimeoutError()) 71 | break 72 | } 73 | case 'abort': { 74 | reject(new AbortError()) 75 | break 76 | } 77 | default: { 78 | break 79 | } 80 | } 81 | } 82 | request.onerror = handleEvent 83 | request.onload = handleEvent 84 | request.ontimeout = handleEvent 85 | request.onabort = handleEvent 86 | 87 | request.send(/** @type {BodyInit} */(options.body)) 88 | }) 89 | } 90 | 91 | const fetchWithStreaming = fetch 92 | 93 | /** 94 | * @param {string | Request} url 95 | * @param {FetchOptions} options 96 | */ 97 | const fetchWith = (url, options = {}) => 98 | (options.onUploadProgress != null) 99 | ? fetchWithProgress(url, options) 100 | : fetchWithStreaming(url, options) 101 | 102 | /** 103 | * Parse Headers from a XMLHttpRequest 104 | * 105 | * @param {string} input 106 | * @returns {Headers} 107 | */ 108 | const parseHeaders = (input) => { 109 | const headers = new Headers() 110 | for (const line of input.trim().split(/[\r\n]+/)) { 111 | const index = line.indexOf(': ') 112 | if (index > 0) { 113 | headers.set(line.slice(0, index), line.slice(index + 1)) 114 | } 115 | } 116 | 117 | return headers 118 | } 119 | 120 | class ResponseWithURL extends Response { 121 | /** 122 | * @param {string} url 123 | * @param {BodyInit} body 124 | * @param {ResponseInit} options 125 | */ 126 | constructor (url, body, options) { 127 | super(body, options) 128 | Object.defineProperty(this, 'url', { value: url }) 129 | } 130 | } 131 | 132 | module.exports = { 133 | fetch: fetchWith, 134 | Request, 135 | Headers, 136 | ResponseWithURL 137 | } 138 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // just a empty entry point to avoid errors from aegir 3 | module.exports = {} 4 | -------------------------------------------------------------------------------- /src/path-join.browser.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * @param {string[]} args 5 | */ 6 | function join (...args) { 7 | if (args.length === 0) { 8 | return '.' 9 | } 10 | 11 | return args.join('/') 12 | } 13 | 14 | module.exports = join 15 | -------------------------------------------------------------------------------- /src/path-join.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { join } = require('path') 3 | module.exports = join 4 | -------------------------------------------------------------------------------- /src/supports.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | // in React Native: global === window === self 5 | supportsFileReader: typeof self !== 'undefined' && 'FileReader' in self, 6 | supportsWebRTC: 'RTCPeerConnection' in globalThis && 7 | (typeof navigator !== 'undefined' && typeof navigator.mediaDevices !== 'undefined' && 'getUserMedia' in navigator.mediaDevices), 8 | supportsWebRTCDataChannels: 'RTCPeerConnection' in globalThis 9 | } 10 | -------------------------------------------------------------------------------- /src/temp-dir.browser.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { nanoid } = require('nanoid') 4 | 5 | /** 6 | * Temporary folder 7 | * 8 | * @param {(uuid: string) => string} transform - Transform function to add prefixes or sufixes to the unique id 9 | * @returns {string} - Full real path to a temporary folder 10 | */ 11 | const tempdir = (transform = d => d) => { 12 | return transform(nanoid()) 13 | } 14 | 15 | module.exports = tempdir 16 | -------------------------------------------------------------------------------- /src/temp-dir.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const os = require('os') 5 | const path = require('path') 6 | const { nanoid } = require('nanoid') 7 | 8 | /** 9 | * Temporary folder 10 | * 11 | * @param {(uuid: string) => string} [transform=(p) => p] - Transform function to add prefixes or sufixes to the unique id 12 | * @returns {string} - Full real path to a temporary folder 13 | */ 14 | const tempdir = (transform = d => d) => { 15 | const osTmpDir = fs.realpathSync(os.tmpdir()) 16 | return path.join(osTmpDir, transform(nanoid())) 17 | } 18 | 19 | module.exports = tempdir 20 | -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { Readable as NodeReadableStream } from 'stream' 2 | import type { MtimeLike } from 'ipfs-unixfs' 3 | 4 | interface ProgressStatus { 5 | total: number 6 | loaded: number 7 | lengthComputable: boolean 8 | } 9 | 10 | export interface ProgressFn { (status: ProgressStatus): void } 11 | 12 | type Override = Omit & R 13 | 14 | export type FetchOptions = Override 38 | 39 | export interface HTTPOptions extends FetchOptions { 40 | json?: any 41 | /** 42 | * The base URL to use in case url is a relative URL 43 | */ 44 | base?: string 45 | /** 46 | * Throw not ok responses as Errors 47 | */ 48 | throwHttpErrors?: boolean 49 | /** 50 | * Transform search params 51 | */ 52 | transformSearchParams?: (params: URLSearchParams) => URLSearchParams 53 | /** 54 | * When iterating the response body, transform each chunk with this function. 55 | */ 56 | transform?: (chunk: any) => any 57 | /** 58 | * Handle errors 59 | */ 60 | handleError?: (rsp: Response) => Promise 61 | } 62 | 63 | export interface ExtendedResponse extends Response { 64 | iterator: () => AsyncGenerator 65 | 66 | ndjson: () => AsyncGenerator 67 | } 68 | 69 | export interface GlobSourceOptions { 70 | /** 71 | * Include .dot files in matched paths 72 | */ 73 | hidden?: boolean 74 | 75 | /** 76 | * follow symlinks 77 | */ 78 | followSymlinks?: boolean 79 | 80 | /** 81 | * Preserve mode 82 | */ 83 | preserveMode?: boolean 84 | 85 | /** 86 | * Preserve mtime 87 | */ 88 | preserveMtime?: boolean 89 | 90 | /** 91 | * mode to use - if preserveMode is true this will be ignored 92 | */ 93 | mode?: number 94 | 95 | /** 96 | * mtime to use - if preserveMtime is true this will be ignored 97 | */ 98 | mtime?: MtimeLike 99 | } 100 | 101 | export interface GlobSourceResult { 102 | path: string 103 | content: AsyncIterable | undefined 104 | mode: number | undefined 105 | mtime: MtimeLike | undefined 106 | } 107 | -------------------------------------------------------------------------------- /test/env.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* eslint-env mocha */ 4 | const { expect } = require('aegir/utils/chai') 5 | const env = require('../src/env') 6 | 7 | describe('env', function () { 8 | it('isElectron should have the correct value in each env', function () { 9 | switch (process.env.AEGIR_RUNNER) { 10 | case 'electron-main': 11 | expect(env.isElectron).to.be.true() 12 | break 13 | case 'electron-renderer': 14 | expect(env.isElectron).to.be.true() 15 | break 16 | case 'node': 17 | expect(env.isElectron).to.be.false() 18 | break 19 | case 'browser': 20 | expect(env.isElectron).to.be.false() 21 | break 22 | case 'webworker': 23 | expect(env.isElectron).to.be.false() 24 | break 25 | case 'react-native-android': 26 | expect(env.isElectron).to.be.false() 27 | break 28 | case 'react-native-ios': 29 | expect(env.isElectron).to.be.false() 30 | break 31 | default: 32 | expect.fail(`Could not detect env. Current env is ${process.env.AEGIR_RUNNER}`) 33 | break 34 | } 35 | }) 36 | 37 | it('isElectronMain should have the correct value in each env', function () { 38 | switch (process.env.AEGIR_RUNNER) { 39 | case 'electron-main': 40 | expect(env.isElectronMain).to.be.true() 41 | break 42 | case 'electron-renderer': 43 | expect(env.isElectronMain).to.be.false() 44 | break 45 | case 'node': 46 | expect(env.isElectronMain).to.be.false() 47 | break 48 | case 'browser': 49 | expect(env.isElectronMain).to.be.false() 50 | break 51 | case 'webworker': 52 | expect(env.isElectronMain).to.be.false() 53 | break 54 | case 'react-native-android': 55 | expect(env.isElectronMain).to.be.false() 56 | break 57 | case 'react-native-ios': 58 | expect(env.isElectronMain).to.be.false() 59 | break 60 | default: 61 | expect.fail(`Could not detect env. Current env is ${process.env.AEGIR_RUNNER}`) 62 | break 63 | } 64 | }) 65 | 66 | it('isElectronRenderer should have the correct value in each env', function () { 67 | switch (process.env.AEGIR_RUNNER) { 68 | case 'electron-main': 69 | expect(env.isElectronRenderer).to.be.false() 70 | break 71 | case 'electron-renderer': 72 | expect(env.isElectronRenderer).to.be.true() 73 | break 74 | case 'node': 75 | expect(env.isElectronRenderer).to.be.false() 76 | break 77 | case 'browser': 78 | expect(env.isElectronRenderer).to.be.false() 79 | break 80 | case 'webworker': 81 | expect(env.isElectronRenderer).to.be.false() 82 | break 83 | case 'react-native-android': 84 | expect(env.isElectronRenderer).to.be.false() 85 | break 86 | case 'react-native-ios': 87 | expect(env.isElectronRenderer).to.be.false() 88 | break 89 | default: 90 | expect.fail(`Could not detect env. Current env is ${process.env.AEGIR_RUNNER}`) 91 | break 92 | } 93 | }) 94 | 95 | it('isNode should have the correct value in each env', function () { 96 | switch (process.env.AEGIR_RUNNER) { 97 | case 'electron-main': 98 | expect(env.isNode).to.be.false() 99 | break 100 | case 'electron-renderer': 101 | expect(env.isNode).to.be.false() 102 | break 103 | case 'node': 104 | expect(env.isNode).to.be.true() 105 | break 106 | case 'browser': 107 | expect(env.isNode).to.be.false() 108 | break 109 | case 'webworker': 110 | expect(env.isNode).to.be.false() 111 | break 112 | case 'react-native-android': 113 | expect(env.isNode).to.be.false() 114 | break 115 | case 'react-native-ios': 116 | expect(env.isNode).to.be.false() 117 | break 118 | default: 119 | expect.fail(`Could not detect env. Current env is ${process.env.AEGIR_RUNNER}`) 120 | break 121 | } 122 | }) 123 | 124 | it('isBrowser should have the correct value in each env', function () { 125 | switch (process.env.AEGIR_RUNNER) { 126 | case 'electron-main': 127 | expect(env.isBrowser).to.be.false() 128 | break 129 | case 'electron-renderer': 130 | expect(env.isBrowser).to.be.false() 131 | break 132 | case 'node': 133 | expect(env.isBrowser).to.be.false() 134 | break 135 | case 'browser': 136 | expect(env.isBrowser).to.be.true() 137 | break 138 | case 'webworker': 139 | expect(env.isBrowser).to.be.false() 140 | break 141 | case 'react-native-android': 142 | expect(env.isBrowser).to.be.false() 143 | break 144 | case 'react-native-ios': 145 | expect(env.isBrowser).to.be.false() 146 | break 147 | default: 148 | expect.fail(`Could not detect env. Current env is ${process.env.AEGIR_RUNNER}`) 149 | break 150 | } 151 | }) 152 | 153 | it('isWebWorker should have the correct value in each env', function () { 154 | switch (process.env.AEGIR_RUNNER) { 155 | case 'electron-main': 156 | expect(env.isWebWorker).to.be.false() 157 | break 158 | case 'electron-renderer': 159 | expect(env.isWebWorker).to.be.false() 160 | break 161 | case 'node': 162 | expect(env.isWebWorker).to.be.false() 163 | break 164 | case 'browser': 165 | expect(env.isWebWorker).to.be.false() 166 | break 167 | case 'webworker': 168 | expect(env.isWebWorker).to.be.true() 169 | break 170 | case 'react-native-android': 171 | expect(env.isWebWorker).to.be.false() 172 | break 173 | case 'react-native-ios': 174 | expect(env.isWebWorker).to.be.false() 175 | break 176 | default: 177 | expect.fail(`Could not detect env. Current env is ${process.env.AEGIR_RUNNER}`) 178 | break 179 | } 180 | }) 181 | 182 | it('isReactNative should have the correct value in each env', function () { 183 | switch (process.env.AEGIR_RUNNER) { 184 | case 'electron-main': 185 | expect(env.isReactNative).to.be.false() 186 | break 187 | case 'electron-renderer': 188 | expect(env.isReactNative).to.be.false() 189 | break 190 | case 'node': 191 | expect(env.isReactNative).to.be.false() 192 | break 193 | case 'browser': 194 | expect(env.isReactNative).to.be.false() 195 | break 196 | case 'webworker': 197 | expect(env.isReactNative).to.be.false() 198 | break 199 | case 'react-native-android': 200 | expect(env.isReactNative).to.be.true() 201 | break 202 | case 'react-native-ios': 203 | expect(env.isReactNative).to.be.true() 204 | break 205 | default: 206 | expect.fail(`Could not detect env. Current env is ${process.env.AEGIR_RUNNER}`) 207 | break 208 | } 209 | }) 210 | }) 211 | -------------------------------------------------------------------------------- /test/files/glob-source.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* eslint-env mocha */ 4 | const { expect } = require('aegir/utils/chai') 5 | const globSource = require('../../src/files/glob-source') 6 | const all = require('it-all') 7 | const path = require('path') 8 | const { 9 | isNode 10 | } = require('../../src/env') 11 | const fs = require('fs') 12 | 13 | function fixtureDir () { 14 | return path.resolve(path.join(__dirname, '..', 'fixtures')) 15 | } 16 | 17 | /** 18 | * @param {string} file 19 | */ 20 | function fixture (file) { 21 | return path.resolve(path.join(fixtureDir(), file)) 22 | } 23 | 24 | /** 25 | * @param {string} file 26 | */ 27 | function findMode (file) { 28 | return fs.statSync(fixture(file)).mode 29 | } 30 | 31 | /** 32 | * @param {string} file 33 | */ 34 | function findMtime (file) { 35 | return fs.statSync(fixture(file)).mtime 36 | } 37 | 38 | describe('glob-source', () => { 39 | it('single file, relative path', async function () { 40 | if (!isNode) { 41 | return this.skip() 42 | } 43 | 44 | const result = await all(globSource('./test/fixtures', 'file-0.html')) 45 | 46 | expect(result.length).to.equal(1) 47 | expect(result[0].path).to.equal('/file-0.html') 48 | }) 49 | 50 | it('single file, absolute path', async function () { 51 | if (!isNode) { 52 | return this.skip() 53 | } 54 | 55 | const result = await all(globSource(fixtureDir(), 'file-0.html')) 56 | 57 | expect(result.length).to.equal(1) 58 | expect(result[0].path).to.equal('/file-0.html') 59 | }) 60 | 61 | it('directory, relative path', async function () { 62 | if (!isNode) { 63 | return this.skip() 64 | } 65 | 66 | const result = await all(globSource(fixtureDir(), 'dir/**/*')) 67 | 68 | expect(result).to.have.lengthOf(5) 69 | expect(result).to.containSubset([{ 70 | path: '/dir/file-1.txt' 71 | }, { 72 | path: '/dir/file-2.js' 73 | }, { 74 | path: '/dir/file-3.css' 75 | }, { 76 | path: '/dir/nested-dir' 77 | }, { 78 | path: '/dir/nested-dir/other.txt' 79 | }]) 80 | }) 81 | 82 | it('multiple directories', async function () { 83 | if (!isNode) { 84 | return this.skip() 85 | } 86 | 87 | const result = await all(globSource(fixtureDir(), '{dir/nested-dir,another-dir/another-nested-dir}/**/*')) 88 | 89 | expect(result).to.have.lengthOf(2) 90 | expect(result).to.containSubset([{ 91 | path: '/dir/nested-dir/other.txt' 92 | }, { 93 | path: '/another-dir/another-nested-dir/other.txt' 94 | }]) 95 | }) 96 | 97 | it('directory, hidden files', async function () { 98 | if (!isNode) { 99 | return this.skip() 100 | } 101 | 102 | const result = await all(globSource(fixtureDir(), 'dir/**/*', { 103 | hidden: true 104 | })) 105 | 106 | expect(result).to.have.lengthOf(6) 107 | expect(result).to.containSubset([{ 108 | path: '/dir/.hidden.txt' 109 | }]) 110 | }) 111 | 112 | it('directory, ignore files', async function () { 113 | if (!isNode) { 114 | return this.skip() 115 | } 116 | 117 | const result = await all(globSource(fixtureDir(), 'dir/**/!(file-1.txt)*')) 118 | 119 | expect(result).to.have.lengthOf(4) 120 | expect(result).to.not.containSubset([{ 121 | path: '/dir/file-1.txt' 122 | }]) 123 | }) 124 | 125 | it('multiple paths', async function () { 126 | if (!isNode) { 127 | return this.skip() 128 | } 129 | 130 | const result = await all(globSource(fixture('dir'), 'file-{1,2}.*')) 131 | 132 | expect(result).to.have.lengthOf(2) 133 | expect(result).to.not.containSubset([{ 134 | path: '/dir/file-1.txt' 135 | }, { 136 | path: '/dir/file-2.js' 137 | }]) 138 | }) 139 | 140 | it('preserves mode for directories', async function () { 141 | if (!isNode) { 142 | return this.skip() 143 | } 144 | 145 | const result = await all(globSource(fixtureDir(), '{dir,dir/**/*}', { 146 | preserveMode: true 147 | })) 148 | 149 | expect(result).to.have.lengthOf(6) 150 | expect(result).to.containSubset([{ 151 | path: '/dir', 152 | mode: findMode('/dir') 153 | }, { 154 | path: '/dir/file-1.txt', 155 | mode: findMode('/dir/file-1.txt') 156 | }, { 157 | path: '/dir/file-2.js', 158 | mode: findMode('/dir/file-2.js') 159 | }, { 160 | path: '/dir/file-3.css', 161 | mode: findMode('/dir/file-3.css') 162 | }, { 163 | path: '/dir/nested-dir', 164 | mode: findMode('/dir/nested-dir') 165 | }, { 166 | path: '/dir/nested-dir/other.txt', 167 | mode: findMode('/dir/nested-dir/other.txt') 168 | }]) 169 | }) 170 | 171 | it('overrides mode for directories', async function () { 172 | if (!isNode) { 173 | return this.skip() 174 | } 175 | 176 | const result = await all(globSource(fixtureDir(), '{dir,dir/**/*}', { 177 | mode: 5 178 | })) 179 | 180 | expect(result).to.have.lengthOf(6) 181 | expect(result).to.containSubset([{ 182 | path: '/dir', 183 | mode: 5 184 | }, { 185 | path: '/dir/file-1.txt', 186 | mode: 5 187 | }, { 188 | path: '/dir/file-2.js', 189 | mode: 5 190 | }, { 191 | path: '/dir/file-3.css', 192 | mode: 5 193 | }, { 194 | path: '/dir/nested-dir', 195 | mode: 5 196 | }, { 197 | path: '/dir/nested-dir/other.txt', 198 | mode: 5 199 | }]) 200 | }) 201 | 202 | it('preserves mtime for directories', async function () { 203 | if (!isNode) { 204 | return this.skip() 205 | } 206 | 207 | const result = await all(globSource(fixtureDir(), '{dir,dir/**/*}', { 208 | preserveMtime: true 209 | })) 210 | 211 | expect(result).to.have.lengthOf(6) 212 | expect(result).to.containSubset([{ 213 | path: '/dir', 214 | mtime: findMtime('/dir') 215 | }, { 216 | path: '/dir/file-1.txt', 217 | mtime: findMtime('/dir/file-1.txt') 218 | }, { 219 | path: '/dir/file-2.js', 220 | mtime: findMtime('/dir/file-2.js') 221 | }, { 222 | path: '/dir/file-3.css', 223 | mtime: findMtime('/dir/file-3.css') 224 | }, { 225 | path: '/dir/nested-dir', 226 | mtime: findMtime('/dir/nested-dir') 227 | }, { 228 | path: '/dir/nested-dir/other.txt', 229 | mtime: findMtime('/dir/nested-dir/other.txt') 230 | }]) 231 | }) 232 | 233 | it('overrides mtime for directories', async function () { 234 | if (!isNode) { 235 | return this.skip() 236 | } 237 | 238 | const result = await all(globSource(fixtureDir(), '{dir,dir/**/*}', { 239 | mtime: new Date(5) 240 | })) 241 | 242 | expect(result).to.have.lengthOf(6) 243 | expect(result).to.containSubset([{ 244 | path: '/dir', 245 | mtime: new Date(5) 246 | }, { 247 | path: '/dir/file-1.txt', 248 | mtime: new Date(5) 249 | }, { 250 | path: '/dir/file-2.js', 251 | mtime: new Date(5) 252 | }, { 253 | path: '/dir/file-3.css', 254 | mtime: new Date(5) 255 | }, { 256 | path: '/dir/nested-dir', 257 | mtime: new Date(5) 258 | }, { 259 | path: '/dir/nested-dir/other.txt', 260 | mtime: new Date(5) 261 | }]) 262 | }) 263 | 264 | it('overrides mtime for file with secs/nsecs', async function () { 265 | if (!isNode) { 266 | return this.skip() 267 | } 268 | 269 | const result = await all(globSource(fixture('dir'), 'file-1.txt', { 270 | mtime: { secs: 5, nsecs: 0 } 271 | })) 272 | 273 | expect(result).to.have.deep.nested.property('[0].mtime', { secs: 5, nsecs: 0 }) 274 | }) 275 | 276 | it('overrides mtime for file with hrtime', async function () { 277 | if (!isNode) { 278 | return this.skip() 279 | } 280 | 281 | const result = await all(globSource(fixture('dir'), 'file-1.txt', { 282 | mtime: [5, 0] 283 | })) 284 | 285 | expect(result).to.have.deep.nested.property('[0].mtime', [5, 0]) 286 | }) 287 | 288 | it('overrides mtime for file with UnixFS timespec', async function () { 289 | if (!isNode) { 290 | return this.skip() 291 | } 292 | 293 | const result = await all(globSource(fixture('dir'), 'file-1.txt', { 294 | mtime: { Seconds: 5, FractionalNanoseconds: 0 } 295 | })) 296 | 297 | expect(result).to.have.deep.nested.property('[0].mtime', { Seconds: 5, FractionalNanoseconds: 0 }) 298 | }) 299 | }) 300 | -------------------------------------------------------------------------------- /test/files/url-source.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* eslint-env mocha */ 4 | 5 | const { expect } = require('aegir/utils/chai') 6 | const all = require('it-all') 7 | const urlSource = require('../../src/files/url-source') 8 | const { Buffer } = require('buffer') 9 | 10 | describe('url-source', function () { 11 | it('can get url content', async function () { 12 | const content = 'foo' 13 | const file = urlSource(`${process.env.ECHO_SERVER}/download?data=${content}`) 14 | 15 | expect(file).to.have.property('path', 'download') 16 | 17 | if (file && file.content) { 18 | await expect(all(file.content)).to.eventually.deep.equal([Buffer.from(content)]) 19 | } else { 20 | throw new Error('empty response') 21 | } 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /test/fixtures/another-dir/another-nested-dir/other.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs/js-ipfs-utils/5de9007de2a592e30916d3c4fb5fd019ffa2c1b0/test/fixtures/another-dir/another-nested-dir/other.txt -------------------------------------------------------------------------------- /test/fixtures/another-dir/hello.txt: -------------------------------------------------------------------------------- 1 | hello -------------------------------------------------------------------------------- /test/fixtures/dir/.hidden.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs/js-ipfs-utils/5de9007de2a592e30916d3c4fb5fd019ffa2c1b0/test/fixtures/dir/.hidden.txt -------------------------------------------------------------------------------- /test/fixtures/dir/file-1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs/js-ipfs-utils/5de9007de2a592e30916d3c4fb5fd019ffa2c1b0/test/fixtures/dir/file-1.txt -------------------------------------------------------------------------------- /test/fixtures/dir/file-2.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs/js-ipfs-utils/5de9007de2a592e30916d3c4fb5fd019ffa2c1b0/test/fixtures/dir/file-2.js -------------------------------------------------------------------------------- /test/fixtures/dir/file-3.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs/js-ipfs-utils/5de9007de2a592e30916d3c4fb5fd019ffa2c1b0/test/fixtures/dir/file-3.css -------------------------------------------------------------------------------- /test/fixtures/dir/nested-dir/other.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs/js-ipfs-utils/5de9007de2a592e30916d3c4fb5fd019ffa2c1b0/test/fixtures/dir/nested-dir/other.txt -------------------------------------------------------------------------------- /test/fixtures/file-0.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs/js-ipfs-utils/5de9007de2a592e30916d3c4fb5fd019ffa2c1b0/test/fixtures/file-0.html -------------------------------------------------------------------------------- /test/http.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* eslint-env mocha */ 4 | const { expect } = require('aegir/utils/chai') 5 | const HTTP = require('../src/http') 6 | // @ts-ignore 7 | const toStream = require('it-to-stream') 8 | const delay = require('delay') 9 | const drain = require('it-drain') 10 | const all = require('it-all') 11 | const { isBrowser, isWebWorker, isReactNative } = require('../src/env') 12 | const { Buffer } = require('buffer') 13 | const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string') 14 | const { toString: uint8ArrayToString } = require('uint8arrays/to-string') 15 | const { equals: uint8ArrayEquals } = require('uint8arrays/equals') 16 | const { concat: uint8ArrayConcat } = require('uint8arrays/concat') 17 | const toBuffer = require('it-to-buffer') 18 | 19 | const ECHO_SERVER = process.env.ECHO_SERVER || '' 20 | 21 | describe('http', function () { 22 | it('makes a GET request', async function () { 23 | const req = await HTTP.get(`${ECHO_SERVER}/echo/query?test=one`) 24 | const rsp = await req.json() 25 | expect(rsp).to.be.deep.eq({ test: 'one' }) 26 | }) 27 | 28 | it('makes a GET request with redirect', async function () { 29 | const req = await HTTP.get(`${ECHO_SERVER}/redirect?to=${encodeURI(`${ECHO_SERVER}/echo/query?test=one`)}`) 30 | const rsp = await req.json() 31 | expect(rsp).to.be.deep.eq({ test: 'one' }) 32 | }) 33 | 34 | it('makes a GET request with a really short timeout', function () { 35 | return expect(HTTP.get(`${ECHO_SERVER}/redirect?to=${encodeURI(`${ECHO_SERVER}/echo/query?test=one`)}`, { 36 | timeout: 1 37 | })).to.eventually.be.rejectedWith().instanceOf(HTTP.TimeoutError) 38 | }) 39 | 40 | it('respects headers', async function () { 41 | const req = await HTTP.post(`${ECHO_SERVER}/echo/headers`, { 42 | headers: { 43 | foo: 'bar' 44 | } 45 | }) 46 | const rsp = await req.json() 47 | expect(rsp).to.have.property('foo', 'bar') 48 | }) 49 | 50 | it('respects constructor headers', async function () { 51 | const http = new HTTP({ 52 | headers: { 53 | bar: 'baz' 54 | } 55 | }) 56 | const req = await http.post(`${ECHO_SERVER}/echo/headers`) 57 | const rsp = await req.json() 58 | expect(rsp).to.have.property('bar', 'baz') 59 | }) 60 | 61 | it('makes a JSON request', async () => { 62 | const req = await HTTP.post(`${ECHO_SERVER}/echo`, { 63 | json: { 64 | test: 2 65 | } 66 | }) 67 | 68 | const out = await req.text() 69 | expect(out).to.be.eq('{"test":2}') 70 | }) 71 | 72 | it('makes a ReadableStream request', async () => { 73 | if (globalThis.ReadableStream == null) { 74 | return 75 | } 76 | 77 | const data = 'hello world' 78 | 79 | const body = new globalThis.ReadableStream({ 80 | start (controller) { 81 | controller.enqueue(data) 82 | controller.close() 83 | } 84 | }) 85 | 86 | const req = await HTTP.post(`${ECHO_SERVER}/echo`, { 87 | body 88 | }) 89 | 90 | const out = uint8ArrayToString(await toBuffer(req.iterator())) 91 | expect(out).to.equal('hello world') 92 | }) 93 | 94 | it('makes a DELETE request', async () => { 95 | const req = await HTTP.delete(`${ECHO_SERVER}/echo`, { 96 | json: { 97 | test: 2 98 | } 99 | }) 100 | 101 | const out = await req.text() 102 | expect(out).to.be.eq('{"test":2}') 103 | }) 104 | 105 | it('allow async aborting', async function () { 106 | const controller = new AbortController() 107 | const res = HTTP.get(ECHO_SERVER, { 108 | signal: controller.signal 109 | }) 110 | controller.abort() 111 | 112 | await expect(res).to.eventually.be.rejectedWith(/aborted/i) 113 | }) 114 | 115 | it('parses the response as ndjson', async function () { 116 | const res = await HTTP.post(`${ECHO_SERVER}/echo`, { 117 | body: '{}\n{}' 118 | }) 119 | 120 | const entities = await all(res.ndjson()) 121 | 122 | expect(entities).to.deep.equal([{}, {}]) 123 | }) 124 | 125 | it('parses the response as an async iterable', async function () { 126 | const res = await HTTP.post('echo', { 127 | base: ECHO_SERVER, 128 | body: 'hello world' 129 | }) 130 | 131 | const entities = await all(res.iterator()) 132 | 133 | expect(entities).to.deep.equal([Buffer.from('hello world')]) 134 | }) 135 | 136 | it.skip('should handle errors in streaming bodies', async function () { 137 | if (isBrowser || isWebWorker || isReactNative) { 138 | // streaming bodies not supported by browsers nor by React Native 139 | return this.skip() 140 | } 141 | 142 | const err = new Error('Should be caught') 143 | const body = (async function * () { 144 | yield Buffer.from('{}\n') 145 | 146 | await delay(100) 147 | 148 | throw err 149 | }()) 150 | 151 | const res = await HTTP.post(ECHO_SERVER, { 152 | body: toStream.readable(body) 153 | }) 154 | 155 | await expect(drain(res.ndjson())).to.eventually.be.rejectedWith(/aborted/) 156 | }) 157 | 158 | it.skip('should handle errors in streaming bodies when a signal is passed', async function () { 159 | if (isBrowser || isWebWorker || isReactNative) { 160 | // streaming bodies not supported by browsers nor by React Native 161 | return this.skip() 162 | } 163 | 164 | const controller = new AbortController() 165 | const err = new Error('Should be caught') 166 | const body = (async function * () { 167 | yield Buffer.from('{}\n') 168 | 169 | await delay(100) 170 | 171 | throw err 172 | }()) 173 | const res = await HTTP.post(ECHO_SERVER, { 174 | body: toStream.readable(body), 175 | signal: controller.signal 176 | }) 177 | 178 | await expect(drain(res.ndjson())).to.eventually.be.rejectedWith(/aborted/) 179 | }) 180 | 181 | it('progress events', async function () { 182 | this.timeout(10000) 183 | let upload = 0 184 | const body = new Uint8Array(1000000 / 2) 185 | let progressInfo 186 | const request = await HTTP.post(`${ECHO_SERVER}/echo`, { 187 | body, 188 | headers: { 189 | 'Content-Type': 'application/octet-stream' 190 | }, 191 | onUploadProgress: (progress) => { 192 | progressInfo = progress 193 | upload += 1 194 | } 195 | }) 196 | 197 | const out = uint8ArrayConcat(await all(request.iterator())) 198 | expect(uint8ArrayEquals(out, body)) 199 | 200 | expect(upload).to.be.greaterThan(0) 201 | expect(progressInfo).to.have.property('lengthComputable').to.be.a('boolean') 202 | expect(progressInfo).to.have.property('total').to.be.a('number') 203 | expect(progressInfo).to.have.property('loaded').that.is.greaterThan(0) 204 | }) 205 | 206 | it('makes a GET request with unprintable characters', async function () { 207 | const buf = uint8ArrayFromString('a163666f6f6c6461672d63626f722d626172', 'base16') 208 | const params = Array.from(buf).map(val => `data=${val.toString()}`).join('&') 209 | 210 | const req = await HTTP.get(`${ECHO_SERVER}/download?${params}`) 211 | const rsp = await req.arrayBuffer() 212 | expect(uint8ArrayEquals(new Uint8Array(rsp), buf)).to.be.true() 213 | }) 214 | }) 215 | -------------------------------------------------------------------------------- /test/supports.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* eslint-env mocha */ 4 | const { expect } = require('aegir/utils/chai') 5 | const supports = require('../src/supports') 6 | const env = require('../src/env') 7 | 8 | describe('supports', function () { 9 | it('supportsFileReader should return false in node', function () { 10 | if (env.isNode) { 11 | expect(supports.supportsFileReader).to.be.false() 12 | } else { 13 | this.skip() 14 | } 15 | }) 16 | 17 | it('supportsFileReader should return true in browser', function () { 18 | if (env.isBrowser) { 19 | expect(supports.supportsFileReader).to.be.true() 20 | } else { 21 | this.skip() 22 | } 23 | }) 24 | 25 | it('supportsFileReader should return true in Web Worker', function () { 26 | if (env.isWebWorker) { 27 | expect(supports.supportsFileReader).to.be.true() 28 | } else { 29 | this.skip() 30 | } 31 | }) 32 | 33 | it('supportsFileReader should return false in Electron main', function () { 34 | if (env.isElectron && !env.isElectronRenderer) { 35 | expect(supports.supportsFileReader).to.be.false() 36 | } else { 37 | this.skip() 38 | } 39 | }) 40 | 41 | it('supportsFileReader should return true in Electron renderer', function () { 42 | if (env.isElectronRenderer) { 43 | expect(supports.supportsFileReader).to.be.true() 44 | } else { 45 | this.skip() 46 | } 47 | }) 48 | 49 | it('supportsFileReader should return true in React Native', function () { 50 | if (env.isReactNative) { 51 | expect(supports.supportsFileReader).to.be.true() 52 | } else { 53 | this.skip() 54 | } 55 | }) 56 | 57 | it('supportsWebRTC should return false in node', function () { 58 | if (env.isNode) { 59 | expect(supports.supportsWebRTC).to.be.false() 60 | } else { 61 | this.skip() 62 | } 63 | }) 64 | 65 | it('supportsWebRTC should return true in browser', function () { 66 | if (env.isBrowser) { 67 | expect(supports.supportsWebRTC).to.be.true() 68 | } else { 69 | this.skip() 70 | } 71 | }) 72 | 73 | it('supportsWebRTC should return false in Web Worker', function () { 74 | if (env.isWebWorker) { 75 | expect(supports.supportsWebRTC).to.be.false() 76 | } else { 77 | this.skip() 78 | } 79 | }) 80 | 81 | it('supportsWebRTC should return false in Electron main', function () { 82 | if (env.isElectron && !env.isElectronRenderer) { 83 | expect(supports.supportsWebRTC).to.be.false() 84 | } else { 85 | this.skip() 86 | } 87 | }) 88 | 89 | it('supportsWebRTC should return true in Electron renderer', function () { 90 | if (env.isElectronRenderer) { 91 | expect(supports.supportsWebRTC).to.be.true() 92 | } else { 93 | this.skip() 94 | } 95 | }) 96 | 97 | it('supportsWebRTC should return false in React Native', function () { 98 | if (env.isReactNative) { 99 | expect(supports.supportsWebRTC).to.be.false() 100 | } else { 101 | this.skip() 102 | } 103 | }) 104 | 105 | it('supportsWebRTCDataChannels should return false in node', function () { 106 | if (env.isNode) { 107 | expect(supports.supportsWebRTCDataChannels).to.be.false() 108 | } else { 109 | this.skip() 110 | } 111 | }) 112 | 113 | it('supportsWebRTCDataChannels should return true in browser', function () { 114 | if (env.isBrowser) { 115 | expect(supports.supportsWebRTCDataChannels).to.be.true() 116 | } else { 117 | this.skip() 118 | } 119 | }) 120 | 121 | it('supportsWebRTCDataChannels should return false in Web Worker', function () { 122 | if (env.isWebWorker) { 123 | expect(supports.supportsWebRTCDataChannels).to.be.false() 124 | } else { 125 | this.skip() 126 | } 127 | }) 128 | 129 | it('supportsWebRTCDataChannels should return false in Electron main', function () { 130 | if (env.isElectron && !env.isElectronRenderer) { 131 | expect(supports.supportsWebRTCDataChannels).to.be.false() 132 | } else { 133 | this.skip() 134 | } 135 | }) 136 | 137 | it('supportsWebRTCDataChannels should return true in Electron renderer', function () { 138 | if (env.isElectronRenderer) { 139 | expect(supports.supportsWebRTCDataChannels).to.be.true() 140 | } else { 141 | this.skip() 142 | } 143 | }) 144 | 145 | it('supportsWebRTCDataChannels should return true in React Native', function () { 146 | if (env.isReactNative) { 147 | expect(supports.supportsWebRTCDataChannels).to.be.false() 148 | } else { 149 | this.skip() 150 | } 151 | }) 152 | }) 153 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "aegir/src/config/tsconfig.aegir.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "emitDeclarationOnly": true 6 | }, 7 | "include": [ 8 | "test", 9 | "src" 10 | ] 11 | } 12 | --------------------------------------------------------------------------------