├── .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 | [](https://ipfs.tech)
4 | [](https://discuss.ipfs.tech)
5 | [](https://codecov.io/gh/ipfs/js-ipfs-utils)
6 | [](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://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 |
--------------------------------------------------------------------------------