diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d401a774..589a3341 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,30 @@ version: 2 updates: - package-ecosystem: npm - directory: "/" + directories: + - "/" schedule: interval: daily time: "10:00" open-pull-requests-limit: 20 commit-message: prefix: "deps" - prefix-development: "deps(dev)" + prefix-development: "chore" + groups: + interplanetary-deps: # Helia/libp2p deps + patterns: + - "*helia*" + - "*libp2p*" + - "*multiformats*" + - "*blockstore*" + - "*datastore*" + kubo-deps: # kubo deps + patterns: + - "*kubo*" + - "ipfsd-ctl" +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: chore diff --git a/.github/workflows/generated-pr.yml b/.github/workflows/generated-pr.yml new file mode 100644 index 00000000..b8c5cc63 --- /dev/null +++ b/.github/workflows/generated-pr.yml @@ -0,0 +1,14 @@ +name: Close Generated PRs + +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + uses: ipdxco/unified-github-workflows/.github/workflows/reusable-generated-pr.yml@v1 diff --git a/.github/workflows/js-test-and-release.yml b/.github/workflows/js-test-and-release.yml index 1d7ff79c..28068134 100644 --- a/.github/workflows/js-test-and-release.yml +++ b/.github/workflows/js-test-and-release.yml @@ -19,9 +19,10 @@ concurrency: jobs: js-test-and-release: - uses: pl-strflt/uci/.github/workflows/js-test-and-release.yml@v0.0 + uses: ipdxco/unified-github-workflows/.github/workflows/js-test-and-release.yml@v1.0 secrets: DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} UCI_GITHUB_TOKEN: ${{ secrets.UCI_GITHUB_TOKEN }} + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/semantic-pull-request.yml b/.github/workflows/semantic-pull-request.yml index bd00f090..8c0b9eaf 100644 --- a/.github/workflows/semantic-pull-request.yml +++ b/.github/workflows/semantic-pull-request.yml @@ -9,4 +9,4 @@ on: jobs: main: - uses: pl-strflt/.github/.github/workflows/reusable-semantic-pull-request.yml@v0.3 + uses: ipdxco/unified-github-workflows/.github/workflows/reusable-semantic-pull-request.yml@v1 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 16d65d72..7c955c41 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,8 +1,9 @@ -name: Close and mark stale issue +name: Close Stale Issues on: schedule: - cron: '0 0 * * *' + workflow_dispatch: permissions: issues: write @@ -10,4 +11,4 @@ permissions: jobs: stale: - uses: pl-strflt/.github/.github/workflows/reusable-stale-issue.yml@v0.3 + uses: ipdxco/unified-github-workflows/.github/workflows/reusable-stale-issue.yml@v1 diff --git a/.gitignore b/.gitignore index 7ad9e674..f115d26b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ node_modules package-lock.json yarn.lock .vscode +.tmp-compiled-docs +tsconfig-doc-check.aegir.json diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..ef904d81 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,392 @@ +## 1.0.0 (2024-08-02) + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` +* s3 filenames are now all base32upper +* the output of store.getMany is now a stream of pairs +* open/close/batch/query methods have been removed from store interface, query/batch added to datastore, getAll added to blockstore +* bump multiformats from 10.0.3 to 11.0.0 (#182) +* this module is now ESM only +* deep requires/imports are no longer possible, moves adapters/in-memory impls etc to core packages +* the compliance tests for interface-datastore have been removed - use the interface-datastore-tests module instead + +### Features + +* add all blockstore and datastore implementations ([#197](https://github.com/ipfs/js-stores/issues/197)) ([0d85128](https://github.com/ipfs/js-stores/commit/0d851286d48c357b07df3f7419c1e903ed0e7fac)) +* add black hole stores ([#227](https://github.com/ipfs/js-stores/issues/227)) ([6074f0f](https://github.com/ipfs/js-stores/commit/6074f0fa831abc45b40177ea498a2e0fbb3eeb32)) +* add identity blockstore ([#298](https://github.com/ipfs/js-stores/issues/298)) ([b8dce49](https://github.com/ipfs/js-stores/commit/b8dce49fc005a76b86bca751b7d703d321cd12d6)) +* add in-memory blockstore implementation ([#1](https://github.com/ipfs/js-stores/issues/1)) ([ab37d40](https://github.com/ipfs/js-stores/commit/ab37d40c62875a976eb55054e0d604e237d5a8aa)) +* add Key.asKey method ([#41](https://github.com/ipfs/js-stores/issues/41)) ([783dcc8](https://github.com/ipfs/js-stores/commit/783dcc866e4ca6784d2801a8e18fa1135a137a6b)) +* add tiered blockstore ([#238](https://github.com/ipfs/js-stores/issues/238)) ([5143948](https://github.com/ipfs/js-stores/commit/51439486d5fcd719b9af9182b35565e87da96c99)) +* add unwrap method ([0c22c9f](https://github.com/ipfs/js-stores/commit/0c22c9ff4fe12ac92e38bcfb6ced626077fdb0ed)) +* allow extending store method options ([#193](https://github.com/ipfs/js-stores/issues/193)) ([007e8ac](https://github.com/ipfs/js-stores/commit/007e8ac83a43ec185368cfad57193f57ef700c45)) +* import interface-datastore ([294b249](https://github.com/ipfs/js-stores/commit/294b249de30e6fa80c4246a6a253db0ab493886b)) +* prep for v1 release ([b95a516](https://github.com/ipfs/js-stores/commit/b95a51610738e8ce6b5e29e9769f19f98e525a94)) +* release prep ([b38a533](https://github.com/ipfs/js-stores/commit/b38a53341d84cbef0aee75be149342e74eadfcc6)) +* simplify store interface, move query/batch to datastore, add getAll to blockstore ([#189](https://github.com/ipfs/js-stores/issues/189)) ([0b8f1a0](https://github.com/ipfs/js-stores/commit/0b8f1a0d7644b32395059db250b301d3d5f024cb)) +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + +### Bug Fixes + +* add sharding to s3 blockstore ([#202](https://github.com/ipfs/js-stores/issues/202)) ([e1324a1](https://github.com/ipfs/js-stores/commit/e1324a16dcfae0a39a325e7a9929eb1c2f8ca6c8)) +* bump aegir to 42.2.3, update project config and fix deps ([#297](https://github.com/ipfs/js-stores/issues/297)) ([d521ef2](https://github.com/ipfs/js-stores/commit/d521ef251815527baee0a70705f775c0e47481ad)) +* cleanup references to datastore in blockstores ([#274](https://github.com/ipfs/js-stores/issues/274)) ([f550624](https://github.com/ipfs/js-stores/commit/f5506243b2cb1e6462457241a1614bd5f0755c12)) +* encode/decode blockstore-s3 keys in base32upper ([#201](https://github.com/ipfs/js-stores/issues/201)) ([513fd9c](https://github.com/ipfs/js-stores/commit/513fd9ca35059c422d440ad955d34042c6bc301e)) +* export key properly ([749b656](https://github.com/ipfs/js-stores/commit/749b656c23fb91aa13bfaf9a5ad99703490b24b4)) +* identity blockstore should wrap child ([#303](https://github.com/ipfs/js-stores/issues/303)) ([3d84dd0](https://github.com/ipfs/js-stores/commit/3d84dd0ab164fb5749c34487a217c763d1d09ccb)) +* incorrect export path ([#273](https://github.com/ipfs/js-stores/issues/273)) ([e840ed4](https://github.com/ipfs/js-stores/commit/e840ed4f0e601062c7cb727bd71390381644caa6)) +* increase timeouts ([#131](https://github.com/ipfs/js-stores/issues/131)) ([733c2ed](https://github.com/ipfs/js-stores/commit/733c2edb32a3aa3a54c6cf2d39f780bd6018010b)) +* lower amount ([b7f9aab](https://github.com/ipfs/js-stores/commit/b7f9aab399b026d34521aad5a9c3757baa60cb4d)) +* make datastore batch commit option extension optional ([#194](https://github.com/ipfs/js-stores/issues/194)) ([7bb6729](https://github.com/ipfs/js-stores/commit/7bb6729f616546284c107eaf5dacf12598ea37a0)) +* make tests more stable ([#38](https://github.com/ipfs/js-stores/issues/38)) ([595de43](https://github.com/ipfs/js-stores/commit/595de438cbb5bda7444bdd8c4ce561215855d190)) +* open and close blockstore during interface tests ([#188](https://github.com/ipfs/js-stores/issues/188)) ([a61a54b](https://github.com/ipfs/js-stores/commit/a61a54ba53fa06800e91ea933924615fe6df5b01)) +* publish with limited concurrency ([85bcc4a](https://github.com/ipfs/js-stores/commit/85bcc4acc09d76d7938c55163c81d9b948c53803)) +* readme update ([3bcfb6d](https://github.com/ipfs/js-stores/commit/3bcfb6d311d32a00f24c64cb55c3ba90ca495dba)) +* remove nanoid ([#283](https://github.com/ipfs/js-stores/issues/283)) ([da03ee2](https://github.com/ipfs/js-stores/commit/da03ee29970fdc860eead076ccedb5eea8ff4266)) +* rename test from blocks to getMany ([#190](https://github.com/ipfs/js-stores/issues/190)) ([60e6c3f](https://github.com/ipfs/js-stores/commit/60e6c3f44596ff8fc7906f7d5fe86f8ebdf227d1)) +* replace datastore references in blockstore-s3 ([#214](https://github.com/ipfs/js-stores/issues/214)) ([1a65042](https://github.com/ipfs/js-stores/commit/1a65042cc37aa0f074b64cf070ed32df174ef1a1)) +* restore empty object default ([#228](https://github.com/ipfs/js-stores/issues/228)) ([f82d02c](https://github.com/ipfs/js-stores/commit/f82d02cc8742b595939b58e2eae0a86bb1cec6b1)) +* return key from put and put many ([#196](https://github.com/ipfs/js-stores/issues/196)) ([dfc4697](https://github.com/ipfs/js-stores/commit/dfc4697868d23b3a62154ddda3ae0747e124e3e1)) +* return stream of pairs from getmany ([#195](https://github.com/ipfs/js-stores/issues/195)) ([252bced](https://github.com/ipfs/js-stores/commit/252bced0ad3111711bd502e8d2a5932d6289e0f9)) +* stop namespaced datastore throwing when queried ([#296](https://github.com/ipfs/js-stores/issues/296)) ([9163490](https://github.com/ipfs/js-stores/commit/916349054a3f83a7c9c4bb692edebcce409c7fee)), closes [#236](https://github.com/ipfs/js-stores/issues/236) [#236](https://github.com/ipfs/js-stores/issues/236) +* throw read error on read error ([#304](https://github.com/ipfs/js-stores/issues/304)) ([f14c824](https://github.com/ipfs/js-stores/commit/f14c8249dfd7bbd1385bbf3513b2b3d5e0e6860c)), closes [#299](https://github.com/ipfs/js-stores/issues/299) +* update project config to publish ESM only ([#172](https://github.com/ipfs/js-stores/issues/172)) ([8c9d21f](https://github.com/ipfs/js-stores/commit/8c9d21fdb97f055569984101d573864c808d90d9)) + +### Trivial Changes + +* add blockstore adapter ([05ddff4](https://github.com/ipfs/js-stores/commit/05ddff401ddb2d94f4440e07bfb0405f9ae8795e)) +* add clean script and control published files ([c58873d](https://github.com/ipfs/js-stores/commit/c58873dfd849a5164f64c507ede719342ffe3561)) +* add description ([2aef1ba](https://github.com/ipfs/js-stores/commit/2aef1ba53ba8e0424b18be540bafe42751849124)) +* add modules for interface-blockstore and interface-datastore tests ([da0ac9c](https://github.com/ipfs/js-stores/commit/da0ac9c2218eb0efef79f6455ba671b44b24c9dc)) +* add or force update .github/workflows/js-test-and-release.yml ([#243](https://github.com/ipfs/js-stores/issues/243)) ([7bdc56b](https://github.com/ipfs/js-stores/commit/7bdc56bddd6a58846d86fd7596ef7af81f22cc63)) +* add release script ([c2e11ab](https://github.com/ipfs/js-stores/commit/c2e11ab55dafaf846787e47612e10a855671d317)) +* build before publish and export all blockstore types ([21980fe](https://github.com/ipfs/js-stores/commit/21980fe533dc03ef2e1303058ca1c5cf5a67385a)) +* change version ([f65249d](https://github.com/ipfs/js-stores/commit/f65249d3801a59c0e84217979bf93c7b079bf59f)) +* delete templates [skip ci] ([#242](https://github.com/ipfs/js-stores/issues/242)) ([c0ecb8a](https://github.com/ipfs/js-stores/commit/c0ecb8a76c477115e7fe6f01e41f0491cef6baca)) +* **deps-dev:** bump rimraf from 1.0.9 to 3.0.2 in /packages/interface-datastore-tests ([#40](https://github.com/ipfs/js-stores/issues/40)) ([5d92f17](https://github.com/ipfs/js-stores/commit/5d92f17bd1580bd25e72d70250eabc0ce02a109c)) +* **deps:** bump nanoid from 3.3.4 to 4.0.0 in /packages/interface-datastore ([#110](https://github.com/ipfs/js-stores/issues/110)) ([5e40d9c](https://github.com/ipfs/js-stores/commit/5e40d9c86170fc4c7f0d6c6ee54161327a5493f1)) +* disable tests that stringify cids ([72c93f9](https://github.com/ipfs/js-stores/commit/72c93f90c7204276ad47cd8d18ae07020a1f2c4c)) +* dual licensed ([dbc6615](https://github.com/ipfs/js-stores/commit/dbc66159d3d3a604bb711d4bfb37ff394a3b057c)) +* enable query tests for blockstores ([d2ce541](https://github.com/ipfs/js-stores/commit/d2ce54170c5dbab65882018d9d2e0063e4260779)) +* export options ([00dbf60](https://github.com/ipfs/js-stores/commit/00dbf604e248eaa5bfb6c27a9e0475cdc32bedd5)) +* fix import ([1ab80a7](https://github.com/ipfs/js-stores/commit/1ab80a7131990d8190262e5773f117b3f1d549bc)) +* fix slow ci ([1a6736f](https://github.com/ipfs/js-stores/commit/1a6736fb6e8354b9f94da3bc91a1defc35ebd2c9)) +* initial commit ([e3e73df](https://github.com/ipfs/js-stores/commit/e3e73df19d543afd98bfbf4cbb76537979bd1a3b)) +* publish ([ca0fc50](https://github.com/ipfs/js-stores/commit/ca0fc50134b164e5afc557c5a1aedb7af1623f9c)) +* publish ([c256fa6](https://github.com/ipfs/js-stores/commit/c256fa6c3ca30047fc9edc9f7e7a16e464e5b333)) +* publish ([1b8c053](https://github.com/ipfs/js-stores/commit/1b8c053b925421566adca356d1116e8169691718)) +* publish ([17a18d9](https://github.com/ipfs/js-stores/commit/17a18d9af34a39ea7b066d523893c3254439f50b)) +* publish ([24becc0](https://github.com/ipfs/js-stores/commit/24becc0ba9ba3e75a7ee87a006b67962f6d510b1)) +* publish ([2059883](https://github.com/ipfs/js-stores/commit/2059883fd39cad552baf13ae0fdbb65493bc74ee)) +* publish ([4f99c21](https://github.com/ipfs/js-stores/commit/4f99c21214ce9f1fb6f808c2fe69b5233ee5d31b)) +* publish ([604a2b2](https://github.com/ipfs/js-stores/commit/604a2b2d9e37781627cbc1382c2e19f1fbeb0317)) +* publish ([5073988](https://github.com/ipfs/js-stores/commit/50739882f922000d389ed50d0452dea06c6bb502)) +* publish ([e246d97](https://github.com/ipfs/js-stores/commit/e246d974c52e9087598c5f78c417f25b08811fb9)) +* publish ([f5d59e8](https://github.com/ipfs/js-stores/commit/f5d59e8f70f72a5900504ef4c4cd7af2b69cb7fb)) +* publish ([b4a2514](https://github.com/ipfs/js-stores/commit/b4a251475df060773c9e3fd5218c95a92b6008b8)) +* publish ([6febcf2](https://github.com/ipfs/js-stores/commit/6febcf2a879e6791ea5ecb617ab6c5f98f77847d)) +* publish ([143ba26](https://github.com/ipfs/js-stores/commit/143ba26d8b2c521640bb0c790758ab82bfc6d1a0)) +* publish ([be5e39a](https://github.com/ipfs/js-stores/commit/be5e39a76dd50104bf4fa3e6ec8221ba4decddb5)) +* publish ([f6a0102](https://github.com/ipfs/js-stores/commit/f6a010286de4db5141ce2eb5617e618bafbb1d71)) +* publish ([05e3072](https://github.com/ipfs/js-stores/commit/05e30725cfe5161dbdcdcaacc18b7820108dddcf)) +* publish ([1e1bdd2](https://github.com/ipfs/js-stores/commit/1e1bdd2bae62f2e55b95635e2f42040c62cf65fa)) +* publish ([f55c465](https://github.com/ipfs/js-stores/commit/f55c465df32023e5e3978834ee5aab3e19af212a)) +* publish ([4e8bf56](https://github.com/ipfs/js-stores/commit/4e8bf56afb37619231b08963ffbf04105dbfeae7)) +* publish ([8edd47d](https://github.com/ipfs/js-stores/commit/8edd47d7a0b358e17e68e117089b954857536885)) +* publish ([67d1667](https://github.com/ipfs/js-stores/commit/67d16675fed0c52e2929fa6df19c4dda12987012)) +* publish ([634387a](https://github.com/ipfs/js-stores/commit/634387adab2d83a51472ae22ff01c955bd889dd2)) +* publish ([33020e4](https://github.com/ipfs/js-stores/commit/33020e403c81530df453dc2f7643fe7d79665f8b)) +* publish ([6aa50db](https://github.com/ipfs/js-stores/commit/6aa50dbcdd23f8c5ef3658fa3c2ed9cbc3abf323)) +* publish ([1c1a4ae](https://github.com/ipfs/js-stores/commit/1c1a4ae7035ec0d164636a0b4742f17c063db7bf)) +* readme update ([56e4826](https://github.com/ipfs/js-stores/commit/56e48266d662cb0b83ea52c99c1a1749b242f177)) +* **release:** 0.1.0 [skip ci] ([10476eb](https://github.com/ipfs/js-stores/commit/10476ebdb2f19324e5dae4f1532b3f1e6bbfffc1)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 1.0.0 [skip ci] ([405250f](https://github.com/ipfs/js-stores/commit/405250fc792bf1ebb993b79ed290645fe1f22c11)), closes [#201](https://github.com/ipfs/js-stores/issues/201) [#200](https://github.com/ipfs/js-stores/issues/200) +* **release:** 1.0.1 [skip ci] ([8dfe4e5](https://github.com/ipfs/js-stores/commit/8dfe4e5261d5dfe0f086c81145077f819806d247)), closes [#202](https://github.com/ipfs/js-stores/issues/202) +* **release:** 1.0.10 [skip ci] ([f87dd44](https://github.com/ipfs/js-stores/commit/f87dd444ff0cc27ed81f9ecf19de1d06df4ab03e)), closes [#273](https://github.com/ipfs/js-stores/issues/273) +* **release:** 1.0.11 [skip ci] ([d292758](https://github.com/ipfs/js-stores/commit/d292758334aa3960d30513dd238afab2765f68f5)), closes [#274](https://github.com/ipfs/js-stores/issues/274) +* **release:** 1.0.12 [skip ci] ([33f9197](https://github.com/ipfs/js-stores/commit/33f9197ef9d80018ca8c5fde8694668e90fc0037)), closes [#282](https://github.com/ipfs/js-stores/issues/282) +* **release:** 1.0.14 [skip ci] ([5d5b295](https://github.com/ipfs/js-stores/commit/5d5b295a4d1840810a423c637899b341422c82fd)) +* **release:** 1.0.15 [skip ci] ([e8e3a1e](https://github.com/ipfs/js-stores/commit/e8e3a1eba91f62842c0f3701ce4f56f9a44fa4a9)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 1.0.16 [skip ci] ([9a62492](https://github.com/ipfs/js-stores/commit/9a62492ee04c97cf7ad6f548b72eeae672a5440b)), closes [#308](https://github.com/ipfs/js-stores/issues/308) +* **release:** 1.0.2 [skip ci] ([642c41f](https://github.com/ipfs/js-stores/commit/642c41f7b3b50286b9ed9a448de54faeaa604883)), closes [#213](https://github.com/ipfs/js-stores/issues/213) +* **release:** 1.0.3 [skip ci] ([03c8959](https://github.com/ipfs/js-stores/commit/03c89590acca0c475086d12166efa3bec1e20c1c)), closes [#214](https://github.com/ipfs/js-stores/issues/214) +* **release:** 1.0.4 [skip ci] ([db9a101](https://github.com/ipfs/js-stores/commit/db9a101218b3ec5f7278024f259fdaed5df29873)), closes [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 1.0.5 [skip ci] ([9e43dd3](https://github.com/ipfs/js-stores/commit/9e43dd339560284082a88b42454c023636c65765)), closes [#231](https://github.com/ipfs/js-stores/issues/231) +* **release:** 1.0.6 [skip ci] ([515b378](https://github.com/ipfs/js-stores/commit/515b378b6e35c68e87936e1b190a008e3452a44e)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 1.0.7 [skip ci] ([0be8503](https://github.com/ipfs/js-stores/commit/0be8503a94cc7c2afa9d2a00b4f20c367dc3feb4)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 1.0.8 [skip ci] ([2cd965e](https://github.com/ipfs/js-stores/commit/2cd965ed62254aef02148c5a824b876932d7480f)), closes [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 1.0.9 [skip ci] ([f496d4f](https://github.com/ipfs/js-stores/commit/f496d4fb81149b454c0b3bd6a7e58c3bc1e2508a)), closes [#265](https://github.com/ipfs/js-stores/issues/265) +* **release:** 1.1.0 [skip ci] ([dcfcfd2](https://github.com/ipfs/js-stores/commit/dcfcfd238687541fbc44f54742e45eb2636805e5)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 1.1.0 [skip ci] ([d49cbfb](https://github.com/ipfs/js-stores/commit/d49cbfbc12c46ff2455bb06c85199d81383249ce)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 1.1.0 [skip ci] ([6fa0f7a](https://github.com/ipfs/js-stores/commit/6fa0f7aa79b76db52265d449f0df275b70542465)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 1.1.1 [skip ci] ([6bfda0a](https://github.com/ipfs/js-stores/commit/6bfda0a42dd537c038b4a2d668cd64577b8b0a9a)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 1.1.1 [skip ci] ([5884aa1](https://github.com/ipfs/js-stores/commit/5884aa1698de5ec6c9af39330fc79a0103fd11cf)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 1.1.1 [skip ci] ([2ec12ba](https://github.com/ipfs/js-stores/commit/2ec12ba13df924654312d0120e6a4e1b5dc90771)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#213](https://github.com/ipfs/js-stores/issues/213) +* **release:** 1.1.10 [skip ci] ([7374423](https://github.com/ipfs/js-stores/commit/7374423d9ebc26b895ac78116750439fff39c50e)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 1.1.11 [skip ci] ([3c15bbf](https://github.com/ipfs/js-stores/commit/3c15bbfbcbceea8ddc20db5259eeda125a3604a1)), closes [#306](https://github.com/ipfs/js-stores/issues/306) +* **release:** 1.1.2 [skip ci] ([d089988](https://github.com/ipfs/js-stores/commit/d08998868e5a4c9fb00216e5505bb28d330aca90)), closes [#231](https://github.com/ipfs/js-stores/issues/231) +* **release:** 1.1.2 [skip ci] ([27e5880](https://github.com/ipfs/js-stores/commit/27e588048598f5206e3675b726a09f9c9ff9fa6e)), closes [#231](https://github.com/ipfs/js-stores/issues/231) +* **release:** 1.1.2 [skip ci] ([ab61569](https://github.com/ipfs/js-stores/commit/ab61569a6557cc52758e4801492bf2a3f55d8a1f)), closes [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 1.1.3 [skip ci] ([45ec187](https://github.com/ipfs/js-stores/commit/45ec1876eef13c0d95369c0f2c5ef85f9eac5e7e)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 1.1.3 [skip ci] ([fe95b02](https://github.com/ipfs/js-stores/commit/fe95b021d78fb2f33980646b0a0016b02c669bb3)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 1.1.3 [skip ci] ([61f56da](https://github.com/ipfs/js-stores/commit/61f56da188adfae4a1eaa6d0e9098a18a7411482)), closes [#226](https://github.com/ipfs/js-stores/issues/226) +* **release:** 1.1.4 [skip ci] ([9ee3162](https://github.com/ipfs/js-stores/commit/9ee316256629783b6281542d7073973c4eb14a64)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 1.1.4 [skip ci] ([4258c30](https://github.com/ipfs/js-stores/commit/4258c300e69b70bd4d18b26c93a7164cf38d0465)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 1.1.4 [skip ci] ([3e07e46](https://github.com/ipfs/js-stores/commit/3e07e46e7395f3fe05657bae4cee8d6044000a04)), closes [#231](https://github.com/ipfs/js-stores/issues/231) +* **release:** 1.1.5 [skip ci] ([e2b96fd](https://github.com/ipfs/js-stores/commit/e2b96fdd4b390c32b4341ab2401d609a44db527c)), closes [#245](https://github.com/ipfs/js-stores/issues/245) [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 1.1.5 [skip ci] ([01f0820](https://github.com/ipfs/js-stores/commit/01f08206896b9181ab0a80158d43dc52c7ca6ec8)), closes [#245](https://github.com/ipfs/js-stores/issues/245) [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 1.1.5 [skip ci] ([38883cc](https://github.com/ipfs/js-stores/commit/38883cc9145a1b1e63a1c76c97227a36b2a5ff37)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 1.1.6 [skip ci] ([91e4845](https://github.com/ipfs/js-stores/commit/91e4845e6b431c96d76223f402c42395d4089db6)), closes [#274](https://github.com/ipfs/js-stores/issues/274) +* **release:** 1.1.6 [skip ci] ([c786bdc](https://github.com/ipfs/js-stores/commit/c786bdc58a173910865ad2cd5c1544b17ecaf3a4)), closes [#274](https://github.com/ipfs/js-stores/issues/274) +* **release:** 1.1.6 [skip ci] ([2f7be23](https://github.com/ipfs/js-stores/commit/2f7be23d7d8897a9ab54eba564bcf492ecbc89c5)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 1.1.7 [skip ci] ([2401e95](https://github.com/ipfs/js-stores/commit/2401e9572cd3daf3ae1dca723dbbbb4997143f43)), closes [#281](https://github.com/ipfs/js-stores/issues/281) [#286](https://github.com/ipfs/js-stores/issues/286) +* **release:** 1.1.7 [skip ci] ([3423745](https://github.com/ipfs/js-stores/commit/34237454fae8adcf2dd07cde3f8fc23ae83d04ea)), closes [#286](https://github.com/ipfs/js-stores/issues/286) +* **release:** 1.1.7 [skip ci] ([20907ff](https://github.com/ipfs/js-stores/commit/20907ffb5813995bba89a8015f36003775b08e3e)), closes [#245](https://github.com/ipfs/js-stores/issues/245) [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 1.1.8 [skip ci] ([a5f10ba](https://github.com/ipfs/js-stores/commit/a5f10badf593669380edd6b0035b67774161c9b7)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 1.1.8 [skip ci] ([479ba70](https://github.com/ipfs/js-stores/commit/479ba709ca54f9b6f7aadec87e6699b2d87f9992)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 1.1.8 [skip ci] ([48b6a70](https://github.com/ipfs/js-stores/commit/48b6a70ba24c8c98829d1017e77cc094621305c3)), closes [#274](https://github.com/ipfs/js-stores/issues/274) +* **release:** 1.1.9 [skip ci] ([5307449](https://github.com/ipfs/js-stores/commit/530744952275f7be66ca53f3a3b58c81d2057cd4)), closes [#286](https://github.com/ipfs/js-stores/issues/286) +* **release:** 10.0.0 [skip ci] ([ae52bfc](https://github.com/ipfs/js-stores/commit/ae52bfc5aa6503ac656ed6312694e2e0445a53dd)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 10.0.0 [skip ci] ([89aa5e1](https://github.com/ipfs/js-stores/commit/89aa5e1b33e6ba7b1db4605155cd66aa56181f8b)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 10.1.0 [skip ci] ([b500ee8](https://github.com/ipfs/js-stores/commit/b500ee87f1dcc32ade8bba95a539b57604fa1b06)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 10.1.1 [skip ci] ([42fd4f3](https://github.com/ipfs/js-stores/commit/42fd4f3be0fc0152cdaa0e0d4cde8d510ced29e6)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#213](https://github.com/ipfs/js-stores/issues/213) +* **release:** 10.1.2 [skip ci] ([cdc3f04](https://github.com/ipfs/js-stores/commit/cdc3f045cc487277b9d9a37817b55cd4cd444eb3)), closes [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 10.1.3 [skip ci] ([b02129c](https://github.com/ipfs/js-stores/commit/b02129cbd367deab0cf199620605012d5af07f0d)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 10.1.4 [skip ci] ([ce5247b](https://github.com/ipfs/js-stores/commit/ce5247b57f8a00c1cc0f204760ad984339115396)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 10.1.5 [skip ci] ([50763d0](https://github.com/ipfs/js-stores/commit/50763d0ae947918aa9a8da05332a37d2229a6412)), closes [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 10.1.6 [skip ci] ([67541e9](https://github.com/ipfs/js-stores/commit/67541e94f0c4579e782d732a5db590a0cc2a015c)) +* **release:** 10.1.7 [skip ci] ([bf0b007](https://github.com/ipfs/js-stores/commit/bf0b0074045dcfe460db115021cd48346694a211)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 10.1.8 [skip ci] ([92d5933](https://github.com/ipfs/js-stores/commit/92d59330bc539e3134f285c3d8d60b4468f84ace)), closes [#299](https://github.com/ipfs/js-stores/issues/299) +* **release:** 11.0.0 [skip ci] ([2cd66f5](https://github.com/ipfs/js-stores/commit/2cd66f5b07476946c25fe7fa915ecdd02bb824a2)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 11.1.0 [skip ci] ([cfcb39d](https://github.com/ipfs/js-stores/commit/cfcb39d33ece55af7aa6c13965e70b1e79f722f1)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 11.1.1 [skip ci] ([5f60f27](https://github.com/ipfs/js-stores/commit/5f60f278449a856cc8b55c5722fe4233eab0aa4b)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#213](https://github.com/ipfs/js-stores/issues/213) +* **release:** 11.1.10 [skip ci] ([a9d62bd](https://github.com/ipfs/js-stores/commit/a9d62bd1ed9d10e603211fb279b9d7dec4bcebc5)) +* **release:** 11.1.11 [skip ci] ([7353d28](https://github.com/ipfs/js-stores/commit/7353d280ae23f8040cf26cc60388799491fd399f)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 11.1.12 [skip ci] ([7877a67](https://github.com/ipfs/js-stores/commit/7877a67817dcc46e4d48904dd576eb728700cb76)), closes [#308](https://github.com/ipfs/js-stores/issues/308) +* **release:** 11.1.2 [skip ci] ([b3148e9](https://github.com/ipfs/js-stores/commit/b3148e9dc9a477072ff2562c8a99e0b886ce339c)), closes [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 11.1.3 [skip ci] ([d8999ba](https://github.com/ipfs/js-stores/commit/d8999ba0b992b83f7f439732f5514f5fc8eb93a2)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 11.1.4 [skip ci] ([8083f44](https://github.com/ipfs/js-stores/commit/8083f448aa447fc71949c54514d2c1b05207109c)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 11.1.5 [skip ci] ([a9218cd](https://github.com/ipfs/js-stores/commit/a9218cddfed1c25995927d060fdc97e85845b83a)), closes [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 11.1.6 [skip ci] ([17a8e76](https://github.com/ipfs/js-stores/commit/17a8e76ad30eb57c0d05a33b9e36ef9da670a9e0)), closes [#265](https://github.com/ipfs/js-stores/issues/265) +* **release:** 11.1.7 [skip ci] ([663ae05](https://github.com/ipfs/js-stores/commit/663ae051d6b23ed76bf7533db767f6de183690a5)), closes [#273](https://github.com/ipfs/js-stores/issues/273) +* **release:** 11.1.8 [skip ci] ([c5c79d0](https://github.com/ipfs/js-stores/commit/c5c79d098109ee58e1fa2698569e9ce452013b11)), closes [#274](https://github.com/ipfs/js-stores/issues/274) +* **release:** 11.1.9 [skip ci] ([edb7c80](https://github.com/ipfs/js-stores/commit/edb7c80b5e24b34c53e2d1eda1d1a96a02d19048)), closes [#282](https://github.com/ipfs/js-stores/issues/282) +* **release:** 12.0.0 [skip ci] ([8ef4ed4](https://github.com/ipfs/js-stores/commit/8ef4ed48537ced54cf8c1ea47fb796f942b97241)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 2.0.0 [skip ci] ([a8dce3e](https://github.com/ipfs/js-stores/commit/a8dce3e53d18bf8b9499f85862b9fc182424ec19)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 2.0.0 [skip ci] ([5b8f461](https://github.com/ipfs/js-stores/commit/5b8f4615ba5cab9737a0582af6a8486bcc3b02e0)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 2.0.0 [skip ci] ([2368fb3](https://github.com/ipfs/js-stores/commit/2368fb35cadcaeeaa5c52df9d95f18fc3d5bda64)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 2.0.0 [skip ci] ([4f4b9ee](https://github.com/ipfs/js-stores/commit/4f4b9ee96dd1195f213391198f4cc62e08281262)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 2.1.0 [skip ci] ([6c92ec7](https://github.com/ipfs/js-stores/commit/6c92ec73b82eac0bf446718f1b1d24fb9b740ce6)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 2.1.1 [skip ci] ([351c496](https://github.com/ipfs/js-stores/commit/351c49680be94a162153b6d71858382ae79cf0e7)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#213](https://github.com/ipfs/js-stores/issues/213) +* **release:** 2.1.2 [skip ci] ([1b2c5e7](https://github.com/ipfs/js-stores/commit/1b2c5e78b6c6c42f0bf100574c72789f83998c2c)), closes [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 2.1.3 [skip ci] ([c1091f3](https://github.com/ipfs/js-stores/commit/c1091f37cacc826d8f6130a73dec7ea6cea81d2a)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 2.1.4 [skip ci] ([03121dc](https://github.com/ipfs/js-stores/commit/03121dcd4db817b58d7304b179fbeb21d5a18a03)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 2.1.5 [skip ci] ([bf900b9](https://github.com/ipfs/js-stores/commit/bf900b969cd18b9f3e83ff0b87d04f61af7614b0)), closes [#245](https://github.com/ipfs/js-stores/issues/245) [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 2.1.6 [skip ci] ([1ca88ca](https://github.com/ipfs/js-stores/commit/1ca88ca943756ea3bc6a465c3267b1bed2738789)), closes [#274](https://github.com/ipfs/js-stores/issues/274) +* **release:** 2.1.7 [skip ci] ([191ab3d](https://github.com/ipfs/js-stores/commit/191ab3d3e093ea5b7dd106a6ff66f4a6e01b2568)), closes [#281](https://github.com/ipfs/js-stores/issues/281) +* **release:** 2.1.8 [skip ci] ([135a09b](https://github.com/ipfs/js-stores/commit/135a09b0ae32beb6a6f51c8239f0800a01e9c95b)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 2.1.9 [skip ci] ([81a54e7](https://github.com/ipfs/js-stores/commit/81a54e73eec48a8a29723ea0a74ce8d7ac17d92e)), closes [#299](https://github.com/ipfs/js-stores/issues/299) +* **release:** 3.0.0 [skip ci] ([cc92722](https://github.com/ipfs/js-stores/commit/cc9272215453b23b72cec2e43f8de7f18d2ff703)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 3.0.1 [skip ci] ([62dd620](https://github.com/ipfs/js-stores/commit/62dd620a03be88a74844fb1da32c7937542dc689)), closes [#175](https://github.com/ipfs/js-stores/issues/175) +* **release:** 3.0.1 [skip ci] ([0a73ce9](https://github.com/ipfs/js-stores/commit/0a73ce9d30d77cdb4cfebdaa7f1381668818d39e)), closes [#174](https://github.com/ipfs/js-stores/issues/174) [#175](https://github.com/ipfs/js-stores/issues/175) +* **release:** 3.0.1 [skip ci] ([d896eec](https://github.com/ipfs/js-stores/commit/d896eec3387e60b47367ed92e10f19914a48fd27)), closes [#174](https://github.com/ipfs/js-stores/issues/174) [#175](https://github.com/ipfs/js-stores/issues/175) +* **release:** 3.0.1 [skip ci] ([75f3006](https://github.com/ipfs/js-stores/commit/75f3006a4ba7b226b0946902356b27fa264431bb)), closes [#175](https://github.com/ipfs/js-stores/issues/175) +* **release:** 3.0.2 [skip ci] ([5a24743](https://github.com/ipfs/js-stores/commit/5a2474328c0260060fe965fc88b54eb3a0df931a)), closes [#181](https://github.com/ipfs/js-stores/issues/181) +* **release:** 3.0.2 [skip ci] ([7c6d0de](https://github.com/ipfs/js-stores/commit/7c6d0de3cb5e329deadc3d9cd5abedfe9fc69d6c)), closes [#181](https://github.com/ipfs/js-stores/issues/181) +* **release:** 3.0.2 [skip ci] ([09aa693](https://github.com/ipfs/js-stores/commit/09aa693dab68009890665ffa4e95e630a0912c0b)), closes [#181](https://github.com/ipfs/js-stores/issues/181) +* **release:** 3.0.2 [skip ci] ([4f9fad1](https://github.com/ipfs/js-stores/commit/4f9fad1c48007ee9d761eed7a9b91b05661f95ef)), closes [#181](https://github.com/ipfs/js-stores/issues/181) +* **release:** 3.0.3 [skip ci] ([20a044d](https://github.com/ipfs/js-stores/commit/20a044d9591612611261f027bef93e707933b9af)), closes [#183](https://github.com/ipfs/js-stores/issues/183) +* **release:** 3.0.3 [skip ci] ([102a8bd](https://github.com/ipfs/js-stores/commit/102a8bd0662afa6fb38d675bae9c159c431c0151)), closes [#28](https://github.com/ipfs/js-stores/issues/28) [#28](https://github.com/ipfs/js-stores/issues/28) [#27](https://github.com/ipfs/js-stores/issues/27) [#24](https://github.com/ipfs/js-stores/issues/24) [#28](https://github.com/ipfs/js-stores/issues/28) [#28](https://github.com/ipfs/js-stores/issues/28) [#27](https://github.com/ipfs/js-stores/issues/27) [#24](https://github.com/ipfs/js-stores/issues/24) [#183](https://github.com/ipfs/js-stores/issues/183) +* **release:** 3.0.3 [skip ci] ([88363b0](https://github.com/ipfs/js-stores/commit/88363b0361358e27676263430da496482925d2cf)), closes [#28](https://github.com/ipfs/js-stores/issues/28) [#28](https://github.com/ipfs/js-stores/issues/28) [#27](https://github.com/ipfs/js-stores/issues/27) [#24](https://github.com/ipfs/js-stores/issues/24) [#28](https://github.com/ipfs/js-stores/issues/28) [#28](https://github.com/ipfs/js-stores/issues/28) [#27](https://github.com/ipfs/js-stores/issues/27) [#24](https://github.com/ipfs/js-stores/issues/24) [#28](https://github.com/ipfs/js-stores/issues/28) [#183](https://github.com/ipfs/js-stores/issues/183) +* **release:** 3.0.4 [skip ci] ([13bc59c](https://github.com/ipfs/js-stores/commit/13bc59c78c4ce5c9dfe13d806ce3fb8d5e1f5e55)), closes [#184](https://github.com/ipfs/js-stores/issues/184) +* **release:** 3.0.4 [skip ci] ([47ce4d7](https://github.com/ipfs/js-stores/commit/47ce4d7a64db63ae36a2a13ad2999f800f3ec3e9)) +* **release:** 3.0.5 [skip ci] ([62aaee2](https://github.com/ipfs/js-stores/commit/62aaee205159135ea744c7e661a8614ed6c422ac)), closes [#184](https://github.com/ipfs/js-stores/issues/184) +* **release:** 4.0.0 [skip ci] ([1d1079c](https://github.com/ipfs/js-stores/commit/1d1079c510799cc2f84f4f78730a8cb691f6c2b8)), closes [#189](https://github.com/ipfs/js-stores/issues/189) +* **release:** 4.0.0 [skip ci] ([756cc83](https://github.com/ipfs/js-stores/commit/756cc8337c5024623f61a5f9c38d13e89b93e989)), closes [#189](https://github.com/ipfs/js-stores/issues/189) +* **release:** 4.0.0 [skip ci] ([2207d7a](https://github.com/ipfs/js-stores/commit/2207d7a81387bb0c41320bc52cd2c5ed32a44c45)), closes [#182](https://github.com/ipfs/js-stores/issues/182) [#234](https://github.com/ipfs/js-stores/issues/234) [#226](https://github.com/ipfs/js-stores/issues/226) [#234](https://github.com/ipfs/js-stores/issues/234) [#226](https://github.com/ipfs/js-stores/issues/226) [#226](https://github.com/ipfs/js-stores/issues/226) +* **release:** 4.0.0 [skip ci] ([fd6a8aa](https://github.com/ipfs/js-stores/commit/fd6a8aaecc724daf80bd0f2dfbef29031e5b82fe)), closes [#182](https://github.com/ipfs/js-stores/issues/182) [#183](https://github.com/ipfs/js-stores/issues/183) [#234](https://github.com/ipfs/js-stores/issues/234) [#226](https://github.com/ipfs/js-stores/issues/226) [#234](https://github.com/ipfs/js-stores/issues/234) [#226](https://github.com/ipfs/js-stores/issues/226) [#226](https://github.com/ipfs/js-stores/issues/226) +* **release:** 4.0.1 [skip ci] ([8e69010](https://github.com/ipfs/js-stores/commit/8e6901010f2600a46cb426a05d0d9b7753f7dc3a)), closes [#184](https://github.com/ipfs/js-stores/issues/184) +* **release:** 4.0.1 [skip ci] ([e8d30c0](https://github.com/ipfs/js-stores/commit/e8d30c0131016d818c791754ffa587a7d3c13466)), closes [#184](https://github.com/ipfs/js-stores/issues/184) +* **release:** 4.0.2 [skip ci] ([54f3eee](https://github.com/ipfs/js-stores/commit/54f3eee5aa128c2ef2cde192037fa694dbd7a73a)), closes [#188](https://github.com/ipfs/js-stores/issues/188) +* **release:** 4.1.0 [skip ci] ([df06919](https://github.com/ipfs/js-stores/commit/df06919855f16d0cdad1892c5ef56af037020f06)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 4.1.0 [skip ci] ([5ed90c4](https://github.com/ipfs/js-stores/commit/5ed90c40e86cae04095fec8624a5c87c51dd0ed5)), closes [#193](https://github.com/ipfs/js-stores/issues/193) +* **release:** 4.1.1 [skip ci] ([346ee0f](https://github.com/ipfs/js-stores/commit/346ee0fc9ec164586704b861ead4b774aae42ae8)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 4.2.0 [skip ci] ([ceb244c](https://github.com/ipfs/js-stores/commit/ceb244c0bbbcae8b36de36c3617e544c1ff6465c)), closes [#227](https://github.com/ipfs/js-stores/issues/227) +* **release:** 4.3.0 [skip ci] ([725e0dd](https://github.com/ipfs/js-stores/commit/725e0dd8f7e4c9dfa6857b54f3cf7d97ff3a56e2)), closes [#238](https://github.com/ipfs/js-stores/issues/238) +* **release:** 4.3.1 [skip ci] ([27f88c0](https://github.com/ipfs/js-stores/commit/27f88c0cf4d6da1e61633b9564305fd8dbbd496a)), closes [#240](https://github.com/ipfs/js-stores/issues/240) +* **release:** 4.3.10 [skip ci] ([d5bdd95](https://github.com/ipfs/js-stores/commit/d5bdd953e0c6b5ea2f69c68447aa119567abd8db)) +* **release:** 4.3.11 [skip ci] ([6173c27](https://github.com/ipfs/js-stores/commit/6173c27a1a66fd106798f2a41aae2458765ad779)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 4.3.2 [skip ci] ([bad53b9](https://github.com/ipfs/js-stores/commit/bad53b98e5361cb44e24ee057e7d7ad8fcd90c48)), closes [#231](https://github.com/ipfs/js-stores/issues/231) +* **release:** 4.3.3 [skip ci] ([04510b1](https://github.com/ipfs/js-stores/commit/04510b149e954442a0ff983c0fa6404cff69f794)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 4.3.4 [skip ci] ([d8552a2](https://github.com/ipfs/js-stores/commit/d8552a2181b409990e44a3fa45cc67c2f883a21e)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 4.3.5 [skip ci] ([c961d00](https://github.com/ipfs/js-stores/commit/c961d009857aec77031107e04040aa99bfa7db5b)), closes [#245](https://github.com/ipfs/js-stores/issues/245) [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 4.3.6 [skip ci] ([a67f66a](https://github.com/ipfs/js-stores/commit/a67f66a205b1326ec69335a3b5211421c70cab1e)), closes [#280](https://github.com/ipfs/js-stores/issues/280) +* **release:** 4.3.7 [skip ci] ([ee2203a](https://github.com/ipfs/js-stores/commit/ee2203a2c7ea14dc73286f58feb5e0af9fb9075c)), closes [#274](https://github.com/ipfs/js-stores/issues/274) +* **release:** 4.3.8 [skip ci] ([e3ecb03](https://github.com/ipfs/js-stores/commit/e3ecb03a033bc31d1bc6e7950c1612e3ebf3ef30)), closes [#282](https://github.com/ipfs/js-stores/issues/282) +* **release:** 4.4.0 [skip ci] ([58c48eb](https://github.com/ipfs/js-stores/commit/58c48eba21ad1e54bf4a2e49d95bb2f55e1218d3)), closes [#298](https://github.com/ipfs/js-stores/issues/298) +* **release:** 4.4.1 [skip ci] ([2e8f8db](https://github.com/ipfs/js-stores/commit/2e8f8db26b07d24ff0f56ce82eb7ce47a918d492)), closes [#303](https://github.com/ipfs/js-stores/issues/303) +* **release:** 5.0.0 [skip ci] ([225da7e](https://github.com/ipfs/js-stores/commit/225da7ebae152f59d11c125f3adc24d3ef6b6e95)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 5.0.0 [skip ci] ([5230741](https://github.com/ipfs/js-stores/commit/523074172b590f6708f92f2e3f8e94317cf565dc)), closes [#196](https://github.com/ipfs/js-stores/issues/196) [#195](https://github.com/ipfs/js-stores/issues/195) +* **release:** 5.0.0 [skip ci] ([3d69492](https://github.com/ipfs/js-stores/commit/3d6949202b8bdf1e923429ec9114640e972ffd5f)), closes [#195](https://github.com/ipfs/js-stores/issues/195) +* **release:** 5.0.0 [skip ci] ([44b12bd](https://github.com/ipfs/js-stores/commit/44b12bd8972607a2907ef25955d5c5732098acf8)), closes [#189](https://github.com/ipfs/js-stores/issues/189) +* **release:** 5.0.0 [skip ci] ([9c25d3f](https://github.com/ipfs/js-stores/commit/9c25d3fa41a774aae2da985b23309e6248c6bbd9)), closes [#189](https://github.com/ipfs/js-stores/issues/189) +* **release:** 5.0.1 [skip ci] ([060e74b](https://github.com/ipfs/js-stores/commit/060e74bf4b3ed23b4b223422f871f1ce612e2cb1)), closes [#196](https://github.com/ipfs/js-stores/issues/196) +* **release:** 5.0.1 [skip ci] ([c1c6add](https://github.com/ipfs/js-stores/commit/c1c6add3474446138939db38f24ebb3cb4922035)), closes [#190](https://github.com/ipfs/js-stores/issues/190) +* **release:** 5.1.0 [skip ci] ([30735a2](https://github.com/ipfs/js-stores/commit/30735a273af3418c5dae30cf754c1e187abc8184)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 5.1.0 [skip ci] ([a571532](https://github.com/ipfs/js-stores/commit/a5715323a8694dfda7d8ec0411da76fe024a36fc)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 5.1.0 [skip ci] ([e24ce0b](https://github.com/ipfs/js-stores/commit/e24ce0b558d62078617335f5c453106b57693c41)), closes [#193](https://github.com/ipfs/js-stores/issues/193) +* **release:** 5.1.1 [skip ci] ([2a1d9bd](https://github.com/ipfs/js-stores/commit/2a1d9bda4158ff740e3515f995061e2c531c591f)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 5.1.1 [skip ci] ([146bc5c](https://github.com/ipfs/js-stores/commit/146bc5c87d5376d1d758c2ee3f3f4bdee2e4c267)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#213](https://github.com/ipfs/js-stores/issues/213) +* **release:** 5.1.1 [skip ci] ([9db13e0](https://github.com/ipfs/js-stores/commit/9db13e0435730e3147d2e3bcde301f6bac1563f7)) +* **release:** 5.1.2 [skip ci] ([f32c9a4](https://github.com/ipfs/js-stores/commit/f32c9a46340eee3498967fa918c31d03885ffd91)), closes [#228](https://github.com/ipfs/js-stores/issues/228) +* **release:** 5.1.2 [skip ci] ([970f81d](https://github.com/ipfs/js-stores/commit/970f81d00f753aed885aceff6ff6abcd9163e69e)), closes [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 5.1.3 [skip ci] ([fc95c0f](https://github.com/ipfs/js-stores/commit/fc95c0f555a1e3b02f3875047f2badad3ebc0d43)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 5.1.3 [skip ci] ([a970ceb](https://github.com/ipfs/js-stores/commit/a970ceb4e6cb0531e994b26fc1c9852fca8df897)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 5.1.4 [skip ci] ([596fde8](https://github.com/ipfs/js-stores/commit/596fde8f16f51bc947e3907150e110c4f62d007b)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 5.1.4 [skip ci] ([238872f](https://github.com/ipfs/js-stores/commit/238872fa0c5c09a0f229d851c56cd6a2933951de)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 5.1.5 [skip ci] ([9997757](https://github.com/ipfs/js-stores/commit/9997757101a34dbd61c71841932433296b7f4038)), closes [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 5.1.5 [skip ci] ([980c8c6](https://github.com/ipfs/js-stores/commit/980c8c69001bc2570884bc9574909215de091bc7)), closes [#245](https://github.com/ipfs/js-stores/issues/245) [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 5.1.6 [skip ci] ([3aee60f](https://github.com/ipfs/js-stores/commit/3aee60ff311be560ae1e8b8f5c304d32df926e40)) +* **release:** 5.1.6 [skip ci] ([55c5855](https://github.com/ipfs/js-stores/commit/55c5855091ddc426a68cea22453e58bdee05f808)), closes [#282](https://github.com/ipfs/js-stores/issues/282) +* **release:** 5.1.7 [skip ci] ([dd7cb8c](https://github.com/ipfs/js-stores/commit/dd7cb8c8c2ab231894462c89ab22e6e37267cdc9)) +* **release:** 5.1.7 [skip ci] ([8f08f7f](https://github.com/ipfs/js-stores/commit/8f08f7f978a9bf8b585d364dddd8cb3e56d85b02)) +* **release:** 5.1.8 [skip ci] ([e151fcd](https://github.com/ipfs/js-stores/commit/e151fcd0938f55ec1b16c51fd25107a23d6a5871)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 5.1.8 [skip ci] ([3494590](https://github.com/ipfs/js-stores/commit/34945900e6f7d29f8998a6674e1049e432d3e64f)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 5.2.0 [skip ci] ([496c177](https://github.com/ipfs/js-stores/commit/496c1770ff96a072655236597d492aab260cacd4)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 5.2.1 [skip ci] ([b40b32e](https://github.com/ipfs/js-stores/commit/b40b32effe26508e62b7b84cbc4ffa9bbcee6753)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 5.2.10 [skip ci] ([235ed35](https://github.com/ipfs/js-stores/commit/235ed35861f17d17b2f042fc63e2981a6335e908)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 5.2.2 [skip ci] ([7d70be5](https://github.com/ipfs/js-stores/commit/7d70be55d69cf988991d9af6bae014066a31f2c2)), closes [#224](https://github.com/ipfs/js-stores/issues/224) +* **release:** 5.2.3 [skip ci] ([eb59834](https://github.com/ipfs/js-stores/commit/eb59834bfb29a7b61fb361ae2ecc90d3d19b908b)), closes [#228](https://github.com/ipfs/js-stores/issues/228) +* **release:** 5.2.4 [skip ci] ([20faf4a](https://github.com/ipfs/js-stores/commit/20faf4a68b4560d648cd92fd387b5a40ced37dff)), closes [#231](https://github.com/ipfs/js-stores/issues/231) +* **release:** 5.2.5 [skip ci] ([0876da6](https://github.com/ipfs/js-stores/commit/0876da6fbf3eb63ba19d1e5e71835342615eba91)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 5.2.6 [skip ci] ([50496de](https://github.com/ipfs/js-stores/commit/50496de5a85abfbfc59003f3e216fe53babb264c)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 5.2.7 [skip ci] ([866b4c5](https://github.com/ipfs/js-stores/commit/866b4c581d110955a07b9c80b4af849f0fc5719f)), closes [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 5.2.9 [skip ci] ([2680073](https://github.com/ipfs/js-stores/commit/2680073d393d2763feaa5c21053fac234097d7c8)) +* **release:** 5.2.9 [skip ci] ([e183a4e](https://github.com/ipfs/js-stores/commit/e183a4ea2ac0b1bd585d6673d80827035e0d8d6e)) +* **release:** 5.3.0 [skip ci] ([49e0618](https://github.com/ipfs/js-stores/commit/49e06180f2061cd6b293596d9d0df76f3d45020e)) +* **release:** 6.0.0 [skip ci] ([2f24f58](https://github.com/ipfs/js-stores/commit/2f24f583138ce3934b020e504c35a276d143456b)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 6.0.0 [skip ci] ([c097d58](https://github.com/ipfs/js-stores/commit/c097d58d0f7ee39aecae9cce0c1bf0069215b6df)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 6.0.0 [skip ci] ([96958c0](https://github.com/ipfs/js-stores/commit/96958c08bf728c536663e569d44011487065f3f7)), closes [#196](https://github.com/ipfs/js-stores/issues/196) [#195](https://github.com/ipfs/js-stores/issues/195) +* **release:** 6.1.0 [skip ci] ([1d518f6](https://github.com/ipfs/js-stores/commit/1d518f6894a4ebfc8538390b718fb77061a879bd)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 6.1.1 [skip ci] ([d3c1041](https://github.com/ipfs/js-stores/commit/d3c10416f5bab8c067adc7ee714fd2809b34e558)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#213](https://github.com/ipfs/js-stores/issues/213) +* **release:** 6.1.10 [skip ci] ([03fd5d3](https://github.com/ipfs/js-stores/commit/03fd5d3020989157493a95720d85a9ee00e12572)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 6.1.2 [skip ci] ([6d0043c](https://github.com/ipfs/js-stores/commit/6d0043cceaab83a6065d5a287b24394db6db2473)), closes [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 6.1.3 [skip ci] ([72b6e10](https://github.com/ipfs/js-stores/commit/72b6e10e7d7f269d98ba1559bf5f96d229430bb9)), closes [#231](https://github.com/ipfs/js-stores/issues/231) +* **release:** 6.1.4 [skip ci] ([095c274](https://github.com/ipfs/js-stores/commit/095c2740284f75616d7e8086563616959912693a)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 6.1.5 [skip ci] ([11acd12](https://github.com/ipfs/js-stores/commit/11acd12dd420907b52fab24fd4c22607665b8010)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 6.1.6 [skip ci] ([6407f97](https://github.com/ipfs/js-stores/commit/6407f97cf4139841f9dd902a9dfaa6bce30edb80)), closes [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 6.1.7 [skip ci] ([4807b1f](https://github.com/ipfs/js-stores/commit/4807b1f92353320eecef97583eb1d43bf0c1dc4b)), closes [#282](https://github.com/ipfs/js-stores/issues/282) +* **release:** 6.1.9 [skip ci] ([d446381](https://github.com/ipfs/js-stores/commit/d446381f7b4a391bb249023e534f1c4fd05f1b17)) +* **release:** 7.0.0 [skip ci] ([027cfa6](https://github.com/ipfs/js-stores/commit/027cfa600bd15206dadb53d8e4d3127642bacb57)), closes [#315](https://github.com/ipfs/js-stores/issues/315) +* **release:** 7.0.1 [skip ci] ([dc879fb](https://github.com/ipfs/js-stores/commit/dc879fb141475fb99a1347f4d511856a30ffd413)), closes [#110](https://github.com/ipfs/js-stores/issues/110) [#175](https://github.com/ipfs/js-stores/issues/175) +* **release:** 7.0.2 [skip ci] ([bfdc00d](https://github.com/ipfs/js-stores/commit/bfdc00dd598d7d468d4134007b4dafa460eb8a5f)), closes [#181](https://github.com/ipfs/js-stores/issues/181) +* **release:** 7.0.3 [skip ci] ([6180190](https://github.com/ipfs/js-stores/commit/6180190b98b184ecc5f93afeaeefa4dd628dfc06)), closes [#183](https://github.com/ipfs/js-stores/issues/183) +* **release:** 7.0.4 [skip ci] ([3430b6c](https://github.com/ipfs/js-stores/commit/3430b6cf00f8512b67dc9034fa22f6355c92197a)), closes [#184](https://github.com/ipfs/js-stores/issues/184) +* **release:** 8.0.0 [skip ci] ([0d01201](https://github.com/ipfs/js-stores/commit/0d01201d665cde0a5d17cf3c65bc28a1930f536f)), closes [#189](https://github.com/ipfs/js-stores/issues/189) +* **release:** 8.1.0 [skip ci] ([9ad0ce5](https://github.com/ipfs/js-stores/commit/9ad0ce5e80e653ed9675f80736a1a58a93308f2e)), closes [#193](https://github.com/ipfs/js-stores/issues/193) +* **release:** 8.1.1 [skip ci] ([40f500b](https://github.com/ipfs/js-stores/commit/40f500be4fd820bdd901bde6a6be2ff1683bb800)), closes [#194](https://github.com/ipfs/js-stores/issues/194) +* **release:** 8.1.2 [skip ci] ([8c312d3](https://github.com/ipfs/js-stores/commit/8c312d3f9702f307fc180a9289d4f55f141c27a4)) +* **release:** 8.2.0 [skip ci] ([2c6f2ae](https://github.com/ipfs/js-stores/commit/2c6f2ae40a6181d6f33c469f851245755d6bf389)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 8.2.1 [skip ci] ([44d0d5c](https://github.com/ipfs/js-stores/commit/44d0d5c2508638a4f19e7f9a981998879effe92e)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 8.2.10 [skip ci] ([7f26bc8](https://github.com/ipfs/js-stores/commit/7f26bc855e23aebbce19c8f19fc642493aab6298)) +* **release:** 8.2.10 [skip ci] ([a8c793e](https://github.com/ipfs/js-stores/commit/a8c793ed72f3e1442f6ead18361f428f6b698bf8)) +* **release:** 8.2.11 [skip ci] ([15369b7](https://github.com/ipfs/js-stores/commit/15369b79ed3ac75342530757811cc31c22e99631)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 8.2.2 [skip ci] ([3b2280a](https://github.com/ipfs/js-stores/commit/3b2280a3aa56c5a515ea79adfbacb52e88ada113)), closes [#224](https://github.com/ipfs/js-stores/issues/224) +* **release:** 8.2.3 [skip ci] ([fc2925e](https://github.com/ipfs/js-stores/commit/fc2925e2b39c9093b157e7424232f6cfac968ad6)), closes [#228](https://github.com/ipfs/js-stores/issues/228) +* **release:** 8.2.4 [skip ci] ([7d4b28e](https://github.com/ipfs/js-stores/commit/7d4b28ef254a73906c9d05665752c413e6791c96)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 8.2.5 [skip ci] ([d440496](https://github.com/ipfs/js-stores/commit/d4404969ce7db0846ee986c6f66ce6c424f8f309)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 8.2.6 [skip ci] ([49d9e86](https://github.com/ipfs/js-stores/commit/49d9e868d6ae990df38d93cf71895b540ff24106)), closes [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 8.2.7 [skip ci] ([becf59f](https://github.com/ipfs/js-stores/commit/becf59fe0849f29644906bc1fe06409a7c3513ef)), closes [#269](https://github.com/ipfs/js-stores/issues/269) +* **release:** 8.2.8 [skip ci] ([23fbc88](https://github.com/ipfs/js-stores/commit/23fbc88fb73114e3999feddc397b3c349941bbc8)), closes [#282](https://github.com/ipfs/js-stores/issues/282) +* **release:** 8.2.9 [skip ci] ([7dad9a4](https://github.com/ipfs/js-stores/commit/7dad9a43fdec367895467752a6fee3ac7f912ca4)), closes [#283](https://github.com/ipfs/js-stores/issues/283) +* **release:** 8.3.0 [skip ci] ([63d45ec](https://github.com/ipfs/js-stores/commit/63d45ec246c5607157334631a44ee09b24bf1c39)) +* **release:** 9.1.0 [skip ci] ([d39c6a9](https://github.com/ipfs/js-stores/commit/d39c6a92ffc8bbc5ae9790abfed9ce7f6fde6c68)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 9.1.0 [skip ci] ([228bd8b](https://github.com/ipfs/js-stores/commit/228bd8b63f4da1169cfb395bd9d483359ee8be56)), closes [#197](https://github.com/ipfs/js-stores/issues/197) +* **release:** 9.1.1 [skip ci] ([7c7f97a](https://github.com/ipfs/js-stores/commit/7c7f97ab4d2b138dcb30dfaccec300ce5ca8281a)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#213](https://github.com/ipfs/js-stores/issues/213) +* **release:** 9.1.1 [skip ci] ([aa3a63f](https://github.com/ipfs/js-stores/commit/aa3a63ffeca74634f7ba279aff93d7dd981d92aa)), closes [#200](https://github.com/ipfs/js-stores/issues/200) [#213](https://github.com/ipfs/js-stores/issues/213) +* **release:** 9.1.2 [skip ci] ([842bdc4](https://github.com/ipfs/js-stores/commit/842bdc4832c526d9b6411f40781bee6a46c9fe49)), closes [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 9.1.2 [skip ci] ([2ebd1ad](https://github.com/ipfs/js-stores/commit/2ebd1ad5f71a109661a0cb30785960abe1c0c810)), closes [#225](https://github.com/ipfs/js-stores/issues/225) +* **release:** 9.1.3 [skip ci] ([4279b47](https://github.com/ipfs/js-stores/commit/4279b4706685d79fe23e680dcef6b32fc0ae24fb)), closes [#232](https://github.com/ipfs/js-stores/issues/232) +* **release:** 9.1.4 [skip ci] ([bcf6004](https://github.com/ipfs/js-stores/commit/bcf6004fe818fa9c24a18aa8eddc1ac4aaae51d7)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 9.1.5 [skip ci] ([050210c](https://github.com/ipfs/js-stores/commit/050210c977afd46e2cac161f21d46fb6d3fee88c)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 9.1.6 [skip ci] ([e11f15f](https://github.com/ipfs/js-stores/commit/e11f15feebcd31a7638004463b9a2fb2225bbeb2)), closes [#245](https://github.com/ipfs/js-stores/issues/245) [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 9.1.7 [skip ci] ([87681d2](https://github.com/ipfs/js-stores/commit/87681d2e21238c2d9f4d9127ba2c45cdb59dfc8f)) +* **release:** 9.1.8 [skip ci] ([0d41afa](https://github.com/ipfs/js-stores/commit/0d41afa8a6da0cfc1853e8d54debce115a3f8b0d)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 9.1.9 [skip ci] ([e860279](https://github.com/ipfs/js-stores/commit/e8602799bf6cb8082fc75717bc4a3e86801c033a)), closes [#306](https://github.com/ipfs/js-stores/issues/306) +* **release:** 9.2.0 [skip ci] ([05f253f](https://github.com/ipfs/js-stores/commit/05f253f7b14a5002be9f153847d081080d3875cb)), closes [#227](https://github.com/ipfs/js-stores/issues/227) +* **release:** 9.2.1 [skip ci] ([caf7841](https://github.com/ipfs/js-stores/commit/caf78416f32857edd965872994745a5ccf13545d)), closes [#240](https://github.com/ipfs/js-stores/issues/240) +* **release:** 9.2.2 [skip ci] ([23df262](https://github.com/ipfs/js-stores/commit/23df262439ee6fa1eb9b02ada314995306a309df)), closes [#241](https://github.com/ipfs/js-stores/issues/241) +* **release:** 9.2.3 [skip ci] ([01898e5](https://github.com/ipfs/js-stores/commit/01898e50f354eae9b1e7baaabbbd600ce42f294a)), closes [#244](https://github.com/ipfs/js-stores/issues/244) +* **release:** 9.2.4 [skip ci] ([b0073e2](https://github.com/ipfs/js-stores/commit/b0073e21997cac333c8237f31f414dc0dfc5d5fd)), closes [#245](https://github.com/ipfs/js-stores/issues/245) [#268](https://github.com/ipfs/js-stores/issues/268) +* **release:** 9.2.5 [skip ci] ([be4f27d](https://github.com/ipfs/js-stores/commit/be4f27d0eefaa873382f2b01bcd298b592197834)), closes [#280](https://github.com/ipfs/js-stores/issues/280) +* **release:** 9.2.6 [skip ci] ([c8e42f3](https://github.com/ipfs/js-stores/commit/c8e42f352feeae2aeeb586bdfdaef325fd820218)), closes [#282](https://github.com/ipfs/js-stores/issues/282) +* **release:** 9.2.7 [skip ci] ([44d2d0e](https://github.com/ipfs/js-stores/commit/44d2d0e01865a2a6007f7c71f6c30d0712c9ea8f)) +* **release:** 9.2.8 [skip ci] ([330060a](https://github.com/ipfs/js-stores/commit/330060abc34512047961f9f585154d23480e355a)), closes [#297](https://github.com/ipfs/js-stores/issues/297) +* **release:** 9.2.9 [skip ci] ([9d1f29e](https://github.com/ipfs/js-stores/commit/9d1f29e6e5fce22399a3a05440dac225138ca6ab)), closes [#236](https://github.com/ipfs/js-stores/issues/236) [#236](https://github.com/ipfs/js-stores/issues/236) +* remove / from CID prefix query ([b07aa1d](https://github.com/ipfs/js-stores/commit/b07aa1ddc46b7f44f6827df306a4d4fb200b0b26)) +* remove lerna ([#183](https://github.com/ipfs/js-stores/issues/183)) ([04e77ec](https://github.com/ipfs/js-stores/commit/04e77ec37ca5857b6156dd211f07a61eddcf19b0)) +* rename master to main ([#200](https://github.com/ipfs/js-stores/issues/200)) ([f85d719](https://github.com/ipfs/js-stores/commit/f85d719b711cd60237bdaa6a0bcd418e69a98598)) +* revert ([2c8d1a2](https://github.com/ipfs/js-stores/commit/2c8d1a25194989410326f3cedd65f94aa3b28ed5)) +* switch to ESM ([#39](https://github.com/ipfs/js-stores/issues/39)) ([c04aa80](https://github.com/ipfs/js-stores/commit/c04aa80d48a84b681962cae227dd2628e7d35cb5)) +* Update .github/workflows/stale.yml [skip ci] ([5d78301](https://github.com/ipfs/js-stores/commit/5d783010c8e1919561614fd6612c65a0c610ec29)) +* Update .github/workflows/stale.yml [skip ci] ([d6e5dd6](https://github.com/ipfs/js-stores/commit/d6e5dd67f60524a450563b8b66ed44c0016a9c09)) +* Update .github/workflows/stale.yml [skip ci] ([f4ff277](https://github.com/ipfs/js-stores/commit/f4ff277aa3cebd33e41c115e3bb4f4d0f859d210)) +* Update .github/workflows/stale.yml [skip ci] ([35e23f1](https://github.com/ipfs/js-stores/commit/35e23f14d84fe9e52b72ea63546a0926bf83edf8)) +* Update .github/workflows/stale.yml [skip ci] ([9d285a1](https://github.com/ipfs/js-stores/commit/9d285a11bbeae5f84f0845b13b7d71ab65a83771)) +* update aegir ([#68](https://github.com/ipfs/js-stores/issues/68)) ([56593d4](https://github.com/ipfs/js-stores/commit/56593d4eed78b918c09d8a96359ec3100f5f4608)) +* update config ([c6d9c09](https://github.com/ipfs/js-stores/commit/c6d9c09a1199c02e7b8be859bc5f71849b3ac85a)) +* update multiformats ([755788e](https://github.com/ipfs/js-stores/commit/755788e78cfaa4faec2bbf2ee6bdafcf20de97c2)) +* update package config ([5998ec2](https://github.com/ipfs/js-stores/commit/5998ec2776132919c24d2a855c22662f1494a191)) +* update sibling dependencies ([a4fb1c5](https://github.com/ipfs/js-stores/commit/a4fb1c5f97650d6ee80084e8c59c7a081f9a09e0)) +* update sibling dependencies ([7a15bcd](https://github.com/ipfs/js-stores/commit/7a15bcdb47bcade4d205fc04b4ffdfd3a1220f11)) +* update sibling dependencies ([65dccef](https://github.com/ipfs/js-stores/commit/65dccef376a9eb140ce0cef5b18e02c8515ab698)) +* update sibling dependencies ([66506a8](https://github.com/ipfs/js-stores/commit/66506a8313894bd65bb30d2015b8e9a65d212746)) +* update uint8arrays ([#28](https://github.com/ipfs/js-stores/issues/28)) ([b1427cd](https://github.com/ipfs/js-stores/commit/b1427cd2771b350ac532750d008442de90c4cf2d)) + +### Documentation + +* fix capitalization in readme of datastore-fs. ([#232](https://github.com/ipfs/js-stores/issues/232)) ([01492bf](https://github.com/ipfs/js-stores/commit/01492bfc90ab3025ff81f098a9bb7e60475be196)) +* fix capitalization of import ([#226](https://github.com/ipfs/js-stores/issues/226)) ([837221a](https://github.com/ipfs/js-stores/commit/837221aff3ef4d217063eb17953aff03764e7600)) +* fix interface-tests readme usage ([#247](https://github.com/ipfs/js-stores/issues/247)) ([e179933](https://github.com/ipfs/js-stores/commit/e179933277b589cf442a372392138067bb2df44b)) +* publish api docs ([#181](https://github.com/ipfs/js-stores/issues/181)) ([64f8473](https://github.com/ipfs/js-stores/commit/64f8473a1d646eda431972afb489ac81d23248fa)) +* remove duplicate readme section ([851a110](https://github.com/ipfs/js-stores/commit/851a11033140e7ae0996adeaf880d6554d12837c)) +* update api docs ([#244](https://github.com/ipfs/js-stores/issues/244)) ([e0f6145](https://github.com/ipfs/js-stores/commit/e0f614575d675fe4db2ab30ea6a2a854e892d635)) +* Update Blockstore and Datastore implementation lists ([#224](https://github.com/ipfs/js-stores/issues/224)) ([ab3b31b](https://github.com/ipfs/js-stores/commit/ab3b31b5ae2dba4b5ddb4b79740afb7c003aedae)) +* update datastore core readme and package config ([#245](https://github.com/ipfs/js-stores/issues/245)) ([c08d29a](https://github.com/ipfs/js-stores/commit/c08d29ab18ddea26a1d9dd73d673847469d28a13)) + +### Dependencies + +* bump @libp2p/logger from 2.1.1 to 3.0.0 ([#240](https://github.com/ipfs/js-stores/issues/240)) ([cc958ef](https://github.com/ipfs/js-stores/commit/cc958ef7ebca61a5355cfa2bc18769e374d76ae6)) +* bump @libp2p/logger from 3.1.0 to 4.0.1 ([#280](https://github.com/ipfs/js-stores/issues/280)) ([ab4731f](https://github.com/ipfs/js-stores/commit/ab4731f49a1a6f9163fb9c246121b3392503ba8b)) +* bump aegir from 37.12.1 to 38.1.0 ([#184](https://github.com/ipfs/js-stores/issues/184)) ([c8ab418](https://github.com/ipfs/js-stores/commit/c8ab418db835a6beefbb44c3ba9176779cebcd0d)) +* bump aegir from 38.1.8 to 39.0.9 ([#225](https://github.com/ipfs/js-stores/issues/225)) ([d0f301b](https://github.com/ipfs/js-stores/commit/d0f301b1243a0f4f692011449567b51b2706e70f)) +* bump idb from 7.1.1 to 8.0.0 ([#281](https://github.com/ipfs/js-stores/issues/281)) ([4d0bdbc](https://github.com/ipfs/js-stores/commit/4d0bdbc600b226c489259e5100af5c8c7031fb79)) +* bump it-all from 1.0.6 to 2.0.0 ([#177](https://github.com/ipfs/js-stores/issues/177)) ([b648877](https://github.com/ipfs/js-stores/commit/b648877c5afb625c7d1e13efd3e0f72d125de734)), closes [#28](https://github.com/ipfs/js-stores/issues/28) [#28](https://github.com/ipfs/js-stores/issues/28) [#27](https://github.com/ipfs/js-stores/issues/27) [#24](https://github.com/ipfs/js-stores/issues/24) +* bump it-drain from 1.0.5 to 2.0.0 ([#178](https://github.com/ipfs/js-stores/issues/178)) ([73e4cfc](https://github.com/ipfs/js-stores/commit/73e4cfcf41178fe6e27f0c7b431fb9511e1dda47)), closes [#28](https://github.com/ipfs/js-stores/issues/28) [#28](https://github.com/ipfs/js-stores/issues/28) [#27](https://github.com/ipfs/js-stores/issues/27) [#24](https://github.com/ipfs/js-stores/issues/24) +* bump it-glob from 2.0.7 to 3.0.1 ([#306](https://github.com/ipfs/js-stores/issues/306)) ([8f6313f](https://github.com/ipfs/js-stores/commit/8f6313f8a22cb537aeeac2a048aad644d3c9a7d2)) +* bump it-length from 1.0.4 to 2.0.0 ([#179](https://github.com/ipfs/js-stores/issues/179)) ([d1919b4](https://github.com/ipfs/js-stores/commit/d1919b40f4ec7ec0a20e0283d063e8b030ddc875)), closes [#28](https://github.com/ipfs/js-stores/issues/28) +* bump lerna from 5.6.2 to 6.0.0 ([#176](https://github.com/ipfs/js-stores/issues/176)) ([e583cce](https://github.com/ipfs/js-stores/commit/e583cced412e675b49bcbb9dad3010cf0d55f7b5)) +* bump multiformats from 10.0.3 to 11.0.0 ([#182](https://github.com/ipfs/js-stores/issues/182)) ([2342b17](https://github.com/ipfs/js-stores/commit/2342b170dd69b1e055c6eda07cdd4e07ed1f9a4c)), closes [#234](https://github.com/ipfs/js-stores/issues/234) [#226](https://github.com/ipfs/js-stores/issues/226) [#234](https://github.com/ipfs/js-stores/issues/234) [#226](https://github.com/ipfs/js-stores/issues/226) [#226](https://github.com/ipfs/js-stores/issues/226) +* bump multiformats from 11.0.2 to 12.0.1 ([#231](https://github.com/ipfs/js-stores/issues/231)) ([93b7c13](https://github.com/ipfs/js-stores/commit/93b7c13d0dd0508b04bae2ac5a9fb9c265fc5589)) +* bump multiformats from 12.1.3 to 13.0.0 ([#286](https://github.com/ipfs/js-stores/issues/286)) ([c8ccd1d](https://github.com/ipfs/js-stores/commit/c8ccd1de91883d1a1cbd394c21a51b021d52baa3)) +* bump multiformats from 9.9.0 to 10.0.0 ([#174](https://github.com/ipfs/js-stores/issues/174)) ([2a4f529](https://github.com/ipfs/js-stores/commit/2a4f529e4a4087fb048b337fbaeedffb939f2ebd)) +* bump nanoid from 4.0.2 to 5.0.3 ([#269](https://github.com/ipfs/js-stores/issues/269)) ([02cb8cd](https://github.com/ipfs/js-stores/commit/02cb8cd82de4174e7c417ea7e45ef7111f38e990)) +* bump uint8arrays from 3.0.0 to 4.0.1 ([#175](https://github.com/ipfs/js-stores/issues/175)) ([e8d5ea6](https://github.com/ipfs/js-stores/commit/e8d5ea63feaaaf379890171f4660bfd8f1cfef5e)) +* bump uint8arrays from 4.0.10 to 5.0.0 ([#282](https://github.com/ipfs/js-stores/issues/282)) ([2cbfd52](https://github.com/ipfs/js-stores/commit/2cbfd52257e9358786962d94e144df9583a45c30)) +* **dev:** bump aegir from 39.0.13 to 40.0.8 ([#241](https://github.com/ipfs/js-stores/issues/241)) ([00741ff](https://github.com/ipfs/js-stores/commit/00741ff043b40cf10ecc185665fcb705160c9877)) +* **dev:** bump aegir from 40.0.13 to 41.1.9 ([#268](https://github.com/ipfs/js-stores/issues/268)) ([0aa0944](https://github.com/ipfs/js-stores/commit/0aa0944d42798d1f6fd589e8a58de7d791760644)) +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) +* **dev:** bump sinon from 15.2.0 to 17.0.1 ([#265](https://github.com/ipfs/js-stores/issues/265)) ([316d3c5](https://github.com/ipfs/js-stores/commit/316d3c54c3a113a8deebc8e53480ac38d40f3a41)) +* **dev:** bump sinon from 17.0.2 to 18.0.0 ([#308](https://github.com/ipfs/js-stores/issues/308)) ([0fbfe11](https://github.com/ipfs/js-stores/commit/0fbfe1112a102055d75f077ff799fbb1001e6aa7)) +* update all it-* deps ([#213](https://github.com/ipfs/js-stores/issues/213)) ([e963497](https://github.com/ipfs/js-stores/commit/e963497fdb33e61e2fe702866abbd42fba648fee)) +* update sibling dependencies ([7c84601](https://github.com/ipfs/js-stores/commit/7c84601df3a17b5e163de68b224c0efef5d5b746)) +* update sibling dependencies ([5ac1112](https://github.com/ipfs/js-stores/commit/5ac1112fd613ef8cb66265ee7b6c89c368bcd0f7)) +* update sibling dependencies ([9adf0bc](https://github.com/ipfs/js-stores/commit/9adf0bc4c1b63ef0ae06ab5cb2f3dc19d83bc1a7)) +* update sibling dependencies ([8f7928c](https://github.com/ipfs/js-stores/commit/8f7928c28b5869001728cc997f0204a40f51978d)) +* update sibling dependencies ([bbfb89c](https://github.com/ipfs/js-stores/commit/bbfb89cbae37e0442df774e4dab63399c1e76d15)) +* update sibling dependencies ([dae86fd](https://github.com/ipfs/js-stores/commit/dae86fd39a133969cd4355fb1e7099a560a75baa)) +* update sibling dependencies ([713c9a4](https://github.com/ipfs/js-stores/commit/713c9a4bda345f488c273b3d55959fb47e9420ed)) +* update sibling dependencies ([94dec57](https://github.com/ipfs/js-stores/commit/94dec576ae4c259f5bd10c811f1d57785445beab)) +* update sibling dependencies ([9aedadd](https://github.com/ipfs/js-stores/commit/9aedadd99b72768124ef4b7f3640f0b6527e55ca)) +* update sibling dependencies ([79100b3](https://github.com/ipfs/js-stores/commit/79100b3fd4a7f88e18a09976e194e8461869b92f)) diff --git a/FUNDING.json b/FUNDING.json new file mode 100644 index 00000000..9085792a --- /dev/null +++ b/FUNDING.json @@ -0,0 +1,5 @@ +{ + "opRetro": { + "projectId": "0x7f330267969cf845a983a9d4e7b7dbcca5c700a5191269af377836d109e0bb69" + } +} diff --git a/README.md b/README.md index 1dd9ec84..ea6b756e 100644 --- a/README.md +++ b/README.md @@ -9,21 +9,21 @@ # Packages -- [`/packages/blockstore-core`](./packages/blockstore-core) Contains various implementations of the API contract described in interface-blockstore -- [`/packages/blockstore-fs`](./packages/blockstore-fs) Blockstore implementation with file system backend -- [`/packages/blockstore-idb`](./packages/blockstore-idb) Blockstore implementation with IndexedDB backend -- [`/packages/blockstore-level`](./packages/blockstore-level) Blockstore implementation with level(up|down) backend -- [`/packages/blockstore-s3`](./packages/blockstore-s3) IPFS blockstore implementation backed by s3 -- [`/packages/datastore-core`](./packages/datastore-core) Wrapper implementation for interface-datastore -- [`/packages/datastore-fs`](./packages/datastore-fs) Datastore implementation with file system backend -- [`/packages/datastore-idb`](./packages/datastore-idb) Datastore implementation with IndexedDB backend. -- [`/packages/datastore-level`](./packages/datastore-level) Datastore implementation with level(up|down) backend -- [`/packages/datastore-s3`](./packages/datastore-s3) IPFS datastore implementation backed by s3 -- [`/packages/interface-blockstore`](./packages/interface-blockstore) An interface for storing and retrieving blocks -- [`/packages/interface-blockstore-tests`](./packages/interface-blockstore-tests) Compliance tests for the blockstore interface -- [`/packages/interface-datastore`](./packages/interface-datastore) datastore interface -- [`/packages/interface-datastore-tests`](./packages/interface-datastore-tests) Compliance tests for the datastore interface -- [`/packages/interface-store`](./packages/interface-store) A generic interface for storing and retrieving data +- [`packages/blockstore-core`](https://github.com/ipfs/js-stores/tree/main/packages/blockstore-core) Contains various implementations of the API contract described in interface-blockstore +- [`packages/blockstore-fs`](https://github.com/ipfs/js-stores/tree/main/packages/blockstore-fs) Blockstore implementation with file system backend +- [`packages/blockstore-idb`](https://github.com/ipfs/js-stores/tree/main/packages/blockstore-idb) Blockstore implementation with IndexedDB backend +- [`packages/blockstore-level`](https://github.com/ipfs/js-stores/tree/main/packages/blockstore-level) Blockstore implementation with level(up|down) backend +- [`packages/blockstore-s3`](https://github.com/ipfs/js-stores/tree/main/packages/blockstore-s3) IPFS blockstore implementation backed by s3 +- [`packages/datastore-core`](https://github.com/ipfs/js-stores/tree/main/packages/datastore-core) Wrapper implementation for interface-datastore +- [`packages/datastore-fs`](https://github.com/ipfs/js-stores/tree/main/packages/datastore-fs) Datastore implementation with file system backend +- [`packages/datastore-idb`](https://github.com/ipfs/js-stores/tree/main/packages/datastore-idb) Datastore implementation with IndexedDB backend. +- [`packages/datastore-level`](https://github.com/ipfs/js-stores/tree/main/packages/datastore-level) Datastore implementation with level(up|down) backend +- [`packages/datastore-s3`](https://github.com/ipfs/js-stores/tree/main/packages/datastore-s3) IPFS datastore implementation backed by s3 +- [`packages/interface-blockstore`](https://github.com/ipfs/js-stores/tree/main/packages/interface-blockstore) An interface for storing and retrieving blocks +- [`packages/interface-blockstore-tests`](https://github.com/ipfs/js-stores/tree/main/packages/interface-blockstore-tests) Compliance tests for the blockstore interface +- [`packages/interface-datastore`](https://github.com/ipfs/js-stores/tree/main/packages/interface-datastore) datastore interface +- [`packages/interface-datastore-tests`](https://github.com/ipfs/js-stores/tree/main/packages/interface-datastore-tests) Compliance tests for the datastore interface +- [`packages/interface-store`](https://github.com/ipfs/js-stores/tree/main/packages/interface-store) A generic interface for storing and retrieving data # API Docs @@ -33,8 +33,8 @@ Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/LICENSE-MIT) / ) # Contribute diff --git a/package.json b/package.json index f6902bd1..83e8670d 100644 --- a/package.json +++ b/package.json @@ -12,91 +12,6 @@ "url": "https://github.com/ipfs/js-stores/issues" }, "private": true, - "release": { - "branches": [ - "main" - ], - "plugins": [ - [ - "@semantic-release/commit-analyzer", - { - "preset": "conventionalcommits", - "releaseRules": [ - { - "breaking": true, - "release": "major" - }, - { - "revert": true, - "release": "patch" - }, - { - "type": "feat", - "release": "minor" - }, - { - "type": "fix", - "release": "patch" - }, - { - "type": "docs", - "release": "patch" - }, - { - "type": "test", - "release": "patch" - }, - { - "type": "deps", - "release": "patch" - }, - { - "scope": "no-release", - "release": false - } - ] - } - ], - [ - "@semantic-release/release-notes-generator", - { - "preset": "conventionalcommits", - "presetConfig": { - "types": [ - { - "type": "feat", - "section": "Features" - }, - { - "type": "fix", - "section": "Bug Fixes" - }, - { - "type": "chore", - "section": "Trivial Changes" - }, - { - "type": "docs", - "section": "Documentation" - }, - { - "type": "deps", - "section": "Dependencies" - }, - { - "type": "test", - "section": "Tests" - } - ] - } - } - ], - "@semantic-release/changelog", - "@semantic-release/npm", - "@semantic-release/github", - "@semantic-release/git" - ] - }, "scripts": { "reset": "aegir run clean && aegir clean **/node_modules **/package-lock.json", "test": "aegir run test", @@ -113,12 +28,12 @@ "lint": "aegir run lint", "dep-check": "aegir run dep-check", "release": "run-s build docs:no-publish npm:release docs", - "npm:release": "aegir release", + "npm:release": "aegir run release --concurrency 1", "docs": "aegir docs", "docs:no-publish": "aegir docs --publish false" }, "devDependencies": { - "aegir": "^42.2.3", + "aegir": "^47.0.16", "npm-run-all": "^4.1.5" }, "workspaces": [ diff --git a/packages/blockstore-core/CHANGELOG.md b/packages/blockstore-core/CHANGELOG.md index 879349c9..0a2ea7d7 100644 --- a/packages/blockstore-core/CHANGELOG.md +++ b/packages/blockstore-core/CHANGELOG.md @@ -1,3 +1,91 @@ +## [blockstore-core-v6.1.2](https://github.com/ipfs/js-stores/compare/blockstore-core-6.1.1...blockstore-core-6.1.2) (2026-01-08) + +### Bug Fixes + +* identity supports async get and getAll ([#365](https://github.com/ipfs/js-stores/issues/365)) ([a1b95fc](https://github.com/ipfs/js-stores/commit/a1b95fc173c066d69bf0f1145d7b458941065be6)) + +## [blockstore-core-v6.1.1](https://github.com/ipfs/js-stores/compare/blockstore-core-6.1.0...blockstore-core-6.1.1) (2025-10-09) + +### Bug Fixes + +* update error name ([304ec71](https://github.com/ipfs/js-stores/commit/304ec7142b16ad0cd70537d48eb69294faacffe5)) + +## [blockstore-core-v6.1.0](https://github.com/ipfs/js-stores/compare/blockstore-core-6.0.2...blockstore-core-6.1.0) (2025-10-09) + +### Features + +* enforce max digest length ([#360](https://github.com/ipfs/js-stores/issues/360)) ([c5b039a](https://github.com/ipfs/js-stores/commit/c5b039ae248cce82bf02fbf0383da8e65e140786)) + +## [blockstore-core-v6.0.2](https://github.com/ipfs/js-stores/compare/blockstore-core-6.0.1...blockstore-core-6.0.2) (2025-10-03) + +### Dependencies + +* bump level from 8.0.1 to 10.0.0 ([#356](https://github.com/ipfs/js-stores/issues/356)) ([c0ec61f](https://github.com/ipfs/js-stores/commit/c0ec61fe965e3bad9d607a0bd3a3c750f00f41d0)) + +## [blockstore-core-v6.0.1](https://github.com/ipfs/js-stores/compare/blockstore-core-6.0.0...blockstore-core-6.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [blockstore-core-v6.0.0](https://github.com/ipfs/js-stores/compare/blockstore-core-5.0.4...blockstore-core-6.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +### Dependencies + +* bump @libp2p/logger ([#359](https://github.com/ipfs/js-stores/issues/359)) ([edb5a1f](https://github.com/ipfs/js-stores/commit/edb5a1f8b575a27ad28bc2e1c4e4d52e1f114ebc)) + +## [blockstore-core-v5.0.4](https://github.com/ipfs/js-stores/compare/blockstore-core-5.0.3...blockstore-core-5.0.4) (2025-05-28) + +### Bug Fixes + +* improve abort signal support ([#350](https://github.com/ipfs/js-stores/issues/350)) ([e17d770](https://github.com/ipfs/js-stores/commit/e17d770cc2fcee77cb0152a855abf162e5a91a99)) + +## [blockstore-core-v5.0.3](https://github.com/ipfs/js-stores/compare/blockstore-core-5.0.2...blockstore-core-5.0.3) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [blockstore-core-v5.0.2](https://github.com/ipfs/js-stores/compare/blockstore-core-5.0.1...blockstore-core-5.0.2) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +## blockstore-core [5.0.0](https://github.com/ipfs/js-stores/compare/blockstore-core-4.4.1...blockstore-core-5.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + + + +### Dependencies + +* **interface-blockstore:** upgraded to 5.3.0 +* **interface-store:** upgraded to 6.0.0 +* **interface-blockstore-tests:** upgraded to 7.0.0 + +## blockstore-core [4.4.1](https://github.com/ipfs/js-stores/compare/blockstore-core-4.4.0...blockstore-core-4.4.1) (2024-04-09) + + +### Bug Fixes + +* identity blockstore should wrap child ([#303](https://github.com/ipfs/js-stores/issues/303)) ([3d84dd0](https://github.com/ipfs/js-stores/commit/3d84dd0ab164fb5749c34487a217c763d1d09ccb)) + ## blockstore-core [4.4.0](https://github.com/ipfs/js-stores/compare/blockstore-core-4.3.11...blockstore-core-4.4.0) (2024-02-12) diff --git a/packages/blockstore-core/CODE_OF_CONDUCT.md b/packages/blockstore-core/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/blockstore-core/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/blockstore-core/LICENSE b/packages/blockstore-core/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/blockstore-core/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/blockstore-core/LICENSE-APACHE b/packages/blockstore-core/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/blockstore-core/LICENSE-APACHE +++ b/packages/blockstore-core/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/blockstore-core/README.md b/packages/blockstore-core/README.md index 94943a86..166d6902 100644 --- a/packages/blockstore-core/README.md +++ b/packages/blockstore-core/README.md @@ -9,14 +9,29 @@ # About + + Various Blockstore implementations are available. ## Implementations -- Base: [`src/base`](src/base.ts) -- Memory: [`src/memory`](src/memory.ts) -- BlackHole: ['src/black-hole](src/black-hole.ts) -- Tiered: ['src/tiered](src/tiered.ts) +- Base: [`src/base`](./src/base.ts) +- Memory: [`src/memory`](./src/memory.ts) +- BlackHole: ['src/black-hole](./src/black-hole.ts) +- Tiered: ['src/tiered](./src/tiered.ts) ## Example - BaseBlockstore @@ -97,7 +112,7 @@ $ npm i blockstore-core ## Browser ` @@ -111,8 +126,8 @@ Loading this module through a script tag will make it's exports available as `Bl Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/blockstore-core/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/blockstore-core/LICENSE-MIT) / ) # Contribute diff --git a/packages/blockstore-core/package.json b/packages/blockstore-core/package.json index abc4fe5d..84d94561 100644 --- a/packages/blockstore-core/package.json +++ b/packages/blockstore-core/package.json @@ -1,6 +1,6 @@ { "name": "blockstore-core", - "version": "4.4.0", + "version": "6.1.2", "description": "Contains various implementations of the API contract described in interface-blockstore", "author": "Alex Potsides ", "license": "Apache-2.0 OR MIT", @@ -76,12 +76,98 @@ "import": "./dist/src/tiered.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true, - "sourceType": "module" - } + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "clean": "aegir clean", @@ -94,22 +180,23 @@ "test:firefox": "aegir test -t browser -- --browser firefox", "test:firefox-webworker": "aegir test -t webworker -- --browser firefox", "test:electron-main": "aegir test -t electron-main", - "dep-check": "aegir dep-check" + "dep-check": "aegir dep-check", + "release": "aegir release" }, "dependencies": { - "@libp2p/logger": "^4.0.6", - "err-code": "^3.0.1", - "interface-blockstore": "^5.0.0", - "interface-store": "^5.0.0", - "it-drain": "^3.0.5", - "it-filter": "^3.0.4", - "it-merge": "^3.0.3", - "it-pushable": "^3.2.3", - "multiformats": "^13.0.1" + "@libp2p/logger": "^6.0.0", + "interface-blockstore": "^6.0.0", + "interface-store": "^7.0.0", + "it-all": "^3.0.9", + "it-filter": "^3.1.3", + "it-merge": "^3.0.11", + "multiformats": "^13.3.6" }, "devDependencies": { - "aegir": "^42.2.3", - "interface-blockstore-tests": "^6.0.0", - "uint8arrays": "^5.0.2" + "aegir": "^47.0.16", + "interface-blockstore-tests": "^8.0.0", + "it-drain": "^3.0.9", + "it-to-buffer": "^4.0.10", + "uint8arrays": "^5.1.0" } } diff --git a/packages/blockstore-core/src/base.ts b/packages/blockstore-core/src/base.ts index 87b1d966..dcb216a0 100644 --- a/packages/blockstore-core/src/base.ts +++ b/packages/blockstore-core/src/base.ts @@ -1,5 +1,5 @@ -import type { Blockstore, Pair } from 'interface-blockstore' -import type { AbortOptions, Await, AwaitIterable } from 'interface-store' +import type { Blockstore, InputPair, Pair } from 'interface-blockstore' +import type { AbortOptions, Await, AwaitGenerator, AwaitIterable } from 'interface-store' import type { CID } from 'multiformats/cid' export class BaseBlockstore implements Blockstore { @@ -7,26 +7,26 @@ export class BaseBlockstore implements Blockstore { return Promise.reject(new Error('.has is not implemented')) } - put (key: CID, val: Uint8Array, options?: AbortOptions): Await { + put (key: CID, val: Uint8Array | AwaitIterable, options?: AbortOptions): Await { return Promise.reject(new Error('.put is not implemented')) } - async * putMany (source: AwaitIterable, options?: AbortOptions): AwaitIterable { - for await (const { cid, block } of source) { - await this.put(cid, block, options) + async * putMany (source: AwaitIterable, options?: AbortOptions): AwaitGenerator { + for await (const { cid, bytes } of source) { + await this.put(cid, bytes, options) yield cid } } - get (key: CID, options?: AbortOptions): Await { - return Promise.reject(new Error('.get is not implemented')) + get (key: CID, options?: AbortOptions): AwaitGenerator { + throw new Error('.get is not implemented') } - async * getMany (source: AwaitIterable, options?: AbortOptions): AwaitIterable { + async * getMany (source: AwaitIterable, options?: AbortOptions): AwaitGenerator { for await (const key of source) { yield { cid: key, - block: await this.get(key, options) + bytes: this.get(key, options) } } } @@ -35,7 +35,7 @@ export class BaseBlockstore implements Blockstore { return Promise.reject(new Error('.delete is not implemented')) } - async * deleteMany (source: AwaitIterable, options?: AbortOptions): AwaitIterable { + async * deleteMany (source: AwaitIterable, options?: AbortOptions): AwaitGenerator { for await (const key of source) { await this.delete(key, options) yield key @@ -45,7 +45,7 @@ export class BaseBlockstore implements Blockstore { /** * Extending classes should override `query` or implement this method */ - async * getAll (options?: AbortOptions): AwaitIterable { // eslint-disable-line require-yield + async * getAll (options?: AbortOptions): AwaitGenerator { // eslint-disable-line require-yield throw new Error('.getAll is not implemented') } } diff --git a/packages/blockstore-core/src/black-hole.ts b/packages/blockstore-core/src/black-hole.ts index ab1b5a61..c71e52c1 100644 --- a/packages/blockstore-core/src/black-hole.ts +++ b/packages/blockstore-core/src/black-hole.ts @@ -1,27 +1,31 @@ +import { NotFoundError } from 'interface-store' import { BaseBlockstore } from './base.js' -import * as Errors from './errors.js' import type { Pair } from 'interface-blockstore' -import type { Await, AwaitIterable } from 'interface-store' +import type { AbortOptions, Await, AwaitGenerator, AwaitIterable } from 'interface-store' import type { CID } from 'multiformats/cid' export class BlackHoleBlockstore extends BaseBlockstore { - put (key: CID): Await { + put (key: CID, value: Uint8Array | AwaitIterable, options?: AbortOptions): Await { + options?.signal?.throwIfAborted() return key } - get (): Await { - throw Errors.notFoundError() + get (key: CID, options?: AbortOptions): AwaitGenerator { + options?.signal?.throwIfAborted() + throw new NotFoundError() } - has (): Await { + has (key: CID, options?: AbortOptions): Await { + options?.signal?.throwIfAborted() return false } - async delete (): Promise { - + async delete (cid: CID, options?: AbortOptions): Promise { + options?.signal?.throwIfAborted() } - async * getAll (): AwaitIterable { - + // eslint-disable-next-line require-yield + async * getAll (options?: AbortOptions): AwaitGenerator { + options?.signal?.throwIfAborted() } } diff --git a/packages/blockstore-core/src/errors.ts b/packages/blockstore-core/src/errors.ts deleted file mode 100644 index d4533c91..00000000 --- a/packages/blockstore-core/src/errors.ts +++ /dev/null @@ -1,41 +0,0 @@ -import errCode from 'err-code' - -export function openFailedError (err?: Error): Error { - err = err ?? new Error('Open failed') - return errCode(err, 'ERR_OPEN_FAILED') -} - -export function closeFailedError (err?: Error): Error { - err = err ?? new Error('Close failed') - return errCode(err, 'ERR_CLOSE_FAILED') -} - -export function putFailedError (err?: Error): Error { - err = err ?? new Error('Put failed') - return errCode(err, 'ERR_PUT_FAILED') -} - -export function getFailedError (err?: Error): Error { - err = err ?? new Error('Get failed') - return errCode(err, 'ERR_GET_FAILED') -} - -export function deleteFailedError (err?: Error): Error { - err = err ?? new Error('Delete failed') - return errCode(err, 'ERR_DELETE_FAILED') -} - -export function hasFailedError (err?: Error): Error { - err = err ?? new Error('Has failed') - return errCode(err, 'ERR_HAS_FAILED') -} - -export function notFoundError (err?: Error): Error { - err = err ?? new Error('Not Found') - return errCode(err, 'ERR_NOT_FOUND') -} - -export function abortedError (err?: Error): Error { - err = err ?? new Error('Aborted') - return errCode(err, 'ERR_ABORTED') -} diff --git a/packages/blockstore-core/src/identity.ts b/packages/blockstore-core/src/identity.ts index 059902df..3c861178 100644 --- a/packages/blockstore-core/src/identity.ts +++ b/packages/blockstore-core/src/identity.ts @@ -1,34 +1,107 @@ +import { NotFoundError } from 'interface-store' import { BaseBlockstore } from './base.js' -import * as Errors from './errors.js' -import type { Pair } from 'interface-blockstore' -import type { Await, AwaitIterable } from 'interface-store' +import type { Blockstore, Pair } from 'interface-blockstore' +import type { AbortOptions, Await, AwaitIterable } from 'interface-store' import type { CID } from 'multiformats/cid' // https://github.com/multiformats/multicodec/blob/d06fc6194710e8909bac64273c43f16b56ca4c34/table.csv#L2 const IDENTITY_CODEC = 0x00 +class IdentityHashDigestTooLongError extends Error { + static name = 'IdentityHashDigestTooLongError' + name = 'IdentityHashDigestTooLongError' +} + +export interface IdentityBlockstoreInit { + maxDigestLength?: number +} + export class IdentityBlockstore extends BaseBlockstore { - put (key: CID): CID { - return key + private readonly child?: Blockstore + private readonly maxDigestLength?: number + + constructor (child?: Blockstore, init?: IdentityBlockstoreInit) { + super() + + this.child = child + this.maxDigestLength = init?.maxDigestLength } - get (key: CID): Await { - if (key.code === IDENTITY_CODEC) { - return key.multihash.digest + put (key: CID, block: Uint8Array | AwaitIterable, options?: AbortOptions): Await { + if (key.multihash.code === IDENTITY_CODEC) { + if (this.maxDigestLength != null && key.multihash.digest.byteLength > this.maxDigestLength) { + throw new IdentityHashDigestTooLongError(`Identity digest too long - ${key.multihash.digest.byteLength} > this.maxDigestLength`) + } + + options?.signal?.throwIfAborted() + return key + } + + if (this.child == null) { + options?.signal?.throwIfAborted() + return key } - throw Errors.notFoundError() + return this.child.put(key, block, options) } - has (key: CID): boolean { - return key.code === IDENTITY_CODEC + async * get (key: CID, options?: AbortOptions): AsyncGenerator { + if (key.multihash.code === IDENTITY_CODEC) { + if (this.maxDigestLength != null && key.multihash.digest.byteLength > this.maxDigestLength) { + throw new IdentityHashDigestTooLongError(`Identity digest too long - ${key.multihash.digest.byteLength} > this.maxDigestLength`) + } + + options?.signal?.throwIfAborted() + yield key.multihash.digest + return + } + + if (this.child == null) { + options?.signal?.throwIfAborted() + throw new NotFoundError() + } + + yield * this.child.get(key, options) } - delete (): void { + has (key: CID, options?: AbortOptions): Await { + if (key.multihash.code === IDENTITY_CODEC) { + if (this.maxDigestLength != null && key.multihash.digest.byteLength > this.maxDigestLength) { + throw new IdentityHashDigestTooLongError(`Identity digest too long - ${key.multihash.digest.byteLength} > this.maxDigestLength`) + } + + options?.signal?.throwIfAborted() + return true + } + if (this.child == null) { + options?.signal?.throwIfAborted() + return false + } + + return this.child.has(key, options) } - * getAll (): AwaitIterable { + delete (key: CID, options?: AbortOptions): Await { + if (key.code === IDENTITY_CODEC) { + if (this.maxDigestLength != null && key.multihash.digest.byteLength > this.maxDigestLength) { + throw new IdentityHashDigestTooLongError(`Identity digest too long - ${key.multihash.digest.byteLength} > this.maxDigestLength`) + } + + options?.signal?.throwIfAborted() + return + } + + if (this.child != null) { + return this.child.delete(key, options) + } + } + + async * getAll (options?: AbortOptions): AsyncGenerator { + if (this.child != null) { + yield * this.child.getAll(options) + } + options?.signal?.throwIfAborted() } } diff --git a/packages/blockstore-core/src/index.ts b/packages/blockstore-core/src/index.ts index 0bab631f..a47ed278 100644 --- a/packages/blockstore-core/src/index.ts +++ b/packages/blockstore-core/src/index.ts @@ -5,10 +5,10 @@ * * ## Implementations * - * - Base: [`src/base`](src/base.ts) - * - Memory: [`src/memory`](src/memory.ts) - * - BlackHole: ['src/black-hole](src/black-hole.ts) - * - Tiered: ['src/tiered](src/tiered.ts) + * - Base: [`src/base`](./src/base.ts) + * - Memory: [`src/memory`](./src/memory.ts) + * - BlackHole: ['src/black-hole](./src/black-hole.ts) + * - Tiered: ['src/tiered](./src/tiered.ts) * * @example BaseBlockstore * @@ -82,13 +82,7 @@ * ``` */ -import * as ErrorsImport from './errors.js' - export { BaseBlockstore } from './base.js' export { MemoryBlockstore } from './memory.js' export { BlackHoleBlockstore } from './black-hole.js' export { TieredBlockstore } from './tiered.js' - -export const Errors = { - ...ErrorsImport -} diff --git a/packages/blockstore-core/src/memory.ts b/packages/blockstore-core/src/memory.ts index 72686d89..a62d1ba3 100644 --- a/packages/blockstore-core/src/memory.ts +++ b/packages/blockstore-core/src/memory.ts @@ -1,14 +1,19 @@ +import { NotFoundError } from 'interface-store' +import all from 'it-all' import { base32 } from 'multiformats/bases/base32' import { CID } from 'multiformats/cid' import * as raw from 'multiformats/codecs/raw' import * as Digest from 'multiformats/hashes/digest' import { BaseBlockstore } from './base.js' -import * as Errors from './errors.js' import type { Pair } from 'interface-blockstore' -import type { Await, AwaitIterable } from 'interface-store' +import type { AbortOptions, Await, AwaitGenerator, AwaitIterable } from 'interface-store' + +function isPromise (p?: any): p is Promise { + return typeof p?.then === 'function' +} export class MemoryBlockstore extends BaseBlockstore { - private readonly data: Map + private readonly data: Map constructor () { super() @@ -16,36 +21,68 @@ export class MemoryBlockstore extends BaseBlockstore { this.data = new Map() } - put (key: CID, val: Uint8Array): Await { // eslint-disable-line require-await + put (key: CID, val: Uint8Array | AwaitIterable, options?: AbortOptions): Await { + options?.signal?.throwIfAborted() + + let buf: Uint8Array[] + + if (val instanceof Uint8Array) { + buf = [val] + } else { + const result = all(val) + + if (isPromise(result)) { + return result.then(val => { + return this._put(key, val, options) + }) + } else { + buf = result + } + } + + return this._put(key, buf, options) + } + + private _put (key: CID, val: Uint8Array[], options?: AbortOptions): Await { + options?.signal?.throwIfAborted() + this.data.set(base32.encode(key.multihash.bytes), val) return key } - get (key: CID): Await { + * get (key: CID, options?: AbortOptions): AwaitGenerator { + options?.signal?.throwIfAborted() const buf = this.data.get(base32.encode(key.multihash.bytes)) if (buf == null) { - throw Errors.notFoundError() + throw new NotFoundError() } - return buf + yield * buf } - has (key: CID): Await { + has (key: CID, options?: AbortOptions): Await { + options?.signal?.throwIfAborted() return this.data.has(base32.encode(key.multihash.bytes)) } - async delete (key: CID): Promise { + async delete (key: CID, options?: AbortOptions): Promise { + options?.signal?.throwIfAborted() this.data.delete(base32.encode(key.multihash.bytes)) } - async * getAll (): AwaitIterable { + * getAll (options?: AbortOptions): AwaitGenerator { + options?.signal?.throwIfAborted() + for (const [key, value] of this.data.entries()) { yield { cid: CID.createV1(raw.code, Digest.decode(base32.decode(key))), - block: value + bytes: (async function * () { + yield * value + })() } + options?.signal?.throwIfAborted() } } } diff --git a/packages/blockstore-core/src/tiered.ts b/packages/blockstore-core/src/tiered.ts index ce5c39f1..5e09a939 100644 --- a/packages/blockstore-core/src/tiered.ts +++ b/packages/blockstore-core/src/tiered.ts @@ -1,12 +1,10 @@ import { logger } from '@libp2p/logger' -import drain from 'it-drain' +import { NotFoundError } from 'interface-store' import filter from 'it-filter' import merge from 'it-merge' -import { pushable } from 'it-pushable' import { BaseBlockstore } from './base.js' -import * as Errors from './errors.js' -import type { Blockstore, Pair } from 'interface-blockstore' -import type { AbortOptions, AwaitIterable } from 'interface-store' +import type { Blockstore, InputPair, Pair } from 'interface-blockstore' +import type { AbortOptions, AwaitGenerator, AwaitIterable } from 'interface-store' import type { CID } from 'multiformats/cid' const log = logger('blockstore:core:tiered') @@ -26,25 +24,30 @@ export class TieredBlockstore extends BaseBlockstore { this.stores = stores.slice() } - async put (key: CID, value: Uint8Array, options?: AbortOptions): Promise { - try { - await Promise.all(this.stores.map(async store => { await store.put(key, value, options) })) - return key - } catch (err: any) { - throw Errors.putFailedError(err) - } + async put (key: CID, value: Uint8Array | AwaitIterable, options?: AbortOptions): Promise { + await Promise.all( + this.stores.map(async store => { + await store.put(key, value, options) + }) + ) + + return key } - async get (key: CID, options?: AbortOptions): Promise { + async * get (key: CID, options?: AbortOptions): AwaitGenerator { + let error: Error | undefined + for (const store of this.stores) { try { - const res = await store.get(key, options) - if (res != null) return res - } catch (err) { + yield * store.get(key, options) + return + } catch (err: any) { + error = err log.error(err) } } - throw Errors.notFoundError() + + throw error ?? new NotFoundError() } async has (key: CID, options?: AbortOptions): Promise { @@ -58,76 +61,28 @@ export class TieredBlockstore extends BaseBlockstore { } async delete (key: CID, options?: AbortOptions): Promise { - try { - await Promise.all(this.stores.map(async store => { await store.delete(key, options) })) - } catch (err: any) { - throw Errors.deleteFailedError(err) - } - } - - async * putMany (source: AwaitIterable, options: AbortOptions = {}): AsyncIterable { - let error: Error | undefined - const pushables = this.stores.map(store => { - const source = pushable({ - objectMode: true + await Promise.all( + this.stores.map(async store => { + await store.delete(key, options) }) + ) + } - drain(store.putMany(source, options)) - .catch(err => { - // store threw while putting, make sure we bubble the error up - error = err - }) - - return source - }) - - try { - for await (const pair of source) { - if (error != null) { - throw error - } - - pushables.forEach(p => p.push(pair)) - - yield pair.cid - } - } finally { - pushables.forEach(p => p.end()) + async * putMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { + for await (const pair of source) { + await this.put(pair.cid, pair.bytes, options) + yield pair.cid } } - async * deleteMany (source: AwaitIterable, options: AbortOptions = {}): AsyncIterable { - let error: Error | undefined - const pushables = this.stores.map(store => { - const source = pushable({ - objectMode: true - }) - - drain(store.deleteMany(source, options)) - .catch(err => { - // store threw while deleting, make sure we bubble the error up - error = err - }) - - return source - }) - - try { - for await (const key of source) { - if (error != null) { - throw error - } - - pushables.forEach(p => p.push(key)) - - yield key - } - } finally { - pushables.forEach(p => p.end()) + async * deleteMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { + for await (const cid of source) { + await this.delete(cid, options) + yield cid } } - async * getAll (options?: AbortOptions): AwaitIterable { // eslint-disable-line require-yield + async * getAll (options?: AbortOptions): AwaitGenerator { // deduplicate yielded pairs const seen = new Set() diff --git a/packages/blockstore-core/test/identity.spec.ts b/packages/blockstore-core/test/identity.spec.ts index e0bfcdfc..7809da69 100644 --- a/packages/blockstore-core/test/identity.spec.ts +++ b/packages/blockstore-core/test/identity.spec.ts @@ -1,28 +1,34 @@ /* eslint-env mocha */ import { expect } from 'aegir/chai' +import all from 'it-all' import drain from 'it-drain' +import toBuffer from 'it-to-buffer' import { CID } from 'multiformats/cid' import * as raw from 'multiformats/codecs/raw' import { identity } from 'multiformats/hashes/identity' import { sha256 } from 'multiformats/hashes/sha2' import { IdentityBlockstore } from '../src/identity.js' +import { MemoryBlockstore } from '../src/memory.js' import type { Blockstore } from 'interface-blockstore' +import type { AbortOptions } from 'interface-store' describe('identity', () => { let blockstore: Blockstore + let child: Blockstore beforeEach(() => { blockstore = new IdentityBlockstore() + child = new MemoryBlockstore() }) - it('has an identity CID', () => { + it('has an identity CID', async () => { const block = Uint8Array.from([0, 1, 2, 3, 4]) const multihash = identity.digest(block) const cid = CID.createV1(identity.code, multihash) expect(blockstore.has(cid)).to.be.true() - expect(blockstore.get(cid)).to.equalBytes(block) + expect(toBuffer(await all(blockstore.get(cid)))).to.equalBytes(block) }) it('does not have a non-identity CID', async () => { @@ -56,4 +62,125 @@ describe('identity', () => { expect(blockstore.has(cid)).to.be.true() }) + + it('puts CIDs to child', async () => { + const block = Uint8Array.from([0, 1, 2, 3, 4]) + const multihash = await sha256.digest(block) + const cid = CID.createV1(raw.code, multihash) + + blockstore = new IdentityBlockstore(child) + + await blockstore.put(cid, block) + expect(child.has(cid)).to.be.true() + expect(toBuffer(await all(child.get(cid)))).to.equalBytes(block) + }) + + it('gets CIDs from child', async () => { + const block = Uint8Array.from([0, 1, 2, 3, 4]) + const multihash = await sha256.digest(block) + const cid = CID.createV1(raw.code, multihash) + + await child.put(cid, block) + + blockstore = new IdentityBlockstore(child) + expect(blockstore.has(cid)).to.be.true() + expect(toBuffer(await all(blockstore.get(cid)))).to.equalBytes(block) + }) + + it('gets CIDs from child (async)', async () => { + const block = Uint8Array.from([0, 1, 2, 3, 4]) + const multihash = await sha256.digest(block) + const cid = CID.createV1(raw.code, multihash) + + await child.put(cid, block) + + const { get } = child + child.get = async function * (key: CID, options: AbortOptions) { yield * get.bind(child)(key, options) } + blockstore = new IdentityBlockstore(child) + expect(blockstore.has(cid)).to.be.true() + expect(toBuffer(await all(blockstore.get(cid)))).to.equalBytes(block) + }) + + it('has CIDs from child', async () => { + const block = Uint8Array.from([0, 1, 2, 3, 4]) + const multihash = await sha256.digest(block) + const cid = CID.createV1(raw.code, multihash) + + await child.put(cid, block) + + blockstore = new IdentityBlockstore(child) + expect(blockstore.has(cid)).to.be.true() + }) + + it('deletes CIDs from child', async () => { + const block = Uint8Array.from([0, 1, 2, 3, 4]) + const multihash = await sha256.digest(block) + const cid = CID.createV1(raw.code, multihash) + + await child.put(cid, block) + + blockstore = new IdentityBlockstore(child) + expect(blockstore.has(cid)).to.be.true() + + await blockstore.delete(cid) + + expect(blockstore.has(cid)).to.be.false() + }) + + it('gets all pairs from child', async () => { + const block = Uint8Array.from([0, 1, 2, 3, 4]) + const multihash = await sha256.digest(block) + const cid = CID.createV1(raw.code, multihash) + + await child.put(cid, block) + + blockstore = new IdentityBlockstore(child) + expect(blockstore.has(cid)).to.be.true() + + const result = await all(blockstore.getAll()) + + expect(result).to.have.lengthOf(1) + expect(result[0].cid.toString()).to.equal(cid.toString()) + }) + + it('gets all pairs from child (async)', async () => { + const block = Uint8Array.from([0, 1, 2, 3, 4]) + const multihash = await sha256.digest(block) + const cid = CID.createV1(raw.code, multihash) + + await child.put(cid, block) + + const { getAll } = child + child.getAll = async function * (options?: AbortOptions) { yield * getAll.bind(child)(options) } + blockstore = new IdentityBlockstore(child) + expect(blockstore.has(cid)).to.be.true() + + const result = await all(blockstore.getAll()) + + expect(result).to.have.lengthOf(1) + expect(result[0].cid.toString()).to.equal(cid.toString()) + }) + + it('should enforce a maximum digest size', async () => { + blockstore = new IdentityBlockstore(child, { + maxDigestLength: 5 + }) + + const tooLong = Uint8Array.from([0, 1, 2, 3, 4, 5]) + const ok = Uint8Array.from([0, 1, 2, 3, 4]) + const buf = Uint8Array.from([]) + + expect(blockstore.get(CID.createV1(raw.code, identity.digest(ok)))).to.be.ok() + expect(blockstore.put(CID.createV1(raw.code, identity.digest(ok)), buf)).to.be.ok() + expect(blockstore.has(CID.createV1(raw.code, identity.digest(ok)))).to.be.ok() + + await expect(all(blockstore.get(CID.createV1(raw.code, identity.digest(tooLong))))).to.eventually.be.rejected() + .with.property('name', 'IdentityHashDigestTooLongError') + + expect(() => blockstore.put(CID.createV1(raw.code, identity.digest(tooLong)), buf)).to.throw() + .with.property('name', 'IdentityHashDigestTooLongError') + + expect(() => blockstore.has(CID.createV1(raw.code, identity.digest(tooLong)))).to.throw() + .with.property('name', 'IdentityHashDigestTooLongError') + }) }) diff --git a/packages/blockstore-core/test/tiered.spec.ts b/packages/blockstore-core/test/tiered.spec.ts index f56dd7b9..2b32c333 100644 --- a/packages/blockstore-core/test/tiered.spec.ts +++ b/packages/blockstore-core/test/tiered.spec.ts @@ -2,6 +2,7 @@ import { expect } from 'aegir/chai' import { interfaceBlockstoreTests } from 'interface-blockstore-tests' +import all from 'it-all' import { CID } from 'multiformats/cid' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { MemoryBlockstore } from '../src/memory.js' @@ -22,9 +23,9 @@ describe('Tiered', () => { const k = CID.parse('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL') const v = uint8ArrayFromString('world') await store.put(k, v) - const res = await Promise.all([ms[0].get(k), ms[1].get(k)]) + const res = await Promise.all([all(ms[0].get(k)), all(ms[1].get(k))]) res.forEach((val) => { - expect(val).to.be.eql(v) + expect(val).to.deep.equal([v]) }) }) @@ -32,14 +33,14 @@ describe('Tiered', () => { const k = CID.parse('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL') const v = uint8ArrayFromString('world') await ms[1].put(k, v) - const val = await store.get(k) - expect(val).to.be.eql(v) + const val = await all(store.get(k)) + expect(val).to.deep.equal([v]) const exists = await store.has(k) - expect(exists).to.be.eql(true) + expect(exists).to.equal(true) }) it('has - key not found', async () => { - expect(await store.has(CID.parse('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnA'))).to.be.eql(false) + expect(await store.has(CID.parse('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnA'))).to.be.false() }) it('has and delete', async () => { @@ -47,10 +48,10 @@ describe('Tiered', () => { const v = uint8ArrayFromString('world') await store.put(k, v) let res = await Promise.all([ms[0].has(k), ms[1].has(k)]) - expect(res).to.be.eql([true, true]) + expect(res).to.deep.equal([true, true]) await store.delete(k) res = await Promise.all([ms[0].has(k), ms[1].has(k)]) - expect(res).to.be.eql([false, false]) + expect(res).to.deep.equal([false, false]) }) }) diff --git a/packages/blockstore-core/typedoc.json b/packages/blockstore-core/typedoc.json index 57c0b938..b22a681e 100644 --- a/packages/blockstore-core/typedoc.json +++ b/packages/blockstore-core/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts", "./src/base.ts", diff --git a/packages/blockstore-fs/CHANGELOG.md b/packages/blockstore-fs/CHANGELOG.md index 8de9b176..d8256f6d 100644 --- a/packages/blockstore-fs/CHANGELOG.md +++ b/packages/blockstore-fs/CHANGELOG.md @@ -1,3 +1,85 @@ +## [blockstore-fs-v3.0.2](https://github.com/ipfs/js-stores/compare/blockstore-fs-3.0.1...blockstore-fs-3.0.2) (2025-10-03) + +### Bug Fixes + +* accept iterables, return generators ([a685552](https://github.com/ipfs/js-stores/commit/a685552f330c5871e60dcee2632c393900cf36f5)) + +## [blockstore-fs-v3.0.1](https://github.com/ipfs/js-stores/compare/blockstore-fs-3.0.0...blockstore-fs-3.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [blockstore-fs-v3.0.0](https://github.com/ipfs/js-stores/compare/blockstore-fs-2.0.5...blockstore-fs-3.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +## [blockstore-fs-v2.0.5](https://github.com/ipfs/js-stores/compare/blockstore-fs-2.0.4...blockstore-fs-2.0.5) (2025-09-02) + +### Dependencies + +* bump race-signal from 1.1.3 to 2.0.0 ([#355](https://github.com/ipfs/js-stores/issues/355)) ([518fee8](https://github.com/ipfs/js-stores/commit/518fee89d3430534c0ec39551e920447fd558581)) + +## [blockstore-fs-v2.0.4](https://github.com/ipfs/js-stores/compare/blockstore-fs-2.0.3...blockstore-fs-2.0.4) (2025-05-28) + +### Bug Fixes + +* improve abort signal support ([#350](https://github.com/ipfs/js-stores/issues/350)) ([e17d770](https://github.com/ipfs/js-stores/commit/e17d770cc2fcee77cb0152a855abf162e5a91a99)) + +## [blockstore-fs-v2.0.3](https://github.com/ipfs/js-stores/compare/blockstore-fs-2.0.2...blockstore-fs-2.0.3) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [blockstore-fs-v2.0.2](https://github.com/ipfs/js-stores/compare/blockstore-fs-2.0.1...blockstore-fs-2.0.2) (2024-09-17) + +### Tests + +* add separate-thread concurrency test ([#305](https://github.com/ipfs/js-stores/issues/305)) ([5e3114e](https://github.com/ipfs/js-stores/commit/5e3114e0160ba8366067359f724c6e49807dfb21)), closes [#285](https://github.com/ipfs/js-stores/issues/285) [#284](https://github.com/ipfs/js-stores/issues/284) + +## [blockstore-fs-v2.0.1](https://github.com/ipfs/js-stores/compare/blockstore-fs-2.0.0...blockstore-fs-2.0.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## blockstore-fs [2.0.0](https://github.com/ipfs/js-stores/compare/blockstore-fs-1.1.11...blockstore-fs-2.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + + + +### Dependencies + +* **interface-blockstore:** upgraded to 5.3.0 +* **interface-store:** upgraded to 6.0.0 +* **interface-blockstore-tests:** upgraded to 7.0.0 + +## blockstore-fs [1.1.11](https://github.com/ipfs/js-stores/compare/blockstore-fs-1.1.10...blockstore-fs-1.1.11) (2024-08-01) + + +### Dependencies + +* bump it-glob from 2.0.7 to 3.0.1 ([#306](https://github.com/ipfs/js-stores/issues/306)) ([8f6313f](https://github.com/ipfs/js-stores/commit/8f6313f8a22cb537aeeac2a048aad644d3c9a7d2)) + ## blockstore-fs [1.1.10](https://github.com/ipfs/js-stores/compare/blockstore-fs-v1.1.9...blockstore-fs-1.1.10) (2024-02-12) diff --git a/packages/blockstore-fs/CODE_OF_CONDUCT.md b/packages/blockstore-fs/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/blockstore-fs/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/blockstore-fs/LICENSE b/packages/blockstore-fs/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/blockstore-fs/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/blockstore-fs/LICENSE-APACHE b/packages/blockstore-fs/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/blockstore-fs/LICENSE-APACHE +++ b/packages/blockstore-fs/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/blockstore-fs/README.md b/packages/blockstore-fs/README.md index a7e4b185..61af9aeb 100644 --- a/packages/blockstore-fs/README.md +++ b/packages/blockstore-fs/README.md @@ -9,6 +9,21 @@ # About + + A Blockstore implementation that stores blocks in the local filesystem. ## Example @@ -33,8 +48,8 @@ $ npm i blockstore-fs Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/blockstore-fs/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/blockstore-fs/LICENSE-MIT) / ) # Contribute diff --git a/packages/blockstore-fs/package.json b/packages/blockstore-fs/package.json index 469a2e91..1919c70e 100644 --- a/packages/blockstore-fs/package.json +++ b/packages/blockstore-fs/package.json @@ -1,6 +1,6 @@ { "name": "blockstore-fs", - "version": "1.1.10", + "version": "3.0.2", "description": "Blockstore implementation with file system backend", "license": "Apache-2.0 OR MIT", "homepage": "https://github.com/ipfs/js-stores/tree/main/packages/blockstore-fs#readme", @@ -56,15 +56,98 @@ "import": "./dist/src/sharding.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": [ - "tsconfig.json", - "benchmarks/encoding/tsconfig.json" + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } ], - "sourceType": "module" - } + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "clean": "aegir clean", @@ -73,20 +156,23 @@ "test": "aegir test -t node -t electron-main", "test:node": "aegir test -t node", "test:electron-main": "aegir test -t electron-main", - "dep-check": "aegir dep-check" + "dep-check": "aegir dep-check", + "release": "aegir release" }, "dependencies": { - "blockstore-core": "^4.0.0", - "fast-write-atomic": "^0.2.1", - "interface-blockstore": "^5.0.0", - "interface-store": "^5.0.0", - "it-glob": "^2.0.6", - "it-map": "^3.0.5", - "it-parallel-batch": "^3.0.4", - "multiformats": "^13.0.1" + "interface-blockstore": "^6.0.0", + "interface-store": "^7.0.0", + "it-glob": "^3.0.3", + "it-map": "^3.1.3", + "it-parallel-batch": "^3.0.8", + "multiformats": "^13.3.6", + "race-signal": "^2.0.0", + "steno": "^4.0.2" }, "devDependencies": { - "aegir": "^42.2.3", - "interface-blockstore-tests": "^6.0.0" + "aegir": "^47.0.16", + "interface-blockstore-tests": "^8.0.0", + "it-all": "^3.0.9", + "threads": "^1.7.0" } } diff --git a/packages/blockstore-fs/src/index.ts b/packages/blockstore-fs/src/index.ts index b2e0af65..d2e9b293 100644 --- a/packages/blockstore-fs/src/index.ts +++ b/packages/blockstore-fs/src/index.ts @@ -14,41 +14,46 @@ import fs from 'node:fs/promises' import path from 'node:path' -import { promisify } from 'node:util' -import { - Errors -} from 'blockstore-core' -// @ts-expect-error no types -import fwa from 'fast-write-atomic' +import { OpenFailedError, PutFailedError, NotFoundError, DeleteFailedError } from 'interface-store' import glob from 'it-glob' import map from 'it-map' import parallelBatch from 'it-parallel-batch' -import { NextToLast, type ShardingStrategy } from './sharding.js' +import { raceSignal } from 'race-signal' +import { Writer } from 'steno' +import { NextToLast } from './sharding.js' +import type { ShardingStrategy } from './sharding.js' import type { Blockstore, Pair } from 'interface-blockstore' -import type { AwaitIterable } from 'interface-store' +import type { AbortOptions, AwaitGenerator, AwaitIterable } from 'interface-store' import type { CID } from 'multiformats/cid' - -const writeAtomic = promisify(fwa) +import type { FileHandle } from 'node:fs/promises' /** * Write a file atomically */ -async function writeFile (file: string, contents: Uint8Array): Promise { +async function writeFile (file: string, contents: Uint8Array | AwaitIterable, options?: AbortOptions): Promise { try { - await writeAtomic(file, contents) + options?.signal?.throwIfAborted() + await raceSignal(fs.mkdir(path.dirname(file), { + recursive: true + }), options?.signal) + + const writer = new Writer(file) + + options?.signal?.throwIfAborted() + + await writer.write(contents) } catch (err: any) { - if (err.code === 'EPERM' && err.syscall === 'rename') { - // fast-write-atomic writes a file to a temp location before renaming it. - // On Windows, if the final file already exists this error is thrown. - // No such error is thrown on Linux/Mac + if (err.syscall === 'rename' && ['ENOENT', 'EPERM'].includes(err.code)) { + // steno writes a file to a temp location before renaming it. + // If the final file already exists this error is thrown. // Make sure we can read & write to this file - await fs.access(file, fs.constants.F_OK | fs.constants.W_OK) + options?.signal?.throwIfAborted() + await raceSignal(fs.access(file, fs.constants.F_OK | fs.constants.W_OK), options?.signal) // The file was created by another context - this means there were // attempts to write the same block by two different function calls return } - throw err } } @@ -122,7 +127,7 @@ export class FsBlockstore implements Blockstore { await fs.access(this.path, fs.constants.F_OK | fs.constants.W_OK) if (this.errorIfExists) { - throw Errors.openFailedError(new Error(`Blockstore directory: ${this.path} already exists`)) + throw new OpenFailedError(`Blockstore directory: ${this.path} already exists`) } } catch (err: any) { if (err.code === 'ENOENT') { @@ -130,7 +135,7 @@ export class FsBlockstore implements Blockstore { await fs.mkdir(this.path, { recursive: true }) return } else { - throw Errors.openFailedError(new Error(`Blockstore directory: ${this.path} does not exist`)) + throw new OpenFailedError(`Blockstore directory: ${this.path} does not exist`) } } @@ -142,29 +147,23 @@ export class FsBlockstore implements Blockstore { await Promise.resolve() } - async put (key: CID, val: Uint8Array): Promise { + async put (key: CID, val: Uint8Array | AwaitIterable, options?: AbortOptions): Promise { const { dir, file } = this.shardingStrategy.encode(key) try { - if (dir != null && dir !== '') { - await fs.mkdir(path.join(this.path, dir), { - recursive: true - }) - } - - await writeFile(path.join(this.path, dir, file), val) + await writeFile(path.join(this.path, dir, file), val, options) return key } catch (err: any) { - throw Errors.putFailedError(err) + throw new PutFailedError(String(err)) } } - async * putMany (source: AwaitIterable): AsyncIterable { + async * putMany (source: AwaitIterable, options?: AbortOptions): AsyncGenerator { yield * parallelBatch( - map(source, ({ cid, block }) => { + map(source, ({ cid, bytes }) => { return async () => { - await this.put(cid, block) + await this.put(cid, bytes, options) return cid } @@ -173,23 +172,32 @@ export class FsBlockstore implements Blockstore { ) } - async get (key: CID): Promise { + async * get (key: CID, options?: AbortOptions): AsyncGenerator { const { dir, file } = this.shardingStrategy.encode(key) + let handle: fs.FileHandle | undefined try { - return await fs.readFile(path.join(this.path, dir, file)) + options?.signal?.throwIfAborted() + + handle = await raceSignal(fs.open(path.join(this.path, dir, file)), options?.signal) + + yield * handle.createReadStream() } catch (err: any) { - throw Errors.notFoundError(err) + if (handle != null) { + await raceSignal(handle.close(), options?.signal) + } + + throw new NotFoundError(String(err)) } } - async * getMany (source: AwaitIterable): AsyncIterable { + async * getMany (source: AwaitIterable, options?: AbortOptions): AsyncGenerator { yield * parallelBatch( map(source, key => { return async () => { return { cid: key, - block: await this.get(key) + bytes: this.get(key, options) } } }), @@ -197,25 +205,26 @@ export class FsBlockstore implements Blockstore { ) } - async delete (key: CID): Promise { + async delete (key: CID, options?: AbortOptions): Promise { const { dir, file } = this.shardingStrategy.encode(key) try { - await fs.unlink(path.join(this.path, dir, file)) + options?.signal?.throwIfAborted() + await raceSignal(fs.unlink(path.join(this.path, dir, file)), options?.signal) } catch (err: any) { if (err.code === 'ENOENT') { return } - throw Errors.deleteFailedError(err) + throw new DeleteFailedError(String(err)) } } - async * deleteMany (source: AwaitIterable): AsyncIterable { + async * deleteMany (source: AwaitIterable, options?: AbortOptions): AsyncGenerator { yield * parallelBatch( map(source, key => { return async () => { - await this.delete(key) + await this.delete(key, options) return key } @@ -224,21 +233,19 @@ export class FsBlockstore implements Blockstore { ) } - /** - * Check for the existence of the given key - */ - async has (key: CID): Promise { + async has (key: CID, options?: AbortOptions): Promise { const { dir, file } = this.shardingStrategy.encode(key) try { - await fs.access(path.join(this.path, dir, file)) + options?.signal?.throwIfAborted() + await raceSignal(fs.access(path.join(this.path, dir, file)), options?.signal) } catch (err: any) { return false } return true } - async * getAll (): AsyncIterable { + async * getAll (options?: AbortOptions): AwaitGenerator { const pattern = `**/*${this.shardingStrategy.extension}` .split(path.sep) .join('/') @@ -248,14 +255,26 @@ export class FsBlockstore implements Blockstore { for await (const file of files) { try { - const buf = await fs.readFile(file) - + options?.signal?.throwIfAborted() const pair: Pair = { cid: this.shardingStrategy.decode(file), - block: buf + bytes: (async function * () { + let handle: FileHandle | undefined + + try { + handle = await raceSignal(fs.open(file), options?.signal) + + yield * handle.createReadStream() + } finally { + if (handle != null) { + await raceSignal(handle.close(), options?.signal) + } + } + })() } yield pair + options?.signal?.throwIfAborted() } catch (err: any) { // if keys are removed from the blockstore while the query is // running, we may encounter missing files. diff --git a/packages/blockstore-fs/test/fixtures/writer-worker.ts b/packages/blockstore-fs/test/fixtures/writer-worker.ts new file mode 100644 index 00000000..1103c33f --- /dev/null +++ b/packages/blockstore-fs/test/fixtures/writer-worker.ts @@ -0,0 +1,31 @@ +import { CID } from 'multiformats/cid' +// @ts-expect-error types are broken: https://github.com/andywer/threads.js/pull/470 +import { expose } from 'threads/worker' +import { FsBlockstore } from '../../src/index.js' + +let fs: FsBlockstore +expose({ + async isReady (path: string) { + fs = new FsBlockstore(path) + try { + await fs.open() + return true + } catch (err) { + // eslint-disable-next-line no-console + console.error('Error opening blockstore', err) + throw err + } + }, + async put (cidString: string, value: Uint8Array) { + const key = CID.parse(cidString) + try { + return await fs.put(key, (async function * () { + yield value + })()) + } catch (err) { + // eslint-disable-next-line no-console + console.error('Error putting block', err) + throw err + } + } +}) diff --git a/packages/blockstore-fs/test/index.spec.ts b/packages/blockstore-fs/test/index.spec.ts index 696bacbc..4582521c 100644 --- a/packages/blockstore-fs/test/index.spec.ts +++ b/packages/blockstore-fs/test/index.spec.ts @@ -1,11 +1,14 @@ -/* eslint-env mocha */ +import { setMaxListeners } from 'node:events' import fs from 'node:fs/promises' import os from 'node:os' import path from 'node:path' import { expect } from 'aegir/chai' import { interfaceBlockstoreTests } from 'interface-blockstore-tests' +import all from 'it-all' import { base32 } from 'multiformats/bases/base32' import { CID } from 'multiformats/cid' +// @ts-expect-error types are broken: https://github.com/andywer/threads.js/pull/470 +import { spawn, Thread, Worker } from 'threads' import { FsBlockstore } from '../src/index.js' import { FlatDirectory, NextToLast } from '../src/sharding.js' @@ -44,7 +47,7 @@ describe('FsBlockstore', () => { const dir = path.join(os.tmpdir(), `test-${Math.random()}`) const store = new FsBlockstore(dir, { createIfMissing: false }) await expect(store.open()).to.eventually.be.rejected - .with.property('code', 'ERR_OPEN_FAILED') + .with.property('name', 'OpenFailedError') }) it('errorIfExists: true - folder exists', async () => { @@ -54,7 +57,7 @@ describe('FsBlockstore', () => { }) const store = new FsBlockstore(dir, { errorIfExists: true }) await expect(store.open()).to.eventually.be.rejected - .with.property('code', 'ERR_OPEN_FAILED') + .with.property('name', 'OpenFailedError') }) }) @@ -64,11 +67,13 @@ describe('FsBlockstore', () => { await fs.open() const key = CID.parse('QmeimKZyjcBnuXmAD9zMnSjM9JodTbgGT3gutofkTqz9rE') - await fs.put(key, Uint8Array.from([0, 1, 2, 3])) + await fs.put(key, (async function * () { + yield Uint8Array.from([0, 1, 2, 3]) + })()) await fs.delete(key) - await expect(fs.get(key)).to.eventually.be.rejected - .with.property('code', 'ERR_NOT_FOUND') + await expect(all(fs.get(key))).to.eventually.be.rejected + .with.property('name', 'NotFoundError') }) it('deleting non-existent files', async () => { @@ -80,8 +85,8 @@ describe('FsBlockstore', () => { await fs.delete(key) - await expect(fs.get(key)).to.eventually.be.rejected - .with.property('code', 'ERR_NOT_FOUND') + await expect(all(fs.get(key))).to.eventually.be.rejected + .with.property('name', 'NotFoundError') }) describe('interface-blockstore (flat directory)', () => { @@ -97,7 +102,10 @@ describe('FsBlockstore', () => { teardown: async (store) => { await store.close() await fs.rm(store.path, { - recursive: true + recursive: true, + force: true, + maxRetries: 5, + retryDelay: 1_000 }) } }) @@ -114,7 +122,10 @@ describe('FsBlockstore', () => { teardown: async (store) => { await store.close() await fs.rm(store.path, { - recursive: true + recursive: true, + force: true, + maxRetries: 5, + retryDelay: 1_000 }) } }) @@ -136,7 +147,10 @@ describe('FsBlockstore', () => { teardown: async (store) => { await store.close() await fs.rm(store.path, { - recursive: true + recursive: true, + force: true, + maxRetries: 5, + retryDelay: 1_000 }) } }) @@ -151,11 +165,48 @@ describe('FsBlockstore', () => { const value = utf8Encoder.encode('Hello world') await Promise.all( - new Array(100).fill(0).map(async () => { await fs.put(key, value) }) + new Array(100).fill(0).map(async () => { + await fs.put(key, (async function * () { + yield value + })()) + }) ) - const res = await fs.get(key) + const res = await all(fs.get(key)) + + expect(res).to.deep.equal([value]) + }) - expect(res).to.deep.equal(value) + /** + * This test spawns 10 workers that concurrently write to the same blockstore. + * it's different from the previous test because it uses workers to write to the blockstore + * which means that the writes are happening in parallel in different threads. + */ + it('can survive concurrent worker writes', async () => { + const dir = path.join(os.tmpdir(), `test-${Math.random()}`) + const key = CID.parse('QmeimKZyjcBnuXmAD9zMnSjM9JodTbgGT3gutofkTqz9rE') + const workers = await Promise.all(new Array(10).fill(0).map(async () => { + const w = new Worker('./fixtures/writer-worker.js') + setMaxListeners(Infinity, w) + const worker = await spawn(w) + await worker.isReady(dir) + return worker + })) + + try { + const value = utf8Encoder.encode('Hello world') + // 100 iterations of looping over all workers and putting the same key value pair + await Promise.all(new Array(100).fill(0).map(async () => { + return Promise.all(workers.map(async (worker: any) => worker.put(key.toString(), value))) + })) + + const fs = new FsBlockstore(dir) + await fs.open() + const res = await all(fs.get(key)) + + expect(res).to.deep.equal([value]) + } finally { + await Promise.all(workers.map(async (worker: any) => Thread.terminate(worker))) + } }) }) diff --git a/packages/blockstore-fs/tsconfig.json b/packages/blockstore-fs/tsconfig.json index e27c7fa6..73395b59 100644 --- a/packages/blockstore-fs/tsconfig.json +++ b/packages/blockstore-fs/tsconfig.json @@ -8,9 +8,6 @@ "test" ], "references": [ - { - "path": "../blockstore-core" - }, { "path": "../interface-blockstore" }, diff --git a/packages/blockstore-fs/typedoc.json b/packages/blockstore-fs/typedoc.json index 332ef6a2..9d7f31b2 100644 --- a/packages/blockstore-fs/typedoc.json +++ b/packages/blockstore-fs/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts", "./src/sharding.ts" diff --git a/packages/blockstore-idb/CHANGELOG.md b/packages/blockstore-idb/CHANGELOG.md index ee7a45aa..f29869f5 100644 --- a/packages/blockstore-idb/CHANGELOG.md +++ b/packages/blockstore-idb/CHANGELOG.md @@ -1,3 +1,67 @@ +## [blockstore-idb-v3.0.1](https://github.com/ipfs/js-stores/compare/blockstore-idb-3.0.0...blockstore-idb-3.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [blockstore-idb-v3.0.0](https://github.com/ipfs/js-stores/compare/blockstore-idb-2.0.4...blockstore-idb-3.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +## [blockstore-idb-v2.0.4](https://github.com/ipfs/js-stores/compare/blockstore-idb-2.0.3...blockstore-idb-2.0.4) (2025-09-02) + +### Dependencies + +* bump race-signal from 1.1.3 to 2.0.0 ([#355](https://github.com/ipfs/js-stores/issues/355)) ([518fee8](https://github.com/ipfs/js-stores/commit/518fee89d3430534c0ec39551e920447fd558581)) + +## [blockstore-idb-v2.0.3](https://github.com/ipfs/js-stores/compare/blockstore-idb-2.0.2...blockstore-idb-2.0.3) (2025-05-28) + +### Bug Fixes + +* improve abort signal support ([#350](https://github.com/ipfs/js-stores/issues/350)) ([e17d770](https://github.com/ipfs/js-stores/commit/e17d770cc2fcee77cb0152a855abf162e5a91a99)) + +## [blockstore-idb-v2.0.2](https://github.com/ipfs/js-stores/compare/blockstore-idb-2.0.1...blockstore-idb-2.0.2) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [blockstore-idb-v2.0.1](https://github.com/ipfs/js-stores/compare/blockstore-idb-2.0.0...blockstore-idb-2.0.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## blockstore-idb [2.0.0](https://github.com/ipfs/js-stores/compare/blockstore-idb-1.1.8...blockstore-idb-2.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + + + +### Dependencies + +* **blockstore-core:** upgraded to 5.0.0 +* **interface-blockstore:** upgraded to 5.3.0 +* **interface-store:** upgraded to 6.0.0 +* **interface-blockstore-tests:** upgraded to 7.0.0 + ## blockstore-idb [1.1.8](https://github.com/ipfs/js-stores/compare/blockstore-idb-v1.1.7...blockstore-idb-1.1.8) (2024-02-12) diff --git a/packages/blockstore-idb/CODE_OF_CONDUCT.md b/packages/blockstore-idb/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/blockstore-idb/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/blockstore-idb/LICENSE b/packages/blockstore-idb/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/blockstore-idb/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/blockstore-idb/LICENSE-APACHE b/packages/blockstore-idb/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/blockstore-idb/LICENSE-APACHE +++ b/packages/blockstore-idb/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/blockstore-idb/README.md b/packages/blockstore-idb/README.md index 7009d9ac..906b5879 100644 --- a/packages/blockstore-idb/README.md +++ b/packages/blockstore-idb/README.md @@ -9,6 +9,21 @@ # About + + A Blockstore implementation for browsers that stores blocks in [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API). ## Example @@ -27,7 +42,7 @@ $ npm i blockstore-idb ## Browser ` @@ -41,8 +56,8 @@ Loading this module through a script tag will make it's exports available as `Bl Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/blockstore-idb/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/blockstore-idb/LICENSE-MIT) / ) # Contribute diff --git a/packages/blockstore-idb/package.json b/packages/blockstore-idb/package.json index f4a3ee04..c94a3c39 100644 --- a/packages/blockstore-idb/package.json +++ b/packages/blockstore-idb/package.json @@ -1,6 +1,6 @@ { "name": "blockstore-idb", - "version": "1.1.8", + "version": "3.0.1", "description": "Blockstore implementation with IndexedDB backend", "license": "Apache-2.0 OR MIT", "homepage": "https://github.com/ipfs/js-stores/tree/main/packages/blockstore-idb#readme", @@ -37,12 +37,98 @@ "import": "./dist/src/index.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true, - "sourceType": "module" - } + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "clean": "aegir clean", @@ -53,17 +139,22 @@ "test:chrome-webworker": "aegir test -t webworker", "test:firefox": "aegir test -t browser -- --browser firefox", "test:firefox-webworker": "aegir test -t webworker -- --browser firefox", - "dep-check": "aegir dep-check" + "dep-check": "aegir dep-check", + "release": "aegir release" }, "dependencies": { - "blockstore-core": "^4.0.0", - "idb": "^8.0.0", - "interface-blockstore": "^5.0.0", - "interface-store": "^5.0.0", - "multiformats": "^13.0.1" + "blockstore-core": "^6.0.0", + "idb": "^8.0.3", + "interface-blockstore": "^6.0.0", + "interface-store": "^7.0.0", + "it-all": "^3.0.9", + "it-to-buffer": "^4.0.10", + "multiformats": "^13.3.6", + "race-signal": "^2.0.0" }, "devDependencies": { - "aegir": "^42.2.3", - "interface-blockstore-tests": "^6.0.0" + "aegir": "^47.0.16", + "interface-blockstore-tests": "^8.0.0", + "it-drain": "^3.0.10" } } diff --git a/packages/blockstore-idb/src/index.ts b/packages/blockstore-idb/src/index.ts index 51048a1d..c75ba256 100644 --- a/packages/blockstore-idb/src/index.ts +++ b/packages/blockstore-idb/src/index.ts @@ -12,17 +12,19 @@ * ``` */ -import { - BaseBlockstore, - Errors -} from 'blockstore-core' -import { openDB, type IDBPDatabase, deleteDB } from 'idb' +import { BaseBlockstore } from 'blockstore-core' +import { openDB, deleteDB } from 'idb' +import { OpenFailedError, PutFailedError, NotFoundError } from 'interface-store' +import all from 'it-all' +import toBuffer from 'it-to-buffer' import { base32upper } from 'multiformats/bases/base32' import { CID } from 'multiformats/cid' import * as raw from 'multiformats/codecs/raw' import * as Digest from 'multiformats/hashes/digest' +import { raceSignal } from 'race-signal' +import type { IDBPDatabase } from 'idb' import type { Pair } from 'interface-blockstore' -import type { AbortOptions, AwaitIterable } from 'interface-store' +import type { AbortOptions, AwaitGenerator, AwaitIterable } from 'interface-store' import type { MultibaseCodec } from 'multiformats/bases/interface' export interface IDBBlockstoreInit { @@ -77,7 +79,7 @@ export class IDBBlockstore extends BaseBlockstore { } }) } catch (err: any) { - throw Errors.openFailedError(err) + throw new OpenFailedError(String(err)) } } @@ -85,21 +87,30 @@ export class IDBBlockstore extends BaseBlockstore { this.db?.close() } - async put (key: CID, val: Uint8Array): Promise { + async put (key: CID, val: Uint8Array | AwaitIterable, options?: AbortOptions): Promise { if (this.db == null) { throw new Error('Blockstore needs to be opened.') } - try { - await this.db.put(this.location, val, this.#encode(key)) + let buf: Uint8Array + + if (val instanceof Uint8Array) { + buf = val + } else { + buf = toBuffer(await all(val)) + } - return key + try { + options?.signal?.throwIfAborted() + await raceSignal(this.db.put(this.location, buf, this.#encode(key)), options?.signal) } catch (err: any) { - throw Errors.putFailedError(err) + throw new PutFailedError(String(err)) } + + return key } - async get (key: CID): Promise { + async * get (key: CID, options?: AbortOptions): AwaitGenerator { if (this.db == null) { throw new Error('Blockstore needs to be opened.') } @@ -107,52 +118,64 @@ export class IDBBlockstore extends BaseBlockstore { let val: Uint8Array | undefined try { - val = await this.db.get(this.location, this.#encode(key)) + options?.signal?.throwIfAborted() + val = await raceSignal(this.db.get(this.location, this.#encode(key)), options?.signal) } catch (err: any) { - throw Errors.putFailedError(err) + throw new PutFailedError(String(err)) } if (val === undefined) { - throw Errors.notFoundError() + throw new NotFoundError() } - return val + yield val } - async delete (key: CID): Promise { + async delete (key: CID, options?: AbortOptions): Promise { if (this.db == null) { throw new Error('Blockstore needs to be opened.') } try { - await this.db.delete(this.location, this.#encode(key)) + options?.signal?.throwIfAborted() + await raceSignal(this.db.delete(this.location, this.#encode(key)), options?.signal) } catch (err: any) { - throw Errors.putFailedError(err) + throw new PutFailedError(String(err)) } } - async has (key: CID): Promise { + async has (key: CID, options?: AbortOptions): Promise { if (this.db == null) { throw new Error('Blockstore needs to be opened.') } try { - return Boolean(await this.db.getKey(this.location, this.#encode(key))) + options?.signal?.throwIfAborted() + const result = await raceSignal(this.db.getKey(this.location, this.#encode(key)), options?.signal) + + return Boolean(result) } catch (err: any) { - throw Errors.putFailedError(err) + throw new PutFailedError(String(err)) } } - async * getAll (options?: AbortOptions): AwaitIterable { + async * getAll (options?: AbortOptions): AwaitGenerator { if (this.db == null) { throw new Error('Blockstore needs to be opened.') } + options?.signal?.throwIfAborted() + for (const key of await this.db.getAllKeys(this.location)) { - const cid = this.#decode(key.toString()) // eslint-disable-line @typescript-eslint/no-base-to-string - const block = await this.get(cid) + options?.signal?.throwIfAborted() + const cid = this.#decode(key.toString()) + + yield { + cid, + bytes: this.get(cid, options) + } - yield { cid, block } + options?.signal?.throwIfAborted() } } diff --git a/packages/blockstore-idb/test/index.spec.ts b/packages/blockstore-idb/test/index.spec.ts index e4b9434a..3fca5e7e 100644 --- a/packages/blockstore-idb/test/index.spec.ts +++ b/packages/blockstore-idb/test/index.spec.ts @@ -2,10 +2,12 @@ import { expect } from 'aegir/chai' import { interfaceBlockstoreTests } from 'interface-blockstore-tests' +import all from 'it-all' +import drain from 'it-drain' import { CID } from 'multiformats/cid' import { IDBBlockstore } from '../src/index.js' -describe('IndexedDB Blockstore', function () { +describe('IndexedDB Blockstore', () => { describe('interface-blockstore (idb)', () => { interfaceBlockstoreTests({ async setup () { @@ -31,13 +33,13 @@ describe('IndexedDB Blockstore', function () { it('should not explode under unreasonable load', function (done) { this.timeout(10000) - const updater = setInterval(async () => { // eslint-disable-line @typescript-eslint/no-misused-promises + const updater = setInterval(async () => { try { const key = CID.parse('QmaQwYWpchozXhFv8nvxprECWBSCEppN9dfd2VQiJfRo3F') await store.put(key, Uint8Array.from([0, 1, 2, 3])) await store.has(key) - await store.get(key) + await drain(store.get(key)) } catch (err) { clearInterval(updater) clearInterval(mutatorQuery) @@ -46,16 +48,16 @@ describe('IndexedDB Blockstore', function () { } }, 0) - const mutatorQuery = setInterval(async () => { // eslint-disable-line @typescript-eslint/no-misused-promises + const mutatorQuery = setInterval(async () => { try { for await (const { cid } of store.getAll()) { - await store.get(cid) + await drain(store.get(cid)) const otherKey = CID.parse('QmaQwYWpchozXhFv8nvxprECWBSCEppN9dfd2VQiJfRo3F') const otherValue = Uint8Array.from([0, 1, 2, 3]) await store.put(otherKey, otherValue) - const res = await store.get(otherKey) - expect(res).to.deep.equal(otherValue) + const res = await all(store.get(otherKey)) + expect(res).to.deep.equal([otherValue]) } } catch (err) { clearInterval(updater) @@ -65,7 +67,7 @@ describe('IndexedDB Blockstore', function () { } }, 0) - const readOnlyQuery = setInterval(async () => { // eslint-disable-line @typescript-eslint/no-misused-promises + const readOnlyQuery = setInterval(async () => { try { for await (const { cid } of store.getAll()) { await store.has(cid) diff --git a/packages/blockstore-idb/typedoc.json b/packages/blockstore-idb/typedoc.json index f599dc72..db0b0747 100644 --- a/packages/blockstore-idb/typedoc.json +++ b/packages/blockstore-idb/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts" ] diff --git a/packages/blockstore-level/CHANGELOG.md b/packages/blockstore-level/CHANGELOG.md index dd3a82f7..83a4f445 100644 --- a/packages/blockstore-level/CHANGELOG.md +++ b/packages/blockstore-level/CHANGELOG.md @@ -1,3 +1,83 @@ +## [blockstore-level-v3.0.2](https://github.com/ipfs/js-stores/compare/blockstore-level-3.0.1...blockstore-level-3.0.2) (2025-10-03) + +### Dependencies + +* bump level from 8.0.1 to 10.0.0 ([#356](https://github.com/ipfs/js-stores/issues/356)) ([c0ec61f](https://github.com/ipfs/js-stores/commit/c0ec61fe965e3bad9d607a0bd3a3c750f00f41d0)) + +## [blockstore-level-v3.0.1](https://github.com/ipfs/js-stores/compare/blockstore-level-3.0.0...blockstore-level-3.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [blockstore-level-v3.0.0](https://github.com/ipfs/js-stores/compare/blockstore-level-2.0.5...blockstore-level-3.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +## [blockstore-level-v2.0.5](https://github.com/ipfs/js-stores/compare/blockstore-level-2.0.4...blockstore-level-2.0.5) (2025-09-02) + +### Bug Fixes + +* readme typos ([e6b5653](https://github.com/ipfs/js-stores/commit/e6b56533b68e6ed9b90ca3e3f35af8577041a9a2)) + +### Dependencies + +* bump race-signal from 1.1.3 to 2.0.0 ([#355](https://github.com/ipfs/js-stores/issues/355)) ([518fee8](https://github.com/ipfs/js-stores/commit/518fee89d3430534c0ec39551e920447fd558581)) + +## [blockstore-level-v2.0.4](https://github.com/ipfs/js-stores/compare/blockstore-level-2.0.3...blockstore-level-2.0.4) (2025-09-02) + +### Bug Fixes + +* deprecate blockstore-level and datastore-fs ([#353](https://github.com/ipfs/js-stores/issues/353)) ([ebc7912](https://github.com/ipfs/js-stores/commit/ebc7912696d5bd9dc991ece5f0c0d4acfb1f9400)) + +## [blockstore-level-v2.0.3](https://github.com/ipfs/js-stores/compare/blockstore-level-2.0.2...blockstore-level-2.0.3) (2025-05-28) + +### Bug Fixes + +* improve abort signal support ([#350](https://github.com/ipfs/js-stores/issues/350)) ([e17d770](https://github.com/ipfs/js-stores/commit/e17d770cc2fcee77cb0152a855abf162e5a91a99)) + +## [blockstore-level-v2.0.2](https://github.com/ipfs/js-stores/compare/blockstore-level-2.0.1...blockstore-level-2.0.2) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [blockstore-level-v2.0.1](https://github.com/ipfs/js-stores/compare/blockstore-level-2.0.0...blockstore-level-2.0.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## blockstore-level [2.0.0](https://github.com/ipfs/js-stores/compare/blockstore-level-1.1.8...blockstore-level-2.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + + + +### Dependencies + +* **blockstore-core:** upgraded to 5.0.0 +* **interface-blockstore:** upgraded to 5.3.0 +* **interface-store:** upgraded to 6.0.0 +* **interface-blockstore-tests:** upgraded to 7.0.0 + ## blockstore-level [1.1.8](https://github.com/ipfs/js-stores/compare/blockstore-level-v1.1.7...blockstore-level-1.1.8) (2024-02-12) diff --git a/packages/blockstore-level/CODE_OF_CONDUCT.md b/packages/blockstore-level/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/blockstore-level/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/blockstore-level/LICENSE b/packages/blockstore-level/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/blockstore-level/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/blockstore-level/LICENSE-APACHE b/packages/blockstore-level/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/blockstore-level/LICENSE-APACHE +++ b/packages/blockstore-level/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/blockstore-level/README.md b/packages/blockstore-level/README.md index a7cdfd73..134deee1 100644 --- a/packages/blockstore-level/README.md +++ b/packages/blockstore-level/README.md @@ -1,5 +1,9 @@ # blockstore-level +## ⚠️ Deprecation Warning + +**This package is deprecated. Instead, use `blockstore-fs` in Node.js, and `blockstore-idb` in browsers.** + [![ipfs.tech](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.tech) [![Discuss](https://img.shields.io/discourse/https/discuss.ipfs.tech/posts.svg?style=flat-square)](https://discuss.ipfs.tech) [![codecov](https://img.shields.io/codecov/c/github/ipfs/js-stores.svg?style=flat-square)](https://codecov.io/gh/ipfs/js-stores) @@ -9,9 +13,24 @@ # About -A Blockstore implementation that uses a flavour of [Level](https://leveljs.org/) as a backend. + + +⚠️ This package is deprecated. Instead, use `blockstore-fs` in Node.js, and `blockstore-idb` in browsers. + +A Blockstore implementation that uses a flavour of [Level](https://leveljs.org/) as a backend. ## Example @@ -29,7 +48,7 @@ $ npm i blockstore-level ## Browser ` @@ -43,8 +62,8 @@ Loading this module through a script tag will make it's exports available as `Bl Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/blockstore-level/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/blockstore-level/LICENSE-MIT) / ) # Contribute diff --git a/packages/blockstore-level/package.json b/packages/blockstore-level/package.json index 20cb946d..d313ac1c 100644 --- a/packages/blockstore-level/package.json +++ b/packages/blockstore-level/package.json @@ -1,6 +1,6 @@ { "name": "blockstore-level", - "version": "1.1.8", + "version": "3.0.2", "description": "Blockstore implementation with level(up|down) backend", "license": "Apache-2.0 OR MIT", "homepage": "https://github.com/ipfs/js-stores/tree/main/packages/blockstore-level#readme", @@ -38,12 +38,98 @@ "import": "./dist/src/index.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true, - "sourceType": "module" - } + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "clean": "aegir clean", @@ -56,19 +142,24 @@ "test:firefox": "aegir test -t browser -- --browser firefox", "test:firefox-webworker": "aegir test -t webworker -- --browser firefox", "test:electron-main": "aegir test -t electron-main", - "dep-check": "aegir dep-check" + "dep-check": "aegir dep-check", + "release": "aegir release" }, "dependencies": { - "blockstore-core": "^4.0.0", - "interface-blockstore": "^5.0.0", - "interface-store": "^5.0.0", - "level": "^8.0.1", - "multiformats": "^13.0.1" + "blockstore-core": "^6.0.0", + "interface-blockstore": "^6.0.0", + "interface-store": "^7.0.0", + "it-all": "^3.0.9", + "it-to-buffer": "^4.0.10", + "level": "^10.0.0", + "multiformats": "^13.3.6", + "race-signal": "^2.0.0", + "uint8arrays": "^5.1.0" }, "devDependencies": { - "aegir": "^42.2.3", - "interface-blockstore-tests": "^6.0.0", + "aegir": "^47.0.16", + "interface-blockstore-tests": "^8.0.0", "ipfs-utils": "^9.0.14", - "memory-level": "^1.0.0" + "memory-level": "^3.1.0" } } diff --git a/packages/blockstore-level/src/index.ts b/packages/blockstore-level/src/index.ts index 21383ce3..a1094391 100644 --- a/packages/blockstore-level/src/index.ts +++ b/packages/blockstore-level/src/index.ts @@ -1,9 +1,9 @@ /** * @packageDocumentation * - * A Blockstore implementation that uses a flavour of [Level](https://leveljs.org/) as a backend. + * ⚠️ This package is deprecated. Instead, use `blockstore-fs` in Node.js, and `blockstore-idb` in browsers. * - * N.b. this is here largely for the sake of completeness, in node you should probably use FSDatastore, in browsers you should probably use IDBDatastore. + * A Blockstore implementation that uses a flavour of [Level](https://leveljs.org/) as a backend. * * @example * @@ -14,14 +14,19 @@ * ``` */ -import { BaseBlockstore, Errors } from 'blockstore-core' +import { BaseBlockstore } from 'blockstore-core' +import { DeleteFailedError, GetFailedError, NotFoundError, OpenFailedError, PutFailedError } from 'interface-store' +import all from 'it-all' +import toBuffer from 'it-to-buffer' import { Level } from 'level' import { base32upper } from 'multiformats/bases/base32' import { CID } from 'multiformats/cid' import * as raw from 'multiformats/codecs/raw' import * as Digest from 'multiformats/hashes/digest' +import { raceSignal } from 'race-signal' +import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import type { Pair } from 'interface-blockstore' -import type { AbortOptions, AwaitIterable } from 'interface-store' +import type { AbortOptions, AwaitGenerator, AwaitIterable } from 'interface-store' import type { DatabaseOptions, OpenOptions, IteratorOptions } from 'level' import type { MultibaseCodec } from 'multiformats/bases/interface' @@ -73,52 +78,60 @@ export class LevelBlockstore extends BaseBlockstore { try { await this.db.open(this.opts) } catch (err: any) { - throw Errors.openFailedError(err) + throw new OpenFailedError(String(err)) } } - async put (key: CID, value: Uint8Array): Promise { + async put (key: CID, value: Uint8Array | AwaitIterable, options?: AbortOptions): Promise { try { - await this.db.put(this.#encode(key), value) + options?.signal?.throwIfAborted() + + let buf: Uint8Array + + if (value instanceof Uint8Array) { + buf = value + } else { + buf = toBuffer(await raceSignal(all(value), options?.signal)) + } - return key + await raceSignal(this.db.put(this.#encode(key), buf), options?.signal) } catch (err: any) { - throw Errors.putFailedError(err) + throw new PutFailedError(String(err)) } + + return key } - async get (key: CID): Promise { - let data + async * get (key: CID, options?: AbortOptions): AwaitGenerator { + let buf + try { - data = await this.db.get(this.#encode(key)) + options?.signal?.throwIfAborted() + buf = await raceSignal(this.db.get(this.#encode(key)), options?.signal) } catch (err: any) { - if (err.notFound != null) { - throw Errors.notFoundError(err) - } + throw new GetFailedError(String(err)) + } - throw Errors.getFailedError(err) + if (buf == null) { + throw new NotFoundError() } - return data + + yield buf } - async has (key: CID): Promise { - try { - await this.db.get(this.#encode(key)) - } catch (err: any) { - if (err.notFound != null) { - return false - } + async has (key: CID, options?: AbortOptions): Promise { + options?.signal?.throwIfAborted() + const buf = await raceSignal(this.db.get(this.#encode(key)), options?.signal) - throw err - } - return true + return buf != null } - async delete (key: CID): Promise { + async delete (key: CID, options?: AbortOptions): Promise { try { - await this.db.del(this.#encode(key)) + options?.signal?.throwIfAborted() + await raceSignal(this.db.del(this.#encode(key)), options?.signal) } catch (err: any) { - throw Errors.deleteFailedError(err) + throw new DeleteFailedError(String(err)) } } @@ -126,13 +139,17 @@ export class LevelBlockstore extends BaseBlockstore { await this.db.close() } - async * getAll (options?: AbortOptions | undefined): AwaitIterable { - for await (const { key, value } of this.#query({ values: true })) { - yield { cid: this.#decode(key), block: value } + async * getAll (options?: AbortOptions | undefined): AwaitGenerator { + options?.signal?.throwIfAborted() + + for await (const { key, value } of this.#query({ values: true }, options)) { + yield { cid: this.#decode(key), bytes: value } } } - async * #query (opts: { values: boolean, prefix?: string }): AsyncIterable<{ key: string, value: Uint8Array }> { + async * #query (opts: { values: boolean, prefix?: string }, options?: AbortOptions): AwaitGenerator<{ key: string, value: AwaitGenerator }> { + options?.signal?.throwIfAborted() + const iteratorOpts: IteratorOptions = { keys: true, keyEncoding: 'buffer', @@ -152,8 +169,16 @@ export class LevelBlockstore extends BaseBlockstore { try { for await (const [key, value] of li) { - // @ts-expect-error key is a buffer because keyEncoding is "buffer" - yield { key: new TextDecoder().decode(key), value } + options?.signal?.throwIfAborted() + + yield { + // @ts-expect-error key is buffer even though types say string + key: uint8ArrayToString(key), + value: (async function * () { + yield value + })() + } + options?.signal?.throwIfAborted() } } finally { await li.close() diff --git a/packages/blockstore-level/typedoc.json b/packages/blockstore-level/typedoc.json index f599dc72..db0b0747 100644 --- a/packages/blockstore-level/typedoc.json +++ b/packages/blockstore-level/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts" ] diff --git a/packages/blockstore-s3/CHANGELOG.md b/packages/blockstore-s3/CHANGELOG.md index 7e43b561..0f98f360 100644 --- a/packages/blockstore-s3/CHANGELOG.md +++ b/packages/blockstore-s3/CHANGELOG.md @@ -1,3 +1,85 @@ +## [blockstore-s3-v3.0.1](https://github.com/ipfs/js-stores/compare/blockstore-s3-3.0.0...blockstore-s3-3.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [blockstore-s3-v3.0.0](https://github.com/ipfs/js-stores/compare/blockstore-s3-2.0.5...blockstore-s3-3.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +### Trivial Changes + +* bump sinon from 20.0.0 to 21.0.0 ([#351](https://github.com/ipfs/js-stores/issues/351)) ([f24dd4f](https://github.com/ipfs/js-stores/commit/f24dd4f9b6eb681b1f9652409a558a8bcc50f4a4)) + +## [blockstore-s3-v2.0.5](https://github.com/ipfs/js-stores/compare/blockstore-s3-2.0.4...blockstore-s3-2.0.5) (2025-09-02) + +### Bug Fixes + +* deprecate blockstore-level and datastore-fs ([#353](https://github.com/ipfs/js-stores/issues/353)) ([ebc7912](https://github.com/ipfs/js-stores/commit/ebc7912696d5bd9dc991ece5f0c0d4acfb1f9400)) + +## [blockstore-s3-v2.0.4](https://github.com/ipfs/js-stores/compare/blockstore-s3-2.0.3...blockstore-s3-2.0.4) (2025-05-28) + +### Bug Fixes + +* improve abort signal support ([#350](https://github.com/ipfs/js-stores/issues/350)) ([e17d770](https://github.com/ipfs/js-stores/commit/e17d770cc2fcee77cb0152a855abf162e5a91a99)) + +## [blockstore-s3-v2.0.3](https://github.com/ipfs/js-stores/compare/blockstore-s3-2.0.2...blockstore-s3-2.0.3) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) +* **dev:** bump sinon from 19.0.5 to 20.0.0 ([#337](https://github.com/ipfs/js-stores/issues/337)) ([ec2a54a](https://github.com/ipfs/js-stores/commit/ec2a54a1ef2ecbee862d17710430a51b79063183)) + +## [blockstore-s3-v2.0.2](https://github.com/ipfs/js-stores/compare/blockstore-s3-2.0.1...blockstore-s3-2.0.2) (2024-09-13) + +### Dependencies + +* **dev:** bump sinon from 18.0.1 to 19.0.2 ([#322](https://github.com/ipfs/js-stores/issues/322)) ([8ea2df5](https://github.com/ipfs/js-stores/commit/8ea2df5c8f531b086b10130b8a3ce59a4d5bc2ba)) + +## [blockstore-s3-v2.0.1](https://github.com/ipfs/js-stores/compare/blockstore-s3-2.0.0...blockstore-s3-2.0.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## blockstore-s3 [2.0.0](https://github.com/ipfs/js-stores/compare/blockstore-s3-1.0.16...blockstore-s3-2.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + + + +### Dependencies + +* **blockstore-core:** upgraded to 5.0.0 +* **interface-blockstore:** upgraded to 5.3.0 +* **interface-store:** upgraded to 6.0.0 +* **interface-blockstore-tests:** upgraded to 7.0.0 + +## blockstore-s3 [1.0.16](https://github.com/ipfs/js-stores/compare/blockstore-s3-1.0.15...blockstore-s3-1.0.16) (2024-08-01) + + +### Dependencies + +* **dev:** bump sinon from 17.0.2 to 18.0.0 ([#308](https://github.com/ipfs/js-stores/issues/308)) ([0fbfe11](https://github.com/ipfs/js-stores/commit/0fbfe1112a102055d75f077ff799fbb1001e6aa7)) + ## blockstore-s3 [1.0.15](https://github.com/ipfs/js-stores/compare/blockstore-s3-v1.0.14...blockstore-s3-1.0.15) (2024-02-12) diff --git a/packages/blockstore-s3/CODE_OF_CONDUCT.md b/packages/blockstore-s3/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/blockstore-s3/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/blockstore-s3/LICENSE b/packages/blockstore-s3/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/blockstore-s3/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/blockstore-s3/LICENSE-APACHE b/packages/blockstore-s3/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/blockstore-s3/LICENSE-APACHE +++ b/packages/blockstore-s3/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/blockstore-s3/README.md b/packages/blockstore-s3/README.md index fa1f519e..9e36a0e3 100644 --- a/packages/blockstore-s3/README.md +++ b/packages/blockstore-s3/README.md @@ -9,6 +9,21 @@ # About + + A Blockstore implementation that stores blocks on Amazon S3. ## Example - Quickstart @@ -50,7 +65,7 @@ $ npm i blockstore-s3 ## Browser ` @@ -64,8 +79,8 @@ Loading this module through a script tag will make it's exports available as `Bl Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/blockstore-s3/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/blockstore-s3/LICENSE-MIT) / ) # Contribute diff --git a/packages/blockstore-s3/examples/helia/index.js b/packages/blockstore-s3/examples/helia/index.js index 6940f74a..4a989374 100644 --- a/packages/blockstore-s3/examples/helia/index.js +++ b/packages/blockstore-s3/examples/helia/index.js @@ -1,8 +1,10 @@ -import { createHelia } from 'helia' -import { unixfs } from '@helia/unixfs' -import toBuffer from 'it-to-buffer' +/* eslint-disable no-console */ + import { S3 } from '@aws-sdk/client-s3' +import { unixfs } from '@helia/unixfs' import { BlockstoreS3 } from 'blockstore-s3' +import { createHelia } from 'helia' +import toBuffer from 'it-to-buffer' async function main () { // Configure S3 as normal @@ -18,7 +20,7 @@ async function main () { // Create a new Helia node with our S3 backed Repo console.log('Start Helia') - const node = await createHelia({ + const helia = await createHelia({ blockstore }) @@ -45,7 +47,7 @@ async function main () { // After everything is done, shut the node down // We don't need to worry about catching errors here console.log('\n\nStopping the node') - await node.stop() + await helia.stop() } main() diff --git a/packages/blockstore-s3/package.json b/packages/blockstore-s3/package.json index 134f3244..57d34df5 100644 --- a/packages/blockstore-s3/package.json +++ b/packages/blockstore-s3/package.json @@ -1,6 +1,6 @@ { "name": "blockstore-s3", - "version": "1.0.15", + "version": "3.0.1", "description": "IPFS blockstore implementation backed by s3", "license": "Apache-2.0 OR MIT", "homepage": "https://github.com/ipfs/js-stores/tree/main/packages/blockstore-s3#readme", @@ -36,12 +36,98 @@ "import": "./dist/src/index.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true, - "sourceType": "module" - } + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "clean": "aegir clean", @@ -54,22 +140,25 @@ "test:firefox": "aegir test -t browser -- --browser firefox", "test:firefox-webworker": "aegir test -t webworker -- --browser firefox", "test:electron-main": "aegir test -t electron-main", - "dep-check": "aegir dep-check" + "dep-check": "aegir dep-check", + "release": "aegir release" }, "dependencies": { - "@aws-sdk/client-s3": "^3.511.0", - "blockstore-core": "^4.0.0", - "interface-blockstore": "^5.0.0", - "interface-store": "^5.0.0", - "it-to-buffer": "^4.0.5", - "multiformats": "^13.0.1", - "uint8arrays": "^5.0.2" + "@aws-sdk/client-s3": "^3.817.0", + "blockstore-core": "^6.0.0", + "interface-blockstore": "^6.0.0", + "interface-store": "^7.0.0", + "multiformats": "^13.3.6", + "uint8arrays": "^5.1.0" }, "devDependencies": { - "@types/sinon": "^17.0.3", - "aegir": "^42.2.3", - "interface-blockstore-tests": "^6.0.0", - "p-defer": "^4.0.0", - "sinon": "^17.0.1" + "@types/sinon": "^21.0.0", + "@types/xmldom": "^0.1.34", + "aegir": "^47.0.16", + "interface-blockstore-tests": "^8.0.0", + "it-drain": "^3.0.10", + "p-defer": "^4.0.1", + "sinon": "^21.0.0", + "xmldom": "^0.6.0" } } diff --git a/packages/blockstore-s3/src/index.ts b/packages/blockstore-s3/src/index.ts index c41b6397..02535bb3 100644 --- a/packages/blockstore-s3/src/index.ts +++ b/packages/blockstore-s3/src/index.ts @@ -40,13 +40,13 @@ import { ListObjectsV2Command } from '@aws-sdk/client-s3' import { BaseBlockstore } from 'blockstore-core/base' -import * as Errors from 'blockstore-core/errors' -import toBuffer from 'it-to-buffer' -import { fromString as unint8arrayFromString } from 'uint8arrays' -import { NextToLast, type ShardingStrategy } from './sharding.js' +import { DeleteFailedError, GetFailedError, HasFailedError, NotFoundError, OpenFailedError, PutFailedError } from 'interface-store' +import { fromString as uint8arrayFromString } from 'uint8arrays' +import { NextToLast } from './sharding.js' +import type { ShardingStrategy } from './sharding.js' import type { S3 } from '@aws-sdk/client-s3' import type { Pair } from 'interface-blockstore' -import type { AbortOptions } from 'interface-store' +import type { AbortOptions, AwaitGenerator } from 'interface-store' import type { CID } from 'multiformats/cid' export type { ShardingStrategy } @@ -94,6 +94,7 @@ export class S3Blockstore extends BaseBlockstore { */ async put (key: CID, val: Uint8Array, options?: AbortOptions): Promise { try { + options?.signal?.throwIfAborted() await this.s3.send( new PutObjectCommand({ Bucket: this.bucket, @@ -106,15 +107,16 @@ export class S3Blockstore extends BaseBlockstore { return key } catch (err: any) { - throw Errors.putFailedError(err) + throw new PutFailedError(String(err)) } } /** * Read from s3 */ - async get (key: CID, options?: AbortOptions): Promise { + async * get (key: CID, options?: AbortOptions): AwaitGenerator { try { + options?.signal?.throwIfAborted() const data = await this.s3.send( new GetObjectCommand({ Bucket: this.bucket, @@ -130,26 +132,32 @@ export class S3Blockstore extends BaseBlockstore { // If a body was returned, ensure it's a Uint8Array if (data.Body instanceof Uint8Array) { - return data.Body + yield data.Body + return } if (typeof data.Body === 'string') { - return unint8arrayFromString(data.Body) + yield uint8arrayFromString(data.Body) + return } if (data.Body instanceof Blob) { - const buf = await data.Body.arrayBuffer() + yield * data.Body.stream() + return + } - return new Uint8Array(buf, 0, buf.byteLength) + if (data.Body[Symbol.asyncIterator] != null) { + yield * data.Body + return } - // @ts-expect-error s3 types define their own Blob as an empty interface - return await toBuffer(data.Body) + throw new Error(`Unknown body type ${data.Body} (typeof ${typeof data.Body})`) } catch (err: any) { if (err.statusCode === 404) { - throw Errors.notFoundError(err) + throw new NotFoundError(String(err)) } - throw err + + throw new GetFailedError(String(err)) } } @@ -158,6 +166,7 @@ export class S3Blockstore extends BaseBlockstore { */ async has (key: CID, options?: AbortOptions): Promise { try { + options?.signal?.throwIfAborted() await this.s3.send( new HeadObjectCommand({ Bucket: this.bucket, @@ -179,7 +188,7 @@ export class S3Blockstore extends BaseBlockstore { return false } - throw err + throw new HasFailedError(String(err)) } } @@ -188,6 +197,7 @@ export class S3Blockstore extends BaseBlockstore { */ async delete (key: CID, options?: AbortOptions): Promise { try { + options?.signal?.throwIfAborted() await this.s3.send( new DeleteObjectCommand({ Bucket: this.bucket, @@ -197,15 +207,17 @@ export class S3Blockstore extends BaseBlockstore { } ) } catch (err: any) { - throw Errors.deleteFailedError(err) + throw new DeleteFailedError(String(err)) } } - async * getAll (options?: AbortOptions): AsyncIterable { + async * getAll (options?: AbortOptions): AwaitGenerator { const params: Record = {} try { while (true) { + options?.signal?.throwIfAborted() + const data = await this.s3.send( new ListObjectsV2Command({ Bucket: this.bucket, @@ -219,7 +231,7 @@ export class S3Blockstore extends BaseBlockstore { return } - if (data == null || data.Contents == null) { + if (data?.Contents == null) { throw new Error('Not found') } @@ -233,7 +245,7 @@ export class S3Blockstore extends BaseBlockstore { yield { cid, - block: await this.get(cid, options) + bytes: this.get(cid, options) } } @@ -249,7 +261,7 @@ export class S3Blockstore extends BaseBlockstore { break } } catch (err: any) { - throw new Error(err.code) + throw new GetFailedError(String(err)) } } @@ -279,7 +291,7 @@ export class S3Blockstore extends BaseBlockstore { return } - throw Errors.openFailedError(err) + throw new OpenFailedError(String(err)) } } } diff --git a/packages/blockstore-s3/test/index.spec.ts b/packages/blockstore-s3/test/index.spec.ts index 7e849e17..702196da 100644 --- a/packages/blockstore-s3/test/index.spec.ts +++ b/packages/blockstore-s3/test/index.spec.ts @@ -1,13 +1,16 @@ /* eslint-env mocha */ -import { type CreateBucketCommand, type HeadObjectCommand, S3 } from '@aws-sdk/client-s3' +import './utils/domparser-polyfill.js' +import { S3 } from '@aws-sdk/client-s3' import { expect } from 'aegir/chai' import { interfaceBlockstoreTests } from 'interface-blockstore-tests' +import drain from 'it-drain' import { CID } from 'multiformats/cid' import defer from 'p-defer' import sinon from 'sinon' import { S3Blockstore } from '../src/index.js' import { s3Resolve, s3Reject, S3Error, s3Mock } from './utils/s3-mock.js' +import type { CreateBucketCommand, HeadObjectCommand } from '@aws-sdk/client-s3' const cid = CID.parse('QmeimKZyjcBnuXmAD9zMnSjM9JodTbgGT3gutofkTqz9rE') @@ -55,7 +58,7 @@ describe('S3Blockstore', () => { }) await expect(store.put(cid, new TextEncoder().encode('test data'))).to.eventually.rejected - .with.property('code', 'ERR_PUT_FAILED') + .with.property('name', 'PutFailedError') }) }) @@ -72,8 +75,8 @@ describe('S3Blockstore', () => { return s3Reject(new S3Error('UnknownCommand')) }) - await expect(store.get(cid)).to.eventually.rejected - .with.property('code', 'ERR_NOT_FOUND') + await expect(drain(store.get(cid))).to.eventually.be.rejected + .with.property('name', 'NotFoundError') }) }) @@ -91,7 +94,7 @@ describe('S3Blockstore', () => { }) await expect(store.delete(cid)).to.eventually.rejected - .with.property('code', 'ERR_DELETE_FAILED') + .with.property('name', 'DeleteFailedError') }) }) @@ -146,7 +149,7 @@ describe('S3Blockstore', () => { }) await expect(store.open()).to.eventually.rejected - .with.property('code', 'ERR_OPEN_FAILED') + .with.property('name', 'OpenFailedError') const headObjectCommand = await bucketTested.promise expect(headObjectCommand).to.have.nested.property('input.Bucket', 'test') @@ -165,7 +168,7 @@ describe('S3Blockstore', () => { }) await expect(store.open()).to.eventually.rejected - .with.property('code', 'ERR_OPEN_FAILED') + .with.property('name', 'OpenFailedError') }) }) diff --git a/packages/blockstore-s3/test/utils/domparser-polyfill.ts b/packages/blockstore-s3/test/utils/domparser-polyfill.ts new file mode 100644 index 00000000..5225bed6 --- /dev/null +++ b/packages/blockstore-s3/test/utils/domparser-polyfill.ts @@ -0,0 +1,3 @@ +import { DOMParser } from 'xmldom' + +globalThis.DOMParser = globalThis.DOMParser ?? DOMParser diff --git a/packages/blockstore-s3/test/utils/s3-mock.ts b/packages/blockstore-s3/test/utils/s3-mock.ts index 1b4aadd5..06118f5c 100644 --- a/packages/blockstore-s3/test/utils/s3-mock.ts +++ b/packages/blockstore-s3/test/utils/s3-mock.ts @@ -17,11 +17,11 @@ export class S3Error extends Error { } } -export const s3Resolve = (res?: any): any => { +export function s3Resolve (res?: any): any { return Promise.resolve(res) } -export const s3Reject = (err: T): any => { +export function s3Reject (err: Error): any { return Promise.reject(err) } diff --git a/packages/blockstore-s3/typedoc.json b/packages/blockstore-s3/typedoc.json index f599dc72..db0b0747 100644 --- a/packages/blockstore-s3/typedoc.json +++ b/packages/blockstore-s3/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts" ] diff --git a/packages/datastore-core/CHANGELOG.md b/packages/datastore-core/CHANGELOG.md index a214bfbc..b253b41d 100644 --- a/packages/datastore-core/CHANGELOG.md +++ b/packages/datastore-core/CHANGELOG.md @@ -1,3 +1,73 @@ +## [datastore-core-v11.0.2](https://github.com/ipfs/js-stores/compare/datastore-core-11.0.1...datastore-core-11.0.2) (2025-10-03) + +### Dependencies + +* bump level from 8.0.1 to 10.0.0 ([#356](https://github.com/ipfs/js-stores/issues/356)) ([c0ec61f](https://github.com/ipfs/js-stores/commit/c0ec61fe965e3bad9d607a0bd3a3c750f00f41d0)) + +## [datastore-core-v11.0.1](https://github.com/ipfs/js-stores/compare/datastore-core-11.0.0...datastore-core-11.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [datastore-core-v11.0.0](https://github.com/ipfs/js-stores/compare/datastore-core-10.0.4...datastore-core-11.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +### Dependencies + +* bump @libp2p/logger ([#359](https://github.com/ipfs/js-stores/issues/359)) ([edb5a1f](https://github.com/ipfs/js-stores/commit/edb5a1f8b575a27ad28bc2e1c4e4d52e1f114ebc)) + +## [datastore-core-v10.0.4](https://github.com/ipfs/js-stores/compare/datastore-core-10.0.3...datastore-core-10.0.4) (2025-05-28) + +### Bug Fixes + +* improve abort signal support ([#350](https://github.com/ipfs/js-stores/issues/350)) ([e17d770](https://github.com/ipfs/js-stores/commit/e17d770cc2fcee77cb0152a855abf162e5a91a99)) + +## [datastore-core-v10.0.3](https://github.com/ipfs/js-stores/compare/datastore-core-10.0.2...datastore-core-10.0.3) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [datastore-core-v10.0.2](https://github.com/ipfs/js-stores/compare/datastore-core-10.0.1...datastore-core-10.0.2) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +## datastore-core [10.0.0](https://github.com/ipfs/js-stores/compare/datastore-core-9.2.9...datastore-core-10.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + + + +### Dependencies + +* **interface-datastore:** upgraded to 8.3.0 +* **interface-store:** upgraded to 6.0.0 +* **interface-datastore-tests:** upgraded to 6.0.0 + +## datastore-core [9.2.9](https://github.com/ipfs/js-stores/compare/datastore-core-9.2.8...datastore-core-9.2.9) (2024-02-28) + + +### Bug Fixes + +* stop namespaced datastore throwing when queried ([#296](https://github.com/ipfs/js-stores/issues/296)) ([9163490](https://github.com/ipfs/js-stores/commit/916349054a3f83a7c9c4bb692edebcce409c7fee)), closes [#236](https://github.com/ipfs/js-stores/issues/236) [#236](https://github.com/ipfs/js-stores/issues/236) + ## datastore-core [9.2.8](https://github.com/ipfs/js-stores/compare/datastore-core-v9.2.7...datastore-core-9.2.8) (2024-02-12) diff --git a/packages/datastore-core/CODE_OF_CONDUCT.md b/packages/datastore-core/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/datastore-core/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/datastore-core/LICENSE b/packages/datastore-core/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/datastore-core/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/datastore-core/LICENSE-APACHE b/packages/datastore-core/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/datastore-core/LICENSE-APACHE +++ b/packages/datastore-core/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/datastore-core/README.md b/packages/datastore-core/README.md index 66a1aa0f..be5f07af 100644 --- a/packages/datastore-core/README.md +++ b/packages/datastore-core/README.md @@ -9,16 +9,31 @@ # About + + Various Datastore implementations are available. ## Implementations -- Mount: [`src/mount`](src/mount.ts) -- Keytransform: [`src/keytransform`](src/keytransform.ts) -- Sharding: [`src/sharding`](src/sharding.ts) -- Tiered: [`src/tiered`](src/tirered.ts) -- Namespace: [`src/namespace`](src/namespace.ts) -- BlackHole: [`src/black-hole`](src/black-hole.ts) +- Mount: [`src/mount`](./src/mount.ts) +- Keytransform: [`src/keytransform`](./src/keytransform.ts) +- Sharding: [`src/sharding`](./src/sharding.ts) +- Tiered: [`src/tiered`](./src/tirered.ts) +- Namespace: [`src/namespace`](./src/namespace.ts) +- BlackHole: [`src/black-hole`](./src/black-hole.ts) ## Example - BaseDatastore @@ -76,7 +91,7 @@ $ npm i datastore-core ## Browser ` @@ -90,8 +105,8 @@ Loading this module through a script tag will make it's exports available as `Da Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/datastore-core/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/datastore-core/LICENSE-MIT) / ) # Contribute diff --git a/packages/datastore-core/package.json b/packages/datastore-core/package.json index a27fc72f..6a552a3f 100644 --- a/packages/datastore-core/package.json +++ b/packages/datastore-core/package.json @@ -1,6 +1,6 @@ { "name": "datastore-core", - "version": "9.2.8", + "version": "11.0.2", "description": "Wrapper implementation for interface-datastore", "author": "Friedel Ziegelmayer ", "license": "Apache-2.0 OR MIT", @@ -92,12 +92,98 @@ "import": "./dist/src/tiered.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true, - "sourceType": "module" - } + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "clean": "aegir clean", @@ -110,26 +196,25 @@ "test:firefox": "aegir test -t browser -- --browser firefox", "test:firefox-webworker": "aegir test -t webworker -- --browser firefox", "test:electron-main": "aegir test -t electron-main", - "dep-check": "aegir dep-check" + "dep-check": "aegir dep-check", + "release": "aegir release" }, "dependencies": { - "@libp2p/logger": "^4.0.6", - "err-code": "^3.0.1", - "interface-datastore": "^8.0.0", - "interface-store": "^5.0.0", - "it-drain": "^3.0.5", - "it-filter": "^3.0.4", - "it-map": "^3.0.5", - "it-merge": "^3.0.3", + "@libp2p/logger": "^6.0.0", + "interface-datastore": "^9.0.0", + "interface-store": "^7.0.0", + "it-drain": "^3.0.9", + "it-filter": "^3.1.3", + "it-map": "^3.1.3", + "it-merge": "^3.0.11", "it-pipe": "^3.0.1", - "it-pushable": "^3.2.3", - "it-sort": "^3.0.4", - "it-take": "^3.0.4" + "it-sort": "^3.0.8", + "it-take": "^3.0.8" }, "devDependencies": { - "aegir": "^42.2.3", - "interface-datastore-tests": "^5.0.0", - "it-all": "^3.0.4", - "uint8arrays": "^5.0.2" + "aegir": "^47.0.16", + "interface-datastore-tests": "^6.0.0", + "it-all": "^3.0.8", + "uint8arrays": "^5.1.0" } } diff --git a/packages/datastore-core/src/base.ts b/packages/datastore-core/src/base.ts index 9108be3f..9a97af7f 100644 --- a/packages/datastore-core/src/base.ts +++ b/packages/datastore-core/src/base.ts @@ -3,7 +3,7 @@ import filter from 'it-filter' import sort from 'it-sort' import take from 'it-take' import type { Batch, Datastore, Key, KeyQuery, Pair, Query } from 'interface-datastore' -import type { AbortOptions, Await, AwaitIterable } from 'interface-store' +import type { AbortOptions, Await, AwaitGenerator, AwaitIterable } from 'interface-store' export class BaseDatastore implements Datastore { put (key: Key, val: Uint8Array, options?: AbortOptions): Await { @@ -22,14 +22,14 @@ export class BaseDatastore implements Datastore { return Promise.reject(new Error('.delete is not implemented')) } - async * putMany (source: AwaitIterable, options: AbortOptions = {}): AwaitIterable { + async * putMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { for await (const { key, value } of source) { await this.put(key, value, options) yield key } } - async * getMany (source: AwaitIterable, options: AbortOptions = {}): AwaitIterable { + async * getMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { for await (const key of source) { yield { key, @@ -38,7 +38,7 @@ export class BaseDatastore implements Datastore { } } - async * deleteMany (source: AwaitIterable, options: AbortOptions = {}): AwaitIterable { + async * deleteMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { for await (const key of source) { await this.delete(key, options) yield key @@ -70,7 +70,7 @@ export class BaseDatastore implements Datastore { * Extending classes should override `query` or implement this method */ // eslint-disable-next-line require-yield - async * _all (q: Query, options?: AbortOptions): AwaitIterable { + async * _all (q: Query, options?: AbortOptions): AwaitGenerator { throw new Error('._all is not implemented') } @@ -78,11 +78,11 @@ export class BaseDatastore implements Datastore { * Extending classes should override `queryKeys` or implement this method */ // eslint-disable-next-line require-yield - async * _allKeys (q: KeyQuery, options?: AbortOptions): AwaitIterable { + async * _allKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { throw new Error('._allKeys is not implemented') } - query (q: Query, options?: AbortOptions): AwaitIterable { + query (q: Query, options?: AbortOptions): AwaitGenerator { let it = this._all(q, options) if (q.prefix != null) { @@ -111,7 +111,7 @@ export class BaseDatastore implements Datastore { return it } - queryKeys (q: KeyQuery, options?: AbortOptions): AwaitIterable { + queryKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { let it = this._allKeys(q, options) if (q.prefix != null) { diff --git a/packages/datastore-core/src/black-hole.ts b/packages/datastore-core/src/black-hole.ts index 9c7b3d3e..00b63c3a 100644 --- a/packages/datastore-core/src/black-hole.ts +++ b/packages/datastore-core/src/black-hole.ts @@ -1,31 +1,36 @@ +import { NotFoundError } from 'interface-store' import { BaseDatastore } from './base.js' -import * as Errors from './errors.js' -import type { Pair } from 'interface-datastore' +import type { Pair, Query, KeyQuery } from 'interface-datastore' import type { Key } from 'interface-datastore/key' -import type { Await, AwaitIterable } from 'interface-store' +import type { AbortOptions, Await, AwaitGenerator } from 'interface-store' export class BlackHoleDatastore extends BaseDatastore { - put (key: Key): Await { + put (key: Key, value: Uint8Array, options?: AbortOptions): Await { + options?.signal?.throwIfAborted() return key } - get (): Await { - throw Errors.notFoundError() + get (key: Key, options?: AbortOptions): Await { + options?.signal?.throwIfAborted() + throw new NotFoundError() } - has (key: Key): Await { + has (key: Key, options?: AbortOptions): Await { + options?.signal?.throwIfAborted() return false } - delete (key: Key): Await { - + delete (key: Key, options?: AbortOptions): Await { + options?.signal?.throwIfAborted() } - * _all (): AwaitIterable { - + // eslint-disable-next-line require-yield + * _all (q: Query, options?: AbortOptions): AwaitGenerator { + options?.signal?.throwIfAborted() } - * _allKeys (): AwaitIterable { - + // eslint-disable-next-line require-yield + * _allKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { + options?.signal?.throwIfAborted() } } diff --git a/packages/datastore-core/src/errors.ts b/packages/datastore-core/src/errors.ts deleted file mode 100644 index 8009aff7..00000000 --- a/packages/datastore-core/src/errors.ts +++ /dev/null @@ -1,31 +0,0 @@ -import errCode from 'err-code' - -export function dbOpenFailedError (err?: Error): Error { - err = err ?? new Error('Cannot open database') - return errCode(err, 'ERR_DB_OPEN_FAILED') -} - -export function dbDeleteFailedError (err?: Error): Error { - err = err ?? new Error('Delete failed') - return errCode(err, 'ERR_DB_DELETE_FAILED') -} - -export function dbWriteFailedError (err?: Error): Error { - err = err ?? new Error('Write failed') - return errCode(err, 'ERR_DB_WRITE_FAILED') -} - -export function dbReadFailedError (err?: Error): Error { - err = err ?? new Error('Read failed') - return errCode(err, 'ERR_DB_READ_FAILED') -} - -export function notFoundError (err?: Error): Error { - err = err ?? new Error('Not Found') - return errCode(err, 'ERR_NOT_FOUND') -} - -export function abortedError (err?: Error): Error { - err = err ?? new Error('Aborted') - return errCode(err, 'ERR_ABORTED') -} diff --git a/packages/datastore-core/src/index.ts b/packages/datastore-core/src/index.ts index 5ec96a75..b050078f 100644 --- a/packages/datastore-core/src/index.ts +++ b/packages/datastore-core/src/index.ts @@ -5,12 +5,12 @@ * * ## Implementations * - * - Mount: [`src/mount`](src/mount.ts) - * - Keytransform: [`src/keytransform`](src/keytransform.ts) - * - Sharding: [`src/sharding`](src/sharding.ts) - * - Tiered: [`src/tiered`](src/tirered.ts) - * - Namespace: [`src/namespace`](src/namespace.ts) - * - BlackHole: [`src/black-hole`](src/black-hole.ts) + * - Mount: [`src/mount`](./src/mount.ts) + * - Keytransform: [`src/keytransform`](./src/keytransform.ts) + * - Sharding: [`src/sharding`](./src/sharding.ts) + * - Tiered: [`src/tiered`](./src/tirered.ts) + * - Namespace: [`src/namespace`](./src/namespace.ts) + * - BlackHole: [`src/black-hole`](./src/black-hole.ts) * * @example BaseDatastore * @@ -61,7 +61,6 @@ * ``` */ -import * as Errors from './errors.js' import * as shard from './shard.js' import type { Key } from 'interface-datastore' @@ -73,7 +72,6 @@ export { MountDatastore } from './mount.js' export { TieredDatastore } from './tiered.js' export { NamespaceDatastore } from './namespace.js' -export { Errors } export { shard } export interface Shard { diff --git a/packages/datastore-core/src/keytransform.ts b/packages/datastore-core/src/keytransform.ts index 6b33e134..faa029dd 100644 --- a/packages/datastore-core/src/keytransform.ts +++ b/packages/datastore-core/src/keytransform.ts @@ -3,7 +3,7 @@ import { pipe } from 'it-pipe' import { BaseDatastore } from './base.js' import type { KeyTransform } from './index.js' import type { Batch, Datastore, Key, KeyQuery, Pair, Query } from 'interface-datastore' -import type { AbortOptions, AwaitIterable } from 'interface-store' +import type { AbortOptions, AwaitGenerator, AwaitIterable } from 'interface-store' /** * A datastore shim, that wraps around a given datastore, changing @@ -39,7 +39,7 @@ export class KeyTransformDatastore extends BaseDatastore { await this.child.delete(this.transform.convert(key), options) } - async * putMany (source: AwaitIterable, options: AbortOptions = {}): AsyncIterable { + async * putMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { const transform = this.transform const child = this.child @@ -60,7 +60,7 @@ export class KeyTransformDatastore extends BaseDatastore { ) } - async * getMany (source: AwaitIterable, options: AbortOptions = {}): AsyncIterable { + async * getMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { const transform = this.transform const child = this.child @@ -81,7 +81,7 @@ export class KeyTransformDatastore extends BaseDatastore { ) } - async * deleteMany (source: AwaitIterable, options: AbortOptions = {}): AsyncIterable { + async * deleteMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { const transform = this.transform const child = this.child @@ -114,7 +114,7 @@ export class KeyTransformDatastore extends BaseDatastore { } } - query (q: Query, options?: AbortOptions): AsyncIterable { + query (q: Query, options?: AbortOptions): AwaitGenerator { const query: Query = { ...q } @@ -148,7 +148,7 @@ export class KeyTransformDatastore extends BaseDatastore { }) } - queryKeys (q: KeyQuery, options?: AbortOptions): AsyncIterable { + queryKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { const query = { ...q } diff --git a/packages/datastore-core/src/memory.ts b/packages/datastore-core/src/memory.ts index df87fd4b..5d7fe19d 100644 --- a/packages/datastore-core/src/memory.ts +++ b/packages/datastore-core/src/memory.ts @@ -1,8 +1,8 @@ import { Key } from 'interface-datastore/key' +import { NotFoundError } from 'interface-store' import { BaseDatastore } from './base.js' -import * as Errors from './errors.js' -import type { Pair } from 'interface-datastore' -import type { Await, AwaitIterable } from 'interface-store' +import type { KeyQuery, Pair, Query } from 'interface-datastore' +import type { AbortOptions, Await, AwaitGenerator } from 'interface-store' export class MemoryDatastore extends BaseDatastore { private readonly data: Map @@ -13,39 +13,49 @@ export class MemoryDatastore extends BaseDatastore { this.data = new Map() } - put (key: Key, val: Uint8Array): Await { // eslint-disable-line require-await + put (key: Key, val: Uint8Array, options?: AbortOptions): Await { + options?.signal?.throwIfAborted() + this.data.set(key.toString(), val) return key } - get (key: Key): Await { + get (key: Key, options?: AbortOptions): Await { + options?.signal?.throwIfAborted() + const result = this.data.get(key.toString()) if (result == null) { - throw Errors.notFoundError() + throw new NotFoundError() } return result } - has (key: Key): Await { // eslint-disable-line require-await + has (key: Key, options?: AbortOptions): Await { + options?.signal?.throwIfAborted() return this.data.has(key.toString()) } - delete (key: Key): Await { // eslint-disable-line require-await + delete (key: Key, options?: AbortOptions): Await { + options?.signal?.throwIfAborted() this.data.delete(key.toString()) } - * _all (): AwaitIterable { + * _all (q: Query, options?: AbortOptions): AwaitGenerator { + options?.signal?.throwIfAborted() for (const [key, value] of this.data.entries()) { yield { key: new Key(key), value } + options?.signal?.throwIfAborted() } } - * _allKeys (): AwaitIterable { + * _allKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { + options?.signal?.throwIfAborted() for (const key of this.data.keys()) { yield new Key(key) + options?.signal?.throwIfAborted() } } } diff --git a/packages/datastore-core/src/mount.ts b/packages/datastore-core/src/mount.ts index 15b74bfb..bb0fef18 100644 --- a/packages/datastore-core/src/mount.ts +++ b/packages/datastore-core/src/mount.ts @@ -1,11 +1,11 @@ +import { DeleteFailedError, NotFoundError, PutFailedError } from 'interface-store' import filter from 'it-filter' import merge from 'it-merge' import sort from 'it-sort' import take from 'it-take' import { BaseDatastore } from './base.js' -import * as Errors from './errors.js' import type { Batch, Datastore, Key, KeyQuery, Pair, Query } from 'interface-datastore' -import type { AbortOptions } from 'interface-store' +import type { AbortOptions, AwaitGenerator } from 'interface-store' /** * A datastore that can combine multiple stores inside various @@ -37,7 +37,7 @@ export class MountDatastore extends BaseDatastore { async put (key: Key, value: Uint8Array, options?: AbortOptions): Promise { const match = this._lookup(key) if (match == null) { - throw Errors.dbWriteFailedError(new Error('No datastore mounted for this key')) + throw new PutFailedError('No datastore mounted for this key') } await match.datastore.put(key, value, options) @@ -52,7 +52,7 @@ export class MountDatastore extends BaseDatastore { async get (key: Key, options: AbortOptions = {}): Promise { const match = this._lookup(key) if (match == null) { - throw Errors.notFoundError(new Error('No datastore mounted for this key')) + throw new NotFoundError('No datastore mounted for this key') } return match.datastore.get(key, options) } @@ -68,7 +68,7 @@ export class MountDatastore extends BaseDatastore { async delete (key: Key, options?: AbortOptions): Promise { const match = this._lookup(key) if (match == null) { - throw Errors.dbDeleteFailedError(new Error('No datastore mounted for this key')) + throw new DeleteFailedError('No datastore mounted for this key') } await match.datastore.delete(key, options) @@ -108,7 +108,7 @@ export class MountDatastore extends BaseDatastore { } } - query (q: Query, options?: AbortOptions): AsyncIterable { + query (q: Query, options?: AbortOptions): AwaitGenerator { const qs = this.mounts.map(m => { return m.datastore.query({ prefix: q.prefix, @@ -117,19 +117,19 @@ export class MountDatastore extends BaseDatastore { }) let it = merge(...qs) - if (q.filters != null) q.filters.forEach(f => { it = filter(it, f) }) - if (q.orders != null) q.orders.forEach(o => { it = sort(it, o) }) + if (q.filters != null) { q.filters.forEach(f => { it = filter(it, f) }) } + if (q.orders != null) { q.orders.forEach(o => { it = sort(it, o) }) } if (q.offset != null) { let i = 0 const offset = q.offset it = filter(it, () => i++ >= offset) } - if (q.limit != null) it = take(it, q.limit) + if (q.limit != null) { it = take(it, q.limit) } return it } - queryKeys (q: KeyQuery, options?: AbortOptions): AsyncIterable { + queryKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { const qs = this.mounts.map(m => { return m.datastore.queryKeys({ prefix: q.prefix, @@ -137,16 +137,15 @@ export class MountDatastore extends BaseDatastore { }, options) }) - /** @type AsyncIterable */ let it = merge(...qs) - if (q.filters != null) q.filters.forEach(f => { it = filter(it, f) }) - if (q.orders != null) q.orders.forEach(o => { it = sort(it, o) }) + if (q.filters != null) { q.filters.forEach(f => { it = filter(it, f) }) } + if (q.orders != null) { q.orders.forEach(o => { it = sort(it, o) }) } if (q.offset != null) { let i = 0 const offset = q.offset it = filter(it, () => i++ >= offset) } - if (q.limit != null) it = take(it, q.limit) + if (q.limit != null) { it = take(it, q.limit) } return it } diff --git a/packages/datastore-core/src/namespace.ts b/packages/datastore-core/src/namespace.ts index 02c20601..b476149e 100644 --- a/packages/datastore-core/src/namespace.ts +++ b/packages/datastore-core/src/namespace.ts @@ -1,6 +1,8 @@ import { Key } from 'interface-datastore' +import map from 'it-map' import { KeyTransformDatastore } from './keytransform.js' -import type { Datastore } from 'interface-datastore' +import type { Datastore, Query, Pair, KeyQuery } from 'interface-datastore' +import type { AbortOptions, AwaitGenerator } from 'interface-store' /** * Wraps a given datastore into a keytransform which @@ -11,6 +13,9 @@ import type { Datastore } from 'interface-datastore' * `/hello/world`. */ export class NamespaceDatastore extends KeyTransformDatastore { + private readonly iChild: Datastore + private readonly iKey: Key + constructor (child: Datastore, prefix: Key) { super(child, { convert (key) { @@ -28,5 +33,77 @@ export class NamespaceDatastore extends KeyTransformDatastore { return new Key(key.toString().slice(prefix.toString().length), false) } }) + + this.iChild = child + this.iKey = prefix + } + + query (q: Query, options?: AbortOptions): AwaitGenerator { + const query: Query = { + ...q + } + + query.filters = (query.filters ?? []).map(filter => { + return ({ key, value }) => filter({ key: this.transform.invert(key), value }) + }) + + const { prefix } = q + if (prefix != null && prefix !== '/') { + delete query.prefix + query.filters.push(({ key }) => { + return this.transform.invert(key).toString().startsWith(prefix) + }) + } + + if (query.orders != null) { + query.orders = query.orders.map(order => { + return (a, b) => order( + { key: this.transform.invert(a.key), value: a.value }, + { key: this.transform.invert(b.key), value: b.value } + ) + }) + } + + query.filters.unshift(({ key }) => this.iKey.isAncestorOf(key)) + + return map(this.iChild.query(query, options), ({ key, value }) => { + return { + key: this.transform.invert(key), + value + } + }) + } + + queryKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { + const query = { + ...q + } + + query.filters = (query.filters ?? []).map(filter => { + return (key) => filter(this.transform.invert(key)) + }) + + const { prefix } = q + if (prefix != null && prefix !== '/') { + delete query.prefix + query.filters.push((key) => { + return this.transform.invert(key).toString().startsWith(prefix) + }) + } + + if (query.orders != null) { + query.orders = query.orders.map(order => { + return (a, b) => order( + this.transform.invert(a), + this.transform.invert(b) + ) + }) + } + + query.filters.unshift(key => this.iKey.isAncestorOf(key)) + + return map(this.iChild.queryKeys(query, options), key => { + return this.transform.invert(key) + }) } } diff --git a/packages/datastore-core/src/sharding.ts b/packages/datastore-core/src/sharding.ts index 6244bb2b..3f34c08c 100644 --- a/packages/datastore-core/src/sharding.ts +++ b/packages/datastore-core/src/sharding.ts @@ -1,13 +1,14 @@ -import { type Batch, Key, type KeyQuery, type KeyQueryFilter, type Pair, type Query, type QueryFilter, type Datastore } from 'interface-datastore' +import { Key } from 'interface-datastore' +import { OpenFailedError } from 'interface-store' import { BaseDatastore } from './base.js' -import * as Errors from './errors.js' import { KeyTransformDatastore } from './keytransform.js' import { readShardFun, SHARDING_FN } from './shard.js' import type { Shard } from './index.js' -import type { AbortOptions, AwaitIterable } from 'interface-store' +import type { Batch, KeyQuery, KeyQueryFilter, Pair, Query, QueryFilter, Datastore } from 'interface-datastore' +import type { AbortOptions, AwaitGenerator, AwaitIterable } from 'interface-store' const shardKey = new Key(SHARDING_FN) @@ -58,7 +59,7 @@ export class ShardingDatastore extends BaseDatastore { if (!hasShard) { if (shard == null) { - throw Errors.dbOpenFailedError(Error('Shard is required when datastore doesn\'t have a shard key already.')) + throw new OpenFailedError('Shard is required when datastore doesn\'t have a shard key already') } await store.put(shardKey, new TextEncoder().encode(shard.toString() + '\n')) @@ -98,15 +99,15 @@ export class ShardingDatastore extends BaseDatastore { await this.child.delete(key, options) } - async * putMany (source: AwaitIterable, options: AbortOptions = {}): AsyncIterable { + async * putMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { yield * this.child.putMany(source, options) } - async * getMany (source: AwaitIterable, options: AbortOptions = {}): AsyncIterable { + async * getMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { yield * this.child.getMany(source, options) } - async * deleteMany (source: AwaitIterable, options: AbortOptions = {}): AsyncIterable { + async * deleteMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { yield * this.child.deleteMany(source, options) } @@ -114,7 +115,7 @@ export class ShardingDatastore extends BaseDatastore { return this.child.batch() } - query (q: Query, options?: AbortOptions): AsyncIterable { + query (q: Query, options?: AbortOptions): AwaitGenerator { const omitShard: QueryFilter = ({ key }) => key.toString() !== shardKey.toString() const tq: Query = { @@ -127,7 +128,7 @@ export class ShardingDatastore extends BaseDatastore { return this.child.query(tq, options) } - queryKeys (q: KeyQuery, options?: AbortOptions): AsyncIterable { + queryKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { const omitShard: KeyQueryFilter = (key) => key.toString() !== shardKey.toString() const tq: KeyQuery = { diff --git a/packages/datastore-core/src/tiered.ts b/packages/datastore-core/src/tiered.ts index e76db6f1..c418bce5 100644 --- a/packages/datastore-core/src/tiered.ts +++ b/packages/datastore-core/src/tiered.ts @@ -1,10 +1,8 @@ import { logger } from '@libp2p/logger' -import drain from 'it-drain' -import { pushable } from 'it-pushable' +import { NotFoundError } from 'interface-store' import { BaseDatastore } from './base.js' -import * as Errors from './errors.js' import type { Batch, Datastore, Key, KeyQuery, Pair, Query } from 'interface-datastore' -import type { AbortOptions, AwaitIterable } from 'interface-store' +import type { AbortOptions, AwaitGenerator, AwaitIterable } from 'interface-store' const log = logger('datastore:core:tiered') @@ -25,24 +23,32 @@ export class TieredDatastore extends BaseDatastore { } async put (key: Key, value: Uint8Array, options?: AbortOptions): Promise { - try { - await Promise.all(this.stores.map(async store => { await store.put(key, value, options) })) - return key - } catch (err: any) { - throw Errors.dbWriteFailedError(err) - } + await Promise.all( + this.stores.map(async store => { + await store.put(key, value, options) + }) + ) + + return key } async get (key: Key, options?: AbortOptions): Promise { + let error: Error | undefined + for (const store of this.stores) { try { const res = await store.get(key, options) - if (res != null) return res - } catch (err) { + + if (res != null) { + return res + } + } catch (err: any) { + error = err log.error(err) } } - throw Errors.notFoundError() + + throw error ?? new NotFoundError() } async has (key: Key, options?: AbortOptions): Promise { @@ -56,72 +62,24 @@ export class TieredDatastore extends BaseDatastore { } async delete (key: Key, options?: AbortOptions): Promise { - try { - await Promise.all(this.stores.map(async store => { await store.delete(key, options) })) - } catch (err: any) { - throw Errors.dbDeleteFailedError(err) - } - } - - async * putMany (source: AwaitIterable, options: AbortOptions = {}): AsyncIterable { - let error: Error | undefined - const pushables = this.stores.map(store => { - const source = pushable({ - objectMode: true + await Promise.all( + this.stores.map(async store => { + await store.delete(key, options) }) + ) + } - drain(store.putMany(source, options)) - .catch(err => { - // store threw while putting, make sure we bubble the error up - error = err - }) - - return source - }) - - try { - for await (const pair of source) { - if (error != null) { - throw error - } - - pushables.forEach(p => p.push(pair)) - - yield pair.key - } - } finally { - pushables.forEach(p => p.end()) + async * putMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { + for await (const pair of source) { + await this.put(pair.key, pair.value, options) + yield pair.key } } - async * deleteMany (source: AwaitIterable, options: AbortOptions = {}): AsyncIterable { - let error: Error | undefined - const pushables = this.stores.map(store => { - const source = pushable({ - objectMode: true - }) - - drain(store.deleteMany(source, options)) - .catch(err => { - // store threw while deleting, make sure we bubble the error up - error = err - }) - - return source - }) - - try { - for await (const key of source) { - if (error != null) { - throw error - } - - pushables.forEach(p => p.push(key)) - - yield key - } - } finally { - pushables.forEach(p => p.end()) + async * deleteMany (source: AwaitIterable, options: AbortOptions = {}): AwaitGenerator { + for await (const key of source) { + await this.delete(key, options) + yield key } } @@ -143,11 +101,11 @@ export class TieredDatastore extends BaseDatastore { } } - query (q: Query, options?: AbortOptions): AwaitIterable { + query (q: Query, options?: AbortOptions): AwaitGenerator { return this.stores[this.stores.length - 1].query(q, options) } - queryKeys (q: KeyQuery, options?: AbortOptions): AwaitIterable { + queryKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { return this.stores[this.stores.length - 1].queryKeys(q, options) } } diff --git a/packages/datastore-core/test/namespace.spec.ts b/packages/datastore-core/test/namespace.spec.ts index 54f57f98..9b1918ff 100644 --- a/packages/datastore-core/test/namespace.spec.ts +++ b/packages/datastore-core/test/namespace.spec.ts @@ -13,19 +13,20 @@ describe('NamespaceDatastore', () => { 'abc', '' ] + + const keys = [ + 'foo', + 'foo/bar', + 'foo/bar/baz', + 'foo/barb', + 'foo/bar/bazb', + 'foo/bar/baz/barb' + ].map((s) => new Key(s)) + prefixes.forEach((prefix) => it(`basic '${prefix}'`, async () => { const mStore = new MemoryDatastore() const store = new NamespaceDatastore(mStore, new Key(prefix)) - const keys = [ - 'foo', - 'foo/bar', - 'foo/bar/baz', - 'foo/barb', - 'foo/bar/bazb', - 'foo/bar/baz/barb' - ].map((s) => new Key(s)) - await Promise.all(keys.map(async key => { await store.put(key, uint8ArrayFromString(key.toString())) })) const nResults = Promise.all(keys.map(async (key) => store.get(key))) const mResults = Promise.all(keys.map(async (key) => mStore.get(new Key(prefix).child(key)))) @@ -45,6 +46,80 @@ describe('NamespaceDatastore', () => { expect(results[0]).to.eql(results[1]) })) + const setupStores = async (keys: Key[]): Promise => { + const prefixes = ['abc', '123', 'sub/prefix'] + const mStore = new MemoryDatastore() + + return Promise.all(prefixes.map(async prefix => { + const store = new NamespaceDatastore(mStore, new Key(prefix)) + + await Promise.all(keys.map(async key => { await store.put(key, uint8ArrayFromString(key.toString())) })) + + return store + })) + } + + const setupNestedStores = async (keys: Key[]): Promise => { + const prefixes = ['abc', '123', 'sub/prefix'] + const mStore = new MemoryDatastore() + + return (await Promise.all(prefixes.map(async prefix => { + const store = new NamespaceDatastore(mStore, new Key(prefix)) + + return Promise.all(prefixes.map(async prefix => { + const child = new NamespaceDatastore(store, new Key(prefix)) + + await Promise.all(keys.map(async key => { await child.put(key, uint8ArrayFromString(key.toString())) })) + + return child + })) + }))).reduce((a, c) => [...a, ...c], []) + } + + it('queries keys under each prefix', async () => { + const stores = await setupStores(keys) + + for (const store of stores) { + const nRes = await all(store.queryKeys({})) + + expect(nRes).deep.equal(keys) + } + }) + + it('queries values under each prefix', async () => { + const stores = await setupStores(keys) + + for (const store of stores) { + const nRes = await all(store.query({})) + const values = keys.map(key => uint8ArrayFromString(key.toString())) + + expect(nRes.map(p => p.key)).deep.equal(keys) + expect(nRes.map(p => p.value)).deep.equal(values) + } + }) + + it('queries keys under each prefix in nested stores', async () => { + const stores = await setupNestedStores(keys) + + for (const store of stores) { + const nRes = await all(store.queryKeys({})) + + expect(nRes).deep.equal(keys) + } + }) + + it('queries values under each prefix in nested stores', async () => { + const stores = await setupNestedStores(keys) + + for (const store of stores) { + const nRes = await all(store.query({})) + const values = keys.map(key => uint8ArrayFromString(key.toString())) + + expect(nRes.map(p => p.key)).deep.equal(keys) + expect(nRes.map(p => p.value)).deep.equal(values) + } + }) + prefixes.forEach((prefix) => { describe(`interface-datastore: '${prefix}'`, () => { interfaceDatastoreTests({ diff --git a/packages/datastore-core/test/sharding.spec.ts b/packages/datastore-core/test/sharding.spec.ts index b4c8b2a6..b02f5bad 100644 --- a/packages/datastore-core/test/sharding.spec.ts +++ b/packages/datastore-core/test/sharding.spec.ts @@ -32,7 +32,7 @@ describe('ShardingDatastore', () => { const store = new ShardingDatastore(ms) return expect(store.open()) .to.eventually.be.rejected() - .with.property('code', 'ERR_DB_OPEN_FAILED') + .with.property('name', 'OpenFailedError') }) it('open - existing', () => { diff --git a/packages/datastore-core/test/tiered.spec.ts b/packages/datastore-core/test/tiered.spec.ts index f2dc73c7..eab536e0 100644 --- a/packages/datastore-core/test/tiered.spec.ts +++ b/packages/datastore-core/test/tiered.spec.ts @@ -39,11 +39,11 @@ describe('Tiered', () => { const val = await store.get(k) expect(val).to.be.eql(v) const exists = await store.has(k) - expect(exists).to.be.eql(true) + expect(exists).to.be.true() }) it('has - key not found', async () => { - expect(await store.has(new Key('hello1'))).to.be.eql(false) + expect(await store.has(new Key('hello1'))).to.be.false() }) it('has and delete', async () => { diff --git a/packages/datastore-core/typedoc.json b/packages/datastore-core/typedoc.json index 6e02e0f9..84f710c5 100644 --- a/packages/datastore-core/typedoc.json +++ b/packages/datastore-core/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts", "./src/base.ts", diff --git a/packages/datastore-fs/CHANGELOG.md b/packages/datastore-fs/CHANGELOG.md index e5bdb960..a14b9dc6 100644 --- a/packages/datastore-fs/CHANGELOG.md +++ b/packages/datastore-fs/CHANGELOG.md @@ -1,3 +1,96 @@ +## [datastore-fs-v11.0.2](https://github.com/ipfs/js-stores/compare/datastore-fs-11.0.1...datastore-fs-11.0.2) (2025-10-03) + +### Dependencies + +* bump level from 8.0.1 to 10.0.0 ([#356](https://github.com/ipfs/js-stores/issues/356)) ([c0ec61f](https://github.com/ipfs/js-stores/commit/c0ec61fe965e3bad9d607a0bd3a3c750f00f41d0)) + +## [datastore-fs-v11.0.1](https://github.com/ipfs/js-stores/compare/datastore-fs-11.0.0...datastore-fs-11.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [datastore-fs-v11.0.0](https://github.com/ipfs/js-stores/compare/datastore-fs-10.0.6...datastore-fs-11.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +## [datastore-fs-v10.0.6](https://github.com/ipfs/js-stores/compare/datastore-fs-10.0.5...datastore-fs-10.0.6) (2025-09-02) + +### Bug Fixes + +* readme typos ([e6b5653](https://github.com/ipfs/js-stores/commit/e6b56533b68e6ed9b90ca3e3f35af8577041a9a2)) + +### Dependencies + +* bump race-signal from 1.1.3 to 2.0.0 ([#355](https://github.com/ipfs/js-stores/issues/355)) ([518fee8](https://github.com/ipfs/js-stores/commit/518fee89d3430534c0ec39551e920447fd558581)) + +## [datastore-fs-v10.0.5](https://github.com/ipfs/js-stores/compare/datastore-fs-10.0.4...datastore-fs-10.0.5) (2025-09-02) + +### Bug Fixes + +* deprecate blockstore-level and datastore-fs ([#353](https://github.com/ipfs/js-stores/issues/353)) ([ebc7912](https://github.com/ipfs/js-stores/commit/ebc7912696d5bd9dc991ece5f0c0d4acfb1f9400)) + +## [datastore-fs-v10.0.4](https://github.com/ipfs/js-stores/compare/datastore-fs-10.0.3...datastore-fs-10.0.4) (2025-05-28) + +### Bug Fixes + +* improve abort signal support ([#350](https://github.com/ipfs/js-stores/issues/350)) ([e17d770](https://github.com/ipfs/js-stores/commit/e17d770cc2fcee77cb0152a855abf162e5a91a99)) + +## [datastore-fs-v10.0.3](https://github.com/ipfs/js-stores/compare/datastore-fs-10.0.2...datastore-fs-10.0.3) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [datastore-fs-v10.0.2](https://github.com/ipfs/js-stores/compare/datastore-fs-10.0.1...datastore-fs-10.0.2) (2024-09-17) + +### Tests + +* add separate-thread concurrency test ([#305](https://github.com/ipfs/js-stores/issues/305)) ([5e3114e](https://github.com/ipfs/js-stores/commit/5e3114e0160ba8366067359f724c6e49807dfb21)), closes [#285](https://github.com/ipfs/js-stores/issues/285) [#284](https://github.com/ipfs/js-stores/issues/284) + +## [datastore-fs-v10.0.1](https://github.com/ipfs/js-stores/compare/datastore-fs-10.0.0...datastore-fs-10.0.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## datastore-fs [10.0.0](https://github.com/ipfs/js-stores/compare/datastore-fs-9.1.9...datastore-fs-10.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + + + +### Dependencies + +* **datastore-core:** upgraded to 10.0.0 +* **interface-datastore:** upgraded to 8.3.0 +* **interface-store:** upgraded to 6.0.0 +* **interface-datastore-tests:** upgraded to 6.0.0 + +## datastore-fs [9.1.9](https://github.com/ipfs/js-stores/compare/datastore-fs-9.1.8...datastore-fs-9.1.9) (2024-08-01) + + +### Dependencies + +* bump it-glob from 2.0.7 to 3.0.1 ([#306](https://github.com/ipfs/js-stores/issues/306)) ([8f6313f](https://github.com/ipfs/js-stores/commit/8f6313f8a22cb537aeeac2a048aad644d3c9a7d2)) + ## datastore-fs [9.1.8](https://github.com/ipfs/js-stores/compare/datastore-fs-v9.1.7...datastore-fs-9.1.8) (2024-02-12) diff --git a/packages/datastore-fs/CODE_OF_CONDUCT.md b/packages/datastore-fs/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/datastore-fs/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/datastore-fs/LICENSE b/packages/datastore-fs/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/datastore-fs/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/datastore-fs/LICENSE-APACHE b/packages/datastore-fs/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/datastore-fs/LICENSE-APACHE +++ b/packages/datastore-fs/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/datastore-fs/README.md b/packages/datastore-fs/README.md index 04454a72..70d7aee2 100644 --- a/packages/datastore-fs/README.md +++ b/packages/datastore-fs/README.md @@ -1,5 +1,9 @@ # datastore-fs +## ⚠️ Deprecation Warning + +**This package is deprecated. Instead, use `datastore-level` in Node.js, and `datastore-idb` in browsers.** + [![ipfs.tech](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.tech) [![Discuss](https://img.shields.io/discourse/https/discuss.ipfs.tech/posts.svg?style=flat-square)](https://discuss.ipfs.tech) [![codecov](https://img.shields.io/codecov/c/github/ipfs/js-stores.svg?style=flat-square)](https://codecov.io/gh/ipfs/js-stores) @@ -9,6 +13,23 @@ # About + + +⚠️ This package is deprecated. Instead, use `datastore-level` in Node.js, and `datastore-idb` in browsers. + A Datastore implementation with a file system backend. ## Example @@ -33,8 +54,8 @@ $ npm i datastore-fs Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/datastore-fs/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/datastore-fs/LICENSE-MIT) / ) # Contribute diff --git a/packages/datastore-fs/package.json b/packages/datastore-fs/package.json index db04aeeb..aa7518ce 100644 --- a/packages/datastore-fs/package.json +++ b/packages/datastore-fs/package.json @@ -1,6 +1,6 @@ { "name": "datastore-fs", - "version": "9.1.8", + "version": "11.0.2", "description": "Datastore implementation with file system backend", "author": "Friedel Ziegelmayer", "license": "Apache-2.0 OR MIT", @@ -37,36 +37,123 @@ "import": "./dist/src/index.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true, - "sourceType": "module" - } + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "clean": "aegir clean", "lint": "aegir lint", "build": "aegir build --bundle false", - "release": "aegir release", "test": "aegir test -t node -t electron-main", "test:node": "aegir test -t node", "test:electron-main": "aegir test -t electron-main", - "dep-check": "aegir dep-check" + "dep-check": "aegir dep-check", + "release": "aegir release" }, "dependencies": { - "datastore-core": "^9.0.0", - "fast-write-atomic": "^0.2.1", - "interface-datastore": "^8.0.0", - "interface-store": "^5.0.0", - "it-glob": "^2.0.6", - "it-map": "^3.0.5", - "it-parallel-batch": "^3.0.4" + "datastore-core": "^11.0.0", + "interface-datastore": "^9.0.0", + "interface-store": "^7.0.0", + "it-glob": "^3.0.3", + "it-map": "^3.1.3", + "it-parallel-batch": "^3.0.8", + "race-signal": "^2.0.0", + "steno": "^4.0.2" }, "devDependencies": { - "@types/mkdirp": "^2.0.0", - "aegir": "^42.2.3", - "interface-datastore-tests": "^5.0.0", - "ipfs-utils": "^9.0.14" + "aegir": "^47.0.16", + "interface-datastore-tests": "^6.0.0", + "ipfs-utils": "^9.0.14", + "threads": "^1.7.0" } } diff --git a/packages/datastore-fs/src/index.ts b/packages/datastore-fs/src/index.ts index bd19b657..d3403ac9 100644 --- a/packages/datastore-fs/src/index.ts +++ b/packages/datastore-fs/src/index.ts @@ -1,6 +1,8 @@ /** * @packageDocumentation * + * ⚠️ This package is deprecated. Instead, use `datastore-level` in Node.js, and `datastore-idb` in browsers. + * * A Datastore implementation with a file system backend. * * @example @@ -14,35 +16,37 @@ import fs from 'node:fs/promises' import path from 'node:path' -import { promisify } from 'util' -import { - BaseDatastore, Errors -} from 'datastore-core' -// @ts-expect-error no types -import fwa from 'fast-write-atomic' -import { - Key, type KeyQuery, type Pair, type Query -} from 'interface-datastore' +import { BaseDatastore } from 'datastore-core' +import { Key } from 'interface-datastore' +import { OpenFailedError, NotFoundError, PutFailedError, DeleteFailedError } from 'interface-store' import glob from 'it-glob' import map from 'it-map' import parallel from 'it-parallel-batch' -import type { AwaitIterable } from 'interface-store' - -const writeAtomic = promisify(fwa) +import { raceSignal } from 'race-signal' +import { Writer } from 'steno' +import type { KeyQuery, Pair, Query } from 'interface-datastore' +import type { AbortOptions, AwaitGenerator, AwaitIterable } from 'interface-store' /** * Write a file atomically */ -async function writeFile (path: string, contents: Uint8Array): Promise { +async function writeFile (file: string, contents: Uint8Array, options?: AbortOptions): Promise { try { - await writeAtomic(path, contents) + options?.signal?.throwIfAborted() + await raceSignal(fs.mkdir(path.dirname(file), { + recursive: true + }), options?.signal) + + const writer = new Writer(file) + options?.signal?.throwIfAborted() + await raceSignal(writer.write(contents), options?.signal) } catch (err: any) { - if (err.code === 'EPERM' && err.syscall === 'rename') { - // fast-write-atomic writes a file to a temp location before renaming it. - // On Windows, if the final file already exists this error is thrown. - // No such error is thrown on Linux/Mac + if (err.syscall === 'rename' && ['ENOENT', 'EPERM'].includes(err.code)) { + // steno writes a file to a temp location before renaming it. + // If the final file already exists this error is thrown. // Make sure we can read & write to this file - await fs.access(path, fs.constants.F_OK | fs.constants.W_OK) + options?.signal?.throwIfAborted() + await raceSignal(fs.access(file, fs.constants.F_OK | fs.constants.W_OK), options?.signal) // The file was created by another context - this means there were // attempts to write the same block by two different function calls @@ -94,7 +98,7 @@ export class FsDatastore extends BaseDatastore { await fs.access(this.path, fs.constants.F_OK | fs.constants.W_OK) if (this.errorIfExists) { - throw Errors.dbOpenFailedError(new Error(`Datastore directory: ${this.path} already exists`)) + throw new OpenFailedError(`Datastore directory: ${this.path} already exists`) } } catch (err: any) { if (err.code === 'ENOENT') { @@ -102,7 +106,7 @@ export class FsDatastore extends BaseDatastore { await fs.mkdir(this.path, { recursive: true }) return } else { - throw Errors.notFoundError(new Error(`Datastore directory: ${this.path} does not exist`)) + throw new NotFoundError(`Datastore directory: ${this.path} does not exist`) } } @@ -146,29 +150,23 @@ export class FsDatastore extends BaseDatastore { return new Key(keyname) } - /** - * Store the given value under the key - */ - async put (key: Key, val: Uint8Array): Promise { + async put (key: Key, val: Uint8Array, options?: AbortOptions): Promise { const parts = this._encode(key) try { - await fs.mkdir(parts.dir, { - recursive: true - }) - await writeFile(parts.file, val) + await writeFile(parts.file, val, options) return key } catch (err: any) { - throw Errors.dbWriteFailedError(err) + throw new PutFailedError(String(err)) } } - async * putMany (source: AwaitIterable): AsyncIterable { + async * putMany (source: AwaitIterable, options?: AbortOptions): AwaitGenerator { yield * parallel( map(source, ({ key, value }) => { return async () => { - await this.put(key, value) + await this.put(key, value, options) return key } @@ -177,27 +175,23 @@ export class FsDatastore extends BaseDatastore { ) } - /** - * Read from the file system - */ - async get (key: Key): Promise { + async get (key: Key, options?: AbortOptions): Promise { const parts = this._encode(key) - let data try { - data = await fs.readFile(parts.file) + options?.signal?.throwIfAborted() + return await raceSignal(fs.readFile(parts.file), options?.signal) } catch (err: any) { - throw Errors.notFoundError(err) + throw new NotFoundError(String(err)) } - return data } - async * getMany (source: AwaitIterable): AsyncIterable { + async * getMany (source: AwaitIterable, options?: AbortOptions): AwaitGenerator { yield * parallel( map(source, key => { return async () => { return { key, - value: await this.get(key) + value: await this.get(key, options) } } }), @@ -205,11 +199,11 @@ export class FsDatastore extends BaseDatastore { ) } - async * deleteMany (source: AwaitIterable): AsyncIterable { + async * deleteMany (source: AwaitIterable, options?: AbortOptions): AwaitGenerator { yield * parallel( map(source, key => { return async () => { - await this.delete(key) + await this.delete(key, options) return key } @@ -218,37 +212,34 @@ export class FsDatastore extends BaseDatastore { ) } - /** - * Check for the existence of the given key - */ - async has (key: Key): Promise { + async has (key: Key, options?: AbortOptions): Promise { const parts = this._encode(key) try { - await fs.access(parts.file) + options?.signal?.throwIfAborted() + await raceSignal(fs.access(parts.file), options?.signal) } catch (err: any) { return false } + return true } - /** - * Delete the record under the given key - */ - async delete (key: Key): Promise { + async delete (key: Key, options?: AbortOptions): Promise { const parts = this._encode(key) try { - await fs.unlink(parts.file) + options?.signal?.throwIfAborted() + await raceSignal(fs.unlink(parts.file), options?.signal) } catch (err: any) { if (err.code === 'ENOENT') { return } - throw Errors.dbDeleteFailedError(err) + throw new DeleteFailedError(String(err)) } } - async * _all (q: Query): AsyncIterable { + async * _all (q: Query, options?: AbortOptions): AwaitGenerator { let prefix = q.prefix ?? '**' // strip leading slashes @@ -263,7 +254,8 @@ export class FsDatastore extends BaseDatastore { for await (const file of files) { try { - const buf = await fs.readFile(file) + options?.signal?.throwIfAborted() + const buf = await raceSignal(fs.readFile(file), options?.signal) const pair: Pair = { key: this._decode(file), @@ -271,6 +263,7 @@ export class FsDatastore extends BaseDatastore { } yield pair + options?.signal?.throwIfAborted() } catch (err: any) { // if keys are removed from the datastore while the query is // running, we may encounter missing files. @@ -281,7 +274,7 @@ export class FsDatastore extends BaseDatastore { } } - async * _allKeys (q: KeyQuery): AsyncIterable { + async * _allKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { let prefix = q.prefix ?? '**' // strip leading slashes @@ -294,6 +287,9 @@ export class FsDatastore extends BaseDatastore { absolute: true }) - yield * map(files, f => this._decode(f)) + yield * map(files, f => { + options?.signal?.throwIfAborted() + return this._decode(f) + }) } } diff --git a/packages/datastore-fs/test/fixtures/writer-worker.ts b/packages/datastore-fs/test/fixtures/writer-worker.ts new file mode 100644 index 00000000..02c71d1e --- /dev/null +++ b/packages/datastore-fs/test/fixtures/writer-worker.ts @@ -0,0 +1,29 @@ +import { Key } from 'interface-datastore' +// @ts-expect-error types are broken: https://github.com/andywer/threads.js/pull/470 +import { expose } from 'threads/worker' +import { FsDatastore } from '../../src/index.js' + +let fs: FsDatastore +expose({ + async isReady (path: string) { + fs = new FsDatastore(path) + try { + await fs.open() + return true + } catch (err) { + // eslint-disable-next-line no-console + console.error('Error opening blockstore', err) + throw err + } + }, + async put (keyString: string, value: Uint8Array) { + const key = new Key(keyString) + try { + return await fs.put(key, value) + } catch (err) { + // eslint-disable-next-line no-console + console.error('Error putting block', err) + throw err + } + } +}) diff --git a/packages/datastore-fs/test/index.spec.ts b/packages/datastore-fs/test/index.spec.ts index 273c30d8..5d3bcd23 100644 --- a/packages/datastore-fs/test/index.spec.ts +++ b/packages/datastore-fs/test/index.spec.ts @@ -1,4 +1,5 @@ /* eslint-env mocha */ +import { setMaxListeners } from 'node:events' import fs from 'node:fs/promises' import path from 'node:path' import { expect } from 'aegir/chai' @@ -6,6 +7,8 @@ import { ShardingDatastore, shard } from 'datastore-core' import { Key } from 'interface-datastore' import { interfaceDatastoreTests } from 'interface-datastore-tests' import tempdir from 'ipfs-utils/src/temp-dir.js' +// @ts-expect-error types are broken: https://github.com/andywer/threads.js/pull/470 +import { spawn, Thread, Worker } from 'threads' import { FsDatastore } from '../src/index.js' const utf8Encoder = new TextEncoder() @@ -43,7 +46,7 @@ describe('FsDatastore', () => { const dir = tempdir() const store = new FsDatastore(dir, { createIfMissing: false }) await expect(store.open()).to.eventually.be.rejected - .with.property('code', 'ERR_NOT_FOUND') + .with.property('name', 'NotFoundError') }) it('errorIfExists: true - folder exists', async () => { @@ -53,7 +56,7 @@ describe('FsDatastore', () => { }) const store = new FsDatastore(dir, { errorIfExists: true }) await expect(store.open()).to.eventually.be.rejected - .with.property('code', 'ERR_DB_OPEN_FAILED') + .with.property('name', 'OpenFailedError') }) }) @@ -89,7 +92,7 @@ describe('FsDatastore', () => { await fs.get(key) throw new Error('Should have errored') } catch (err: any) { - expect(err.code).to.equal('ERR_NOT_FOUND') + expect(err).to.have.property('name', 'NotFoundError') } }) @@ -105,7 +108,7 @@ describe('FsDatastore', () => { await fs.get(key) throw new Error('Should have errored') } catch (err: any) { - expect(err.code).to.equal('ERR_NOT_FOUND') + expect(err).to.have.property('name', 'NotFoundError') } }) @@ -116,7 +119,7 @@ describe('FsDatastore', () => { await ShardingDatastore.create(fstore, new shard.NextToLast(2)) const file = await fs.readFile(path.join(dir, shard.SHARDING_FN + '.data')) - expect(file.toString()).to.be.eql('/repo/flatfs/shard/v1/next-to-last/2\n') + expect(file.toString()).to.equal('/repo/flatfs/shard/v1/next-to-last/2\n') await fs.rm(dir, { recursive: true @@ -170,4 +173,37 @@ describe('FsDatastore', () => { expect(res).to.deep.equal(value) }) + + /** + * This test spawns 10 workers that concurrently write to the same blockstore. + * it's different from the previous test because it uses workers to write to the blockstore + * which means that the writes are happening in parallel in different threads. + */ + it('can survive concurrent worker writes', async () => { + const dir = tempdir() + const key = new Key('CIQGFTQ7FSI2COUXWWLOQ45VUM2GUZCGAXLWCTOKKPGTUWPXHBNIVOY') + const workers = await Promise.all(new Array(10).fill(0).map(async () => { + const w = new Worker('./fixtures/writer-worker.js') + setMaxListeners(Infinity, w) + const worker = await spawn(w) + await worker.isReady(dir) + return worker + })) + + try { + const value = utf8Encoder.encode('Hello world') + // 100 iterations of looping over all workers and putting the same key value pair + await Promise.all(new Array(100).fill(0).map(async () => { + return Promise.all(workers.map(async (worker: any) => worker.put(key.toString(), value))) + })) + + const fs = new FsDatastore(dir) + await fs.open() + const res = await fs.get(key) + + expect(res).to.deep.equal(value) + } finally { + await Promise.all(workers.map(async (worker: any) => Thread.terminate(worker))) + } + }) }) diff --git a/packages/datastore-fs/typedoc.json b/packages/datastore-fs/typedoc.json index f599dc72..db0b0747 100644 --- a/packages/datastore-fs/typedoc.json +++ b/packages/datastore-fs/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts" ] diff --git a/packages/datastore-idb/CHANGELOG.md b/packages/datastore-idb/CHANGELOG.md index 63178426..ffcdd236 100644 --- a/packages/datastore-idb/CHANGELOG.md +++ b/packages/datastore-idb/CHANGELOG.md @@ -1,3 +1,74 @@ +## [datastore-idb-v4.0.1](https://github.com/ipfs/js-stores/compare/datastore-idb-4.0.0...datastore-idb-4.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [datastore-idb-v4.0.0](https://github.com/ipfs/js-stores/compare/datastore-idb-3.0.4...datastore-idb-4.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +## [datastore-idb-v3.0.4](https://github.com/ipfs/js-stores/compare/datastore-idb-3.0.3...datastore-idb-3.0.4) (2025-09-02) + +### Dependencies + +* bump race-signal from 1.1.3 to 2.0.0 ([#355](https://github.com/ipfs/js-stores/issues/355)) ([518fee8](https://github.com/ipfs/js-stores/commit/518fee89d3430534c0ec39551e920447fd558581)) + +## [datastore-idb-v3.0.3](https://github.com/ipfs/js-stores/compare/datastore-idb-3.0.2...datastore-idb-3.0.3) (2025-05-28) + +### Bug Fixes + +* improve abort signal support ([#350](https://github.com/ipfs/js-stores/issues/350)) ([e17d770](https://github.com/ipfs/js-stores/commit/e17d770cc2fcee77cb0152a855abf162e5a91a99)) + +## [datastore-idb-v3.0.2](https://github.com/ipfs/js-stores/compare/datastore-idb-3.0.1...datastore-idb-3.0.2) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [datastore-idb-v3.0.1](https://github.com/ipfs/js-stores/compare/datastore-idb-3.0.0...datastore-idb-3.0.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## datastore-idb [3.0.0](https://github.com/ipfs/js-stores/compare/datastore-idb-2.1.9...datastore-idb-3.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + + + +### Dependencies + +* **datastore-core:** upgraded to 10.0.0 +* **interface-datastore:** upgraded to 8.3.0 +* **interface-store:** upgraded to 6.0.0 +* **interface-datastore-tests:** upgraded to 6.0.0 + +## datastore-idb [2.1.9](https://github.com/ipfs/js-stores/compare/datastore-idb-2.1.8...datastore-idb-2.1.9) (2024-04-09) + + +### Bug Fixes + +* throw read error on read error ([#304](https://github.com/ipfs/js-stores/issues/304)) ([f14c824](https://github.com/ipfs/js-stores/commit/f14c8249dfd7bbd1385bbf3513b2b3d5e0e6860c)), closes [#299](https://github.com/ipfs/js-stores/issues/299) + ## datastore-idb [2.1.8](https://github.com/ipfs/js-stores/compare/datastore-idb-v2.1.7...datastore-idb-2.1.8) (2024-02-12) diff --git a/packages/datastore-idb/CODE_OF_CONDUCT.md b/packages/datastore-idb/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/datastore-idb/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/datastore-idb/LICENSE b/packages/datastore-idb/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/datastore-idb/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/datastore-idb/LICENSE-APACHE b/packages/datastore-idb/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/datastore-idb/LICENSE-APACHE +++ b/packages/datastore-idb/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/datastore-idb/README.md b/packages/datastore-idb/README.md index 68c97a86..ebbb5f33 100644 --- a/packages/datastore-idb/README.md +++ b/packages/datastore-idb/README.md @@ -9,6 +9,21 @@ # About + + A Datastore implementation for browsers that stores data in [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API). ## Example @@ -27,7 +42,7 @@ $ npm i datastore-idb ## Browser ` @@ -41,8 +56,8 @@ Loading this module through a script tag will make it's exports available as `Da Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/datastore-idb/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/datastore-idb/LICENSE-MIT) / ) # Contribute diff --git a/packages/datastore-idb/package.json b/packages/datastore-idb/package.json index 3aff6d72..ce304195 100644 --- a/packages/datastore-idb/package.json +++ b/packages/datastore-idb/package.json @@ -1,6 +1,6 @@ { "name": "datastore-idb", - "version": "2.1.8", + "version": "4.0.1", "description": "Datastore implementation with IndexedDB backend.", "author": "Hugo Dias ", "license": "Apache-2.0 OR MIT", @@ -38,15 +38,98 @@ "import": "./dist/src/index.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": [ - "tsconfig.json", - "benchmarks/datastore-level/tsconfig.json" + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } ], - "sourceType": "module" - } + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "test": "aegir test -t browser -t webworker", @@ -54,18 +137,20 @@ "test:webworker": "aegir test -t webworker", "build": "aegir build", "lint": "aegir lint", - "release": "aegir release", - "dep-check": "aegir dep-check" + "dep-check": "aegir dep-check", + "release": "aegir release" }, "dependencies": { - "datastore-core": "^9.0.0", - "idb": "^8.0.0", - "interface-datastore": "^8.0.0", - "it-filter": "^3.0.4", - "it-sort": "^3.0.4" + "datastore-core": "^11.0.0", + "idb": "^8.0.3", + "interface-datastore": "^9.0.0", + "interface-store": "^7.0.0", + "it-filter": "^3.1.3", + "it-sort": "^3.0.8", + "race-signal": "^2.0.0" }, "devDependencies": { - "aegir": "^42.2.3", - "interface-datastore-tests": "^5.0.0" + "aegir": "^47.0.16", + "interface-datastore-tests": "^6.0.0" } } diff --git a/packages/datastore-idb/src/index.ts b/packages/datastore-idb/src/index.ts index b6b2e935..eb040bc1 100644 --- a/packages/datastore-idb/src/index.ts +++ b/packages/datastore-idb/src/index.ts @@ -12,11 +12,16 @@ * ``` */ -import { Errors, BaseDatastore } from 'datastore-core' -import { openDB, deleteDB, type IDBPDatabase } from 'idb' -import { type Batch, Key, type KeyQuery, type Pair, type Query } from 'interface-datastore' +import { BaseDatastore } from 'datastore-core' +import { openDB, deleteDB } from 'idb' +import { Key } from 'interface-datastore' +import { DeleteFailedError, GetFailedError, NotFoundError, OpenFailedError, PutFailedError } from 'interface-store' import filter from 'it-filter' import sort from 'it-sort' +import { raceSignal } from 'race-signal' +import type { IDBPDatabase } from 'idb' +import type { Batch, KeyQuery, Pair, Query } from 'interface-datastore' +import type { AbortOptions, AwaitGenerator } from 'interface-store' export interface IDBDatastoreInit { /** @@ -52,7 +57,7 @@ export class IDBDatastore extends BaseDatastore { } }) } catch (err: any) { - throw Errors.dbOpenFailedError(err) + throw new OpenFailedError(String(err)) } } @@ -60,21 +65,22 @@ export class IDBDatastore extends BaseDatastore { this.db?.close() } - async put (key: Key, val: Uint8Array): Promise { + async put (key: Key, val: Uint8Array, options?: AbortOptions): Promise { if (this.db == null) { throw new Error('Datastore needs to be opened.') } try { - await this.db.put(this.location, val, key.toString()) - - return key + options?.signal?.throwIfAborted() + await raceSignal(this.db.put(this.location, val, key.toString()), options?.signal) } catch (err: any) { - throw Errors.dbWriteFailedError(err) + throw new PutFailedError(String(err)) } + + return key } - async get (key: Key): Promise { + async get (key: Key, options?: AbortOptions): Promise { if (this.db == null) { throw new Error('Datastore needs to be opened.') } @@ -82,39 +88,43 @@ export class IDBDatastore extends BaseDatastore { let val: Uint8Array | undefined try { - val = await this.db.get(this.location, key.toString()) + options?.signal?.throwIfAborted() + val = await raceSignal(this.db.get(this.location, key.toString()), options?.signal) } catch (err: any) { - throw Errors.dbReadFailedError(err) + throw new GetFailedError(String(err)) } if (val === undefined) { - throw Errors.notFoundError() + throw new NotFoundError() } return val } - async has (key: Key): Promise { + async has (key: Key, options?: AbortOptions): Promise { if (this.db == null) { throw new Error('Datastore needs to be opened.') } try { - return Boolean(await this.db.getKey(this.location, key.toString())) + options?.signal?.throwIfAborted() + const result = await raceSignal(this.db.getKey(this.location, key.toString())) + return Boolean(result) } catch (err: any) { - throw Errors.dbReadFailedError(err) + throw new GetFailedError(String(err)) } } - async delete (key: Key): Promise { + async delete (key: Key, options?: AbortOptions): Promise { if (this.db == null) { throw new Error('Datastore needs to be opened.') } try { - await this.db.delete(this.location, key.toString()) + options?.signal?.throwIfAborted() + await raceSignal(this.db.delete(this.location, key.toString()), options?.signal) } catch (err: any) { - throw Errors.dbWriteFailedError(err) + throw new DeleteFailedError(String(err)) } } @@ -129,11 +139,13 @@ export class IDBDatastore extends BaseDatastore { delete (key) { dels.push(key) }, - commit: async () => { + commit: async (options?: AbortOptions) => { if (this.db == null) { throw new Error('Datastore needs to be opened.') } + options?.signal?.throwIfAborted() + const tx = this.db.transaction(this.location, 'readwrite') try { @@ -155,7 +167,8 @@ export class IDBDatastore extends BaseDatastore { await tx.done }) - await Promise.all(ops.map(async op => { await op() })) + options?.signal?.throwIfAborted() + await raceSignal(Promise.all(ops.map(async op => { await op() })), options?.signal) } catch { tx.abort() } @@ -163,10 +176,10 @@ export class IDBDatastore extends BaseDatastore { } } - async * query (q: Query): AsyncIterable { + async * query (q: Query, options?: AbortOptions): AwaitGenerator { let it = this.#queryIt(q, (key, value) => { return { key, value } - }) + }, options) if (Array.isArray(q.filters)) { it = q.filters.reduce((it, f) => filter(it, f), it) @@ -179,8 +192,8 @@ export class IDBDatastore extends BaseDatastore { yield * it } - async * queryKeys (q: KeyQuery): AsyncIterable { - let it = this.#queryIt(q, (key) => key) + async * queryKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { + let it = this.#queryIt(q, (key) => key, options) if (Array.isArray(q.filters)) { it = q.filters.reduce((it, f) => filter(it, f), it) @@ -193,7 +206,7 @@ export class IDBDatastore extends BaseDatastore { yield * it } - async * #queryIt (q: { prefix?: string, offset?: number, limit?: number }, transform: (key: Key, value: Uint8Array) => T): AsyncIterable { + async * #queryIt (q: { prefix?: string, offset?: number, limit?: number }, transform: (key: Key, value: Uint8Array) => T, options?: AbortOptions): AsyncIterable { if (this.db == null) { throw new Error('Datastore needs to be opened.') } @@ -201,8 +214,11 @@ export class IDBDatastore extends BaseDatastore { let yielded = 0 let index = -1 + options?.signal?.throwIfAborted() + for (const key of await this.db.getAllKeys(this.location)) { - if (q.prefix != null && !key.toString().startsWith(q.prefix)) { // eslint-disable-line @typescript-eslint/no-base-to-string + options?.signal?.throwIfAborted() + if (q.prefix != null && !key.toString().startsWith(q.prefix)) { continue } @@ -216,13 +232,13 @@ export class IDBDatastore extends BaseDatastore { continue } - const k = new Key(key.toString()) // eslint-disable-line @typescript-eslint/no-base-to-string + const k = new Key(key.toString()) let value: Uint8Array | undefined try { - value = await this.get(k) + value = await this.get(k, options) } catch (err: any) { - if (err.code !== 'ERR_NOT_FOUND') { + if (err.name !== 'NotFoundError') { throw err } continue @@ -234,6 +250,8 @@ export class IDBDatastore extends BaseDatastore { yield transform(k, value) + options?.signal?.throwIfAborted() + yielded++ } } diff --git a/packages/datastore-idb/test/index.spec.ts b/packages/datastore-idb/test/index.spec.ts index ecf3e381..75a6554a 100644 --- a/packages/datastore-idb/test/index.spec.ts +++ b/packages/datastore-idb/test/index.spec.ts @@ -66,7 +66,7 @@ describe('IndexedDB Datastore', function () { it('should not explode under unreasonable load', function (done) { this.timeout(10000) - const updater = setInterval(async () => { // eslint-disable-line @typescript-eslint/no-misused-promises + const updater = setInterval(async () => { try { const key = new Key(`/a-${Date.now()}`) @@ -81,7 +81,7 @@ describe('IndexedDB Datastore', function () { } }, 0) - const mutatorQuery = setInterval(async () => { // eslint-disable-line @typescript-eslint/no-misused-promises + const mutatorQuery = setInterval(async () => { try { for await (const { key } of store.query({})) { await store.get(key) @@ -100,7 +100,7 @@ describe('IndexedDB Datastore', function () { } }, 0) - const readOnlyQuery = setInterval(async () => { // eslint-disable-line @typescript-eslint/no-misused-promises + const readOnlyQuery = setInterval(async () => { try { for await (const { key } of store.query({})) { await store.has(key) diff --git a/packages/datastore-idb/tsconfig.json b/packages/datastore-idb/tsconfig.json index 58cae45c..e30932c0 100644 --- a/packages/datastore-idb/tsconfig.json +++ b/packages/datastore-idb/tsconfig.json @@ -16,6 +16,9 @@ }, { "path": "../interface-datastore-tests" + }, + { + "path": "../interface-store" } ] } diff --git a/packages/datastore-idb/typedoc.json b/packages/datastore-idb/typedoc.json index f599dc72..db0b0747 100644 --- a/packages/datastore-idb/typedoc.json +++ b/packages/datastore-idb/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts" ] diff --git a/packages/datastore-level/CHANGELOG.md b/packages/datastore-level/CHANGELOG.md index 14a36df6..22639c5b 100644 --- a/packages/datastore-level/CHANGELOG.md +++ b/packages/datastore-level/CHANGELOG.md @@ -1,3 +1,80 @@ +## [datastore-level-v12.0.2](https://github.com/ipfs/js-stores/compare/datastore-level-12.0.1...datastore-level-12.0.2) (2025-10-03) + +### Dependencies + +* bump level from 8.0.1 to 10.0.0 ([#356](https://github.com/ipfs/js-stores/issues/356)) ([c0ec61f](https://github.com/ipfs/js-stores/commit/c0ec61fe965e3bad9d607a0bd3a3c750f00f41d0)) + +## [datastore-level-v12.0.1](https://github.com/ipfs/js-stores/compare/datastore-level-12.0.0...datastore-level-12.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [datastore-level-v12.0.0](https://github.com/ipfs/js-stores/compare/datastore-level-11.0.4...datastore-level-12.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +## [datastore-level-v11.0.4](https://github.com/ipfs/js-stores/compare/datastore-level-11.0.3...datastore-level-11.0.4) (2025-09-02) + +### Dependencies + +* bump race-signal from 1.1.3 to 2.0.0 ([#355](https://github.com/ipfs/js-stores/issues/355)) ([518fee8](https://github.com/ipfs/js-stores/commit/518fee89d3430534c0ec39551e920447fd558581)) + +## [datastore-level-v11.0.3](https://github.com/ipfs/js-stores/compare/datastore-level-11.0.2...datastore-level-11.0.3) (2025-05-28) + +### Bug Fixes + +* improve abort signal support ([#350](https://github.com/ipfs/js-stores/issues/350)) ([e17d770](https://github.com/ipfs/js-stores/commit/e17d770cc2fcee77cb0152a855abf162e5a91a99)) + +## [datastore-level-v11.0.2](https://github.com/ipfs/js-stores/compare/datastore-level-11.0.1...datastore-level-11.0.2) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [datastore-level-v11.0.1](https://github.com/ipfs/js-stores/compare/datastore-level-11.0.0...datastore-level-11.0.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## datastore-level [11.0.0](https://github.com/ipfs/js-stores/compare/datastore-level-10.1.8...datastore-level-11.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + + + +### Dependencies + +* **datastore-core:** upgraded to 10.0.0 +* **interface-datastore:** upgraded to 8.3.0 +* **interface-store:** upgraded to 6.0.0 +* **interface-datastore-tests:** upgraded to 6.0.0 + +## datastore-level [10.1.8](https://github.com/ipfs/js-stores/compare/datastore-level-10.1.7...datastore-level-10.1.8) (2024-04-09) + + +### Bug Fixes + +* throw read error on read error ([#304](https://github.com/ipfs/js-stores/issues/304)) ([f14c824](https://github.com/ipfs/js-stores/commit/f14c8249dfd7bbd1385bbf3513b2b3d5e0e6860c)), closes [#299](https://github.com/ipfs/js-stores/issues/299) + ## datastore-level [10.1.7](https://github.com/ipfs/js-stores/compare/datastore-level-v10.1.6...datastore-level-10.1.7) (2024-02-12) diff --git a/packages/datastore-level/CODE_OF_CONDUCT.md b/packages/datastore-level/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/datastore-level/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/datastore-level/LICENSE b/packages/datastore-level/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/datastore-level/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/datastore-level/LICENSE-APACHE b/packages/datastore-level/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/datastore-level/LICENSE-APACHE +++ b/packages/datastore-level/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/datastore-level/README.md b/packages/datastore-level/README.md index 938cab76..07a6c85e 100644 --- a/packages/datastore-level/README.md +++ b/packages/datastore-level/README.md @@ -9,6 +9,21 @@ # About + + A Datastore implementation that uses a flavour of [Level](https://leveljs.org/) as a backend. This module is targetted at Node.js. It is possible to use it in a browser but you should probably use IDBDatastore instead. @@ -59,7 +74,7 @@ $ npm i datastore-level ## Browser ` @@ -73,8 +88,8 @@ Loading this module through a script tag will make it's exports available as `Da Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/datastore-level/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/datastore-level/LICENSE-MIT) / ) # Contribute diff --git a/packages/datastore-level/package.json b/packages/datastore-level/package.json index 3d350a1f..5fdee811 100644 --- a/packages/datastore-level/package.json +++ b/packages/datastore-level/package.json @@ -1,6 +1,6 @@ { "name": "datastore-level", - "version": "10.1.7", + "version": "12.0.2", "description": "Datastore implementation with level(up|down) backend", "author": "Friedel Ziegelmayer", "license": "Apache-2.0 OR MIT", @@ -39,18 +39,103 @@ "import": "./dist/src/index.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true, - "sourceType": "module" - } + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "clean": "aegir clean", "lint": "aegir lint", "build": "aegir build", - "release": "aegir release", "test": "aegir test", "test:node": "aegir test -t node --cov", "test:chrome": "aegir test -t browser --cov", @@ -58,21 +143,24 @@ "test:firefox": "aegir test -t browser -- --browser firefox", "test:firefox-webworker": "aegir test -t webworker -- --browser firefox", "test:electron-main": "aegir test -t electron-main", - "dep-check": "aegir dep-check" + "dep-check": "aegir dep-check", + "release": "aegir release" }, "dependencies": { - "datastore-core": "^9.0.0", - "interface-datastore": "^8.0.0", - "it-filter": "^3.0.4", - "it-map": "^3.0.5", - "it-sort": "^3.0.4", - "it-take": "^3.0.4", - "level": "^8.0.1" + "datastore-core": "^11.0.0", + "interface-datastore": "^9.0.0", + "interface-store": "^7.0.0", + "it-filter": "^3.1.3", + "it-map": "^3.1.3", + "it-sort": "^3.0.8", + "it-take": "^3.0.8", + "level": "^10.0.0", + "race-signal": "^2.0.0" }, "devDependencies": { - "aegir": "^42.2.3", - "interface-datastore-tests": "^5.0.0", + "aegir": "^47.0.16", + "interface-datastore-tests": "^6.0.0", "ipfs-utils": "^9.0.14", - "memory-level": "^1.0.0" + "memory-level": "^3.1.0" } } diff --git a/packages/datastore-level/src/index.ts b/packages/datastore-level/src/index.ts index 66d8411a..ab238572 100644 --- a/packages/datastore-level/src/index.ts +++ b/packages/datastore-level/src/index.ts @@ -44,13 +44,17 @@ * More information: [https://github.com/Level/level-js/blob/master/UPGRADING.md#new-database-prefix](https://github.com/Level/level-js/blob/99831913e905d19e5f6dee56d512b7264fbed7bd/UPGRADING.md#new-database-prefix) */ -import { BaseDatastore, Errors } from 'datastore-core' -import { type Batch, Key, type KeyQuery, type Pair, type Query } from 'interface-datastore' +import { BaseDatastore } from 'datastore-core' +import { Key } from 'interface-datastore' +import { DeleteFailedError, GetFailedError, NotFoundError, OpenFailedError, PutFailedError } from 'interface-store' import filter from 'it-filter' import map from 'it-map' import sort from 'it-sort' import take from 'it-take' import { Level } from 'level' +import { raceSignal } from 'race-signal' +import type { Batch, KeyQuery, Pair, Query } from 'interface-datastore' +import type { AbortOptions, AwaitGenerator } from 'interface-store' import type { DatabaseOptions, OpenOptions, IteratorOptions } from 'level' interface BatchPut { @@ -95,52 +99,51 @@ export class LevelDatastore extends BaseDatastore { try { await this.db.open(this.opts) } catch (err: any) { - throw Errors.dbOpenFailedError(err) + throw new OpenFailedError(String(err)) } } - async put (key: Key, value: Uint8Array): Promise { + async put (key: Key, value: Uint8Array, options?: AbortOptions): Promise { try { - await this.db.put(key.toString(), value) + options?.signal?.throwIfAborted() + await raceSignal(this.db.put(key.toString(), value), options?.signal) return key } catch (err: any) { - throw Errors.dbWriteFailedError(err) + throw new PutFailedError(String(err)) } } - async get (key: Key): Promise { + async get (key: Key, options?: AbortOptions): Promise { let data + try { - data = await this.db.get(key.toString()) + options?.signal?.throwIfAborted() + data = await raceSignal(this.db.get(key.toString()), options?.signal) } catch (err: any) { - if (err.notFound != null) { - throw Errors.notFoundError(err) - } + throw new GetFailedError(String(err)) + } - throw Errors.dbWriteFailedError(err) + if (data == null) { + throw new NotFoundError() } + return data } - async has (key: Key): Promise { - try { - await this.db.get(key.toString()) - } catch (err: any) { - if (err.notFound != null) { - return false - } + async has (key: Key, options?: AbortOptions): Promise { + options?.signal?.throwIfAborted() + const data = await raceSignal(this.db.get(key.toString()), options?.signal) - throw err - } - return true + return data != null } - async delete (key: Key): Promise { + async delete (key: Key, options?: AbortOptions): Promise { try { - await this.db.del(key.toString()) + options?.signal?.throwIfAborted() + await raceSignal(this.db.del(key.toString()), options?.signal) } catch (err: any) { - throw Errors.dbDeleteFailedError(err) + throw new DeleteFailedError(String(err)) } } @@ -165,20 +168,24 @@ export class LevelDatastore extends BaseDatastore { key: key.toString() }) }, - commit: async () => { + commit: async (options?: AbortOptions) => { if (this.db.batch == null) { throw new Error('Batch operations unsupported by underlying Level') } - await this.db.batch(ops) + options?.signal?.throwIfAborted() + await raceSignal(this.db.batch(ops), options?.signal) } } } - query (q: Query): AsyncIterable { - let it = this._query({ + query (q: Query, options?: AbortOptions): AwaitGenerator { + let it = map(this._query({ values: true, prefix: q.prefix + }), (res) => { + options?.signal?.throwIfAborted() + return res }) if (Array.isArray(q.filters)) { @@ -202,11 +209,14 @@ export class LevelDatastore extends BaseDatastore { return it } - queryKeys (q: KeyQuery): AsyncIterable { + queryKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { let it = map(this._query({ values: false, prefix: q.prefix - }), ({ key }) => key) + }), ({ key }) => { + options?.signal?.throwIfAborted() + return key + }) if (Array.isArray(q.filters)) { it = q.filters.reduce((it, f) => filter(it, f), it) diff --git a/packages/datastore-level/tsconfig.json b/packages/datastore-level/tsconfig.json index 031d7aae..8bae2503 100644 --- a/packages/datastore-level/tsconfig.json +++ b/packages/datastore-level/tsconfig.json @@ -16,6 +16,9 @@ }, { "path": "../interface-datastore-tests" + }, + { + "path": "../interface-store" } ] } diff --git a/packages/datastore-level/typedoc.json b/packages/datastore-level/typedoc.json index f599dc72..db0b0747 100644 --- a/packages/datastore-level/typedoc.json +++ b/packages/datastore-level/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts" ] diff --git a/packages/datastore-s3/CHANGELOG.md b/packages/datastore-s3/CHANGELOG.md index e2ef1610..5e9b184a 100644 --- a/packages/datastore-s3/CHANGELOG.md +++ b/packages/datastore-s3/CHANGELOG.md @@ -1,3 +1,85 @@ +## [datastore-s3-v13.0.1](https://github.com/ipfs/js-stores/compare/datastore-s3-13.0.0...datastore-s3-13.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [datastore-s3-v13.0.0](https://github.com/ipfs/js-stores/compare/datastore-s3-12.0.5...datastore-s3-13.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +### Trivial Changes + +* bump sinon from 20.0.0 to 21.0.0 ([#351](https://github.com/ipfs/js-stores/issues/351)) ([f24dd4f](https://github.com/ipfs/js-stores/commit/f24dd4f9b6eb681b1f9652409a558a8bcc50f4a4)) + +## [datastore-s3-v12.0.5](https://github.com/ipfs/js-stores/compare/datastore-s3-12.0.4...datastore-s3-12.0.5) (2025-09-02) + +### Bug Fixes + +* deprecate blockstore-level and datastore-fs ([#353](https://github.com/ipfs/js-stores/issues/353)) ([ebc7912](https://github.com/ipfs/js-stores/commit/ebc7912696d5bd9dc991ece5f0c0d4acfb1f9400)) + +## [datastore-s3-v12.0.4](https://github.com/ipfs/js-stores/compare/datastore-s3-12.0.3...datastore-s3-12.0.4) (2025-05-28) + +### Bug Fixes + +* improve abort signal support ([#350](https://github.com/ipfs/js-stores/issues/350)) ([e17d770](https://github.com/ipfs/js-stores/commit/e17d770cc2fcee77cb0152a855abf162e5a91a99)) + +## [datastore-s3-v12.0.3](https://github.com/ipfs/js-stores/compare/datastore-s3-12.0.2...datastore-s3-12.0.3) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) +* **dev:** bump sinon from 19.0.5 to 20.0.0 ([#337](https://github.com/ipfs/js-stores/issues/337)) ([ec2a54a](https://github.com/ipfs/js-stores/commit/ec2a54a1ef2ecbee862d17710430a51b79063183)) + +## [datastore-s3-v12.0.2](https://github.com/ipfs/js-stores/compare/datastore-s3-12.0.1...datastore-s3-12.0.2) (2024-09-13) + +### Dependencies + +* **dev:** bump sinon from 18.0.1 to 19.0.2 ([#322](https://github.com/ipfs/js-stores/issues/322)) ([8ea2df5](https://github.com/ipfs/js-stores/commit/8ea2df5c8f531b086b10130b8a3ce59a4d5bc2ba)) + +## [datastore-s3-v12.0.1](https://github.com/ipfs/js-stores/compare/datastore-s3-12.0.0...datastore-s3-12.0.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## datastore-s3 [12.0.0](https://github.com/ipfs/js-stores/compare/datastore-s3-11.1.12...datastore-s3-12.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + + + +### Dependencies + +* **datastore-core:** upgraded to 10.0.0 +* **interface-datastore:** upgraded to 8.3.0 +* **interface-store:** upgraded to 6.0.0 +* **interface-datastore-tests:** upgraded to 6.0.0 + +## datastore-s3 [11.1.12](https://github.com/ipfs/js-stores/compare/datastore-s3-11.1.11...datastore-s3-11.1.12) (2024-08-01) + + +### Dependencies + +* **dev:** bump sinon from 17.0.2 to 18.0.0 ([#308](https://github.com/ipfs/js-stores/issues/308)) ([0fbfe11](https://github.com/ipfs/js-stores/commit/0fbfe1112a102055d75f077ff799fbb1001e6aa7)) + ## datastore-s3 [11.1.11](https://github.com/ipfs/js-stores/compare/datastore-s3-v11.1.10...datastore-s3-11.1.11) (2024-02-12) diff --git a/packages/datastore-s3/CODE_OF_CONDUCT.md b/packages/datastore-s3/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/datastore-s3/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/datastore-s3/LICENSE b/packages/datastore-s3/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/datastore-s3/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/datastore-s3/LICENSE-APACHE b/packages/datastore-s3/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/datastore-s3/LICENSE-APACHE +++ b/packages/datastore-s3/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/datastore-s3/README.md b/packages/datastore-s3/README.md index 2abdac48..49493922 100644 --- a/packages/datastore-s3/README.md +++ b/packages/datastore-s3/README.md @@ -9,6 +9,21 @@ # About + + A Datastore implementation that stores data on Amazon S3. ## Example - Quickstart @@ -50,7 +65,7 @@ $ npm i datastore-s3 ## Browser ` @@ -64,8 +79,8 @@ Loading this module through a script tag will make it's exports available as `Da Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/datastore-s3/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/datastore-s3/LICENSE-MIT) / ) # Contribute diff --git a/packages/datastore-s3/examples/helia/index.js b/packages/datastore-s3/examples/helia/index.js index 0de17895..c1cd09c3 100644 --- a/packages/datastore-s3/examples/helia/index.js +++ b/packages/datastore-s3/examples/helia/index.js @@ -1,8 +1,10 @@ -import { createHelia } from 'helia' -import { unixfs } from '@helia/unixfs' -import toBuffer from 'it-to-buffer' +/* eslint-disable no-console */ + import { S3 } from '@aws-sdk/client-s3' +import { unixfs } from '@helia/unixfs' import { DatastoreS3 } from 'datastore-s3' +import { createHelia } from 'helia' +import toBuffer from 'it-to-buffer' async function main () { // Configure S3 as normal @@ -18,7 +20,7 @@ async function main () { // Create a new Helia node with our S3 backed Repo console.log('Start Helia') - const node = await createHelia({ + const helia = await createHelia({ datastore }) @@ -45,7 +47,7 @@ async function main () { // After everything is done, shut the node down // We don't need to worry about catching errors here console.log('\n\nStopping the node') - await node.stop() + await helia.stop() } main() diff --git a/packages/datastore-s3/package.json b/packages/datastore-s3/package.json index b2e6f90f..e1cfa8d8 100644 --- a/packages/datastore-s3/package.json +++ b/packages/datastore-s3/package.json @@ -1,6 +1,6 @@ { "name": "datastore-s3", - "version": "11.1.11", + "version": "13.0.1", "description": "IPFS datastore implementation backed by s3", "license": "Apache-2.0 OR MIT", "homepage": "https://github.com/ipfs/js-stores/tree/main/packages/datastore-s3#readme", @@ -36,18 +36,103 @@ "import": "./dist/src/index.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true, - "sourceType": "module" - } + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "clean": "aegir clean", "lint": "aegir lint", "build": "aegir build", - "release": "aegir release", "test": "aegir test", "test:node": "aegir test -t node --cov", "test:chrome": "aegir test -t browser --cov", @@ -55,22 +140,25 @@ "test:firefox": "aegir test -t browser -- --browser firefox", "test:firefox-webworker": "aegir test -t webworker -- --browser firefox", "test:electron-main": "aegir test -t electron-main", - "dep-check": "aegir dep-check" + "dep-check": "aegir dep-check", + "release": "aegir release" }, "dependencies": { - "@aws-sdk/client-s3": "^3.511.0", - "datastore-core": "^9.0.0", - "interface-datastore": "^8.0.0", - "interface-store": "^5.0.0", - "it-filter": "^3.0.4", - "it-to-buffer": "^4.0.5", - "uint8arrays": "^5.0.2" + "@aws-sdk/client-s3": "^3.817.0", + "datastore-core": "^11.0.0", + "interface-datastore": "^9.0.0", + "interface-store": "^7.0.0", + "it-filter": "^3.1.3", + "it-to-buffer": "^4.0.9", + "uint8arrays": "^5.1.0" }, "devDependencies": { - "@types/sinon": "^17.0.3", - "aegir": "^42.2.3", - "interface-datastore-tests": "^5.0.0", - "p-defer": "^4.0.0", - "sinon": "^17.0.1" + "@types/sinon": "^21.0.0", + "@types/xmldom": "^0.1.34", + "aegir": "^47.0.16", + "interface-datastore-tests": "^6.0.0", + "p-defer": "^4.0.1", + "sinon": "^21.0.0", + "xmldom": "^0.6.0" } } diff --git a/packages/datastore-s3/src/index.ts b/packages/datastore-s3/src/index.ts index 9d464f3f..6e3d0d08 100644 --- a/packages/datastore-s3/src/index.ts +++ b/packages/datastore-s3/src/index.ts @@ -40,13 +40,14 @@ import { ListObjectsV2Command } from '@aws-sdk/client-s3' import { BaseDatastore } from 'datastore-core/base' -import * as Errors from 'datastore-core/errors' -import { Key, type KeyQuery, type Pair, type Query } from 'interface-datastore' +import { Key } from 'interface-datastore' +import { DeleteFailedError, GetFailedError, HasFailedError, NotFoundError, OpenFailedError, PutFailedError } from 'interface-store' import filter from 'it-filter' import toBuffer from 'it-to-buffer' -import { fromString as unint8arrayFromString } from 'uint8arrays' +import { fromString as uint8arrayFromString } from 'uint8arrays' import type { S3 } from '@aws-sdk/client-s3' -import type { AbortOptions } from 'interface-store' +import type { KeyQuery, Pair, Query } from 'interface-datastore' +import type { AbortOptions, AwaitGenerator } from 'interface-store' export interface S3DatastoreInit { /** @@ -105,6 +106,7 @@ export class S3Datastore extends BaseDatastore { */ async put (key: Key, val: Uint8Array, options?: AbortOptions): Promise { try { + options?.signal?.throwIfAborted() await this.s3.send( new PutObjectCommand({ Bucket: this.bucket, @@ -117,7 +119,7 @@ export class S3Datastore extends BaseDatastore { return key } catch (err: any) { - throw Errors.dbWriteFailedError(err) + throw new PutFailedError(String(err)) } } @@ -126,6 +128,7 @@ export class S3Datastore extends BaseDatastore { */ async get (key: Key, options?: AbortOptions): Promise { try { + options?.signal?.throwIfAborted() const data = await this.s3.send( new GetObjectCommand({ Bucket: this.bucket, @@ -146,7 +149,7 @@ export class S3Datastore extends BaseDatastore { } if (typeof data.Body === 'string') { - return unint8arrayFromString(data.Body) + return uint8arrayFromString(data.Body) } if (data.Body instanceof Blob) { @@ -155,13 +158,13 @@ export class S3Datastore extends BaseDatastore { return new Uint8Array(buf, 0, buf.byteLength) } - // @ts-expect-error s3 types define their own Blob as an empty interface return await toBuffer(data.Body) } catch (err: any) { if (err.statusCode === 404) { - throw Errors.notFoundError(err) + throw new NotFoundError(String(err)) } - throw err + + throw new GetFailedError(String(err)) } } @@ -170,6 +173,7 @@ export class S3Datastore extends BaseDatastore { */ async has (key: Key, options?: AbortOptions): Promise { try { + options?.signal?.throwIfAborted() await this.s3.send( new HeadObjectCommand({ Bucket: this.bucket, @@ -191,7 +195,7 @@ export class S3Datastore extends BaseDatastore { return false } - throw err + throw new HasFailedError(String(err)) } } @@ -200,6 +204,7 @@ export class S3Datastore extends BaseDatastore { */ async delete (key: Key, options?: AbortOptions): Promise { try { + options?.signal?.throwIfAborted() await this.s3.send( new DeleteObjectCommand({ Bucket: this.bucket, @@ -209,7 +214,7 @@ export class S3Datastore extends BaseDatastore { } ) } catch (err: any) { - throw Errors.dbDeleteFailedError(err) + throw new DeleteFailedError(String(err)) } } @@ -218,6 +223,7 @@ export class S3Datastore extends BaseDatastore { */ async * _listKeys (params: { Prefix?: string, StartAfter?: string }, options?: AbortOptions): AsyncIterable { try { + options?.signal?.throwIfAborted() const data = await this.s3.send( new ListObjectsV2Command({ Bucket: this.bucket, @@ -231,7 +237,7 @@ export class S3Datastore extends BaseDatastore { return } - if (data == null || data.Contents == null) { + if (data?.Contents == null) { throw new Error('Not found') } @@ -253,11 +259,11 @@ export class S3Datastore extends BaseDatastore { yield * this._listKeys(params) } } catch (err: any) { - throw new Error(err.code) + throw new GetFailedError(String(err)) } } - async * _all (q: Query, options?: AbortOptions): AsyncIterable { + async * _all (q: Query, options?: AbortOptions): AwaitGenerator { for await (const key of this._allKeys({ prefix: q.prefix }, options)) { try { const res: Pair = { @@ -268,14 +274,14 @@ export class S3Datastore extends BaseDatastore { yield res } catch (err: any) { // key was deleted while we are iterating over the results - if (err.statusCode !== 404) { + if (err.name !== 'NotFoundError') { throw err } } } } - async * _allKeys (q: KeyQuery, options?: AbortOptions): AsyncIterable { + async * _allKeys (q: KeyQuery, options?: AbortOptions): AwaitGenerator { const prefix = [this.path, q.prefix ?? ''].filter(Boolean).join('/').replace(/\/\/+/g, '/') // Get all the keys via list object, recursively as needed @@ -316,7 +322,7 @@ export class S3Datastore extends BaseDatastore { return } - throw Errors.dbOpenFailedError(err) + throw new OpenFailedError(String(err)) } } } diff --git a/packages/datastore-s3/test/index.spec.ts b/packages/datastore-s3/test/index.spec.ts index 1fbd5d4e..ded077d0 100644 --- a/packages/datastore-s3/test/index.spec.ts +++ b/packages/datastore-s3/test/index.spec.ts @@ -1,6 +1,7 @@ /* eslint-env mocha */ -import { type CreateBucketCommand, type PutObjectCommand, type HeadObjectCommand, S3, type GetObjectCommand } from '@aws-sdk/client-s3' +import './utils/domparser-polyfill.js' +import { S3 } from '@aws-sdk/client-s3' import { expect } from 'aegir/chai' import { Key } from 'interface-datastore' import { interfaceDatastoreTests } from 'interface-datastore-tests' @@ -8,6 +9,7 @@ import defer from 'p-defer' import sinon from 'sinon' import { S3Datastore } from '../src/index.js' import { s3Resolve, s3Reject, S3Error, s3Mock } from './utils/s3-mock.js' +import type { CreateBucketCommand, PutObjectCommand, HeadObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3' describe('S3Datastore', () => { describe('construction', () => { @@ -72,7 +74,7 @@ describe('S3Datastore', () => { }) await expect(store.put(new Key('/z/key'), new TextEncoder().encode('test data'))).to.eventually.rejected - .with.property('code', 'ERR_DB_WRITE_FAILED') + .with.property('name', 'PutFailedError') }) }) @@ -116,7 +118,7 @@ describe('S3Datastore', () => { }) await expect(store.get(new Key('/z/key'))).to.eventually.rejected - .with.property('code', 'ERR_NOT_FOUND') + .with.property('name', 'NotFoundError') }) }) @@ -134,7 +136,7 @@ describe('S3Datastore', () => { }) await expect(store.delete(new Key('/z/key'))).to.eventually.rejected - .with.property('code', 'ERR_DB_DELETE_FAILED') + .with.property('name', 'DeleteFailedError') }) }) @@ -189,7 +191,7 @@ describe('S3Datastore', () => { }) await expect(store.open()).to.eventually.rejected - .with.property('code', 'ERR_DB_OPEN_FAILED') + .with.property('name', 'OpenFailedError') const headObjectCommand = await bucketTested.promise expect(headObjectCommand).to.have.nested.property('input.Bucket', 'test') @@ -208,7 +210,7 @@ describe('S3Datastore', () => { }) await expect(store.open()).to.eventually.rejected - .with.property('code', 'ERR_DB_OPEN_FAILED') + .with.property('name', 'OpenFailedError') }) }) diff --git a/packages/datastore-s3/test/utils/domparser-polyfill.ts b/packages/datastore-s3/test/utils/domparser-polyfill.ts new file mode 100644 index 00000000..5225bed6 --- /dev/null +++ b/packages/datastore-s3/test/utils/domparser-polyfill.ts @@ -0,0 +1,3 @@ +import { DOMParser } from 'xmldom' + +globalThis.DOMParser = globalThis.DOMParser ?? DOMParser diff --git a/packages/datastore-s3/test/utils/s3-mock.ts b/packages/datastore-s3/test/utils/s3-mock.ts index f49ea81a..6b11726f 100644 --- a/packages/datastore-s3/test/utils/s3-mock.ts +++ b/packages/datastore-s3/test/utils/s3-mock.ts @@ -17,11 +17,11 @@ export class S3Error extends Error { } } -export const s3Resolve = (res?: any): any => { +export function s3Resolve (res?: any): any { return Promise.resolve(res) } -export const s3Reject = (err: T): any => { +export function s3Reject (err: Error): any { return Promise.reject(err) } diff --git a/packages/datastore-s3/typedoc.json b/packages/datastore-s3/typedoc.json index f599dc72..db0b0747 100644 --- a/packages/datastore-s3/typedoc.json +++ b/packages/datastore-s3/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts" ] diff --git a/packages/interface-blockstore-tests/CHANGELOG.md b/packages/interface-blockstore-tests/CHANGELOG.md index 578f3b06..c3917b10 100644 --- a/packages/interface-blockstore-tests/CHANGELOG.md +++ b/packages/interface-blockstore-tests/CHANGELOG.md @@ -1,3 +1,64 @@ +## [interface-blockstore-tests-v8.0.2](https://github.com/ipfs/js-stores/compare/interface-blockstore-tests-8.0.1...interface-blockstore-tests-8.0.2) (2025-10-03) + +### Dependencies + +* bump level from 8.0.1 to 10.0.0 ([#356](https://github.com/ipfs/js-stores/issues/356)) ([c0ec61f](https://github.com/ipfs/js-stores/commit/c0ec61fe965e3bad9d607a0bd3a3c750f00f41d0)) + +## [interface-blockstore-tests-v8.0.1](https://github.com/ipfs/js-stores/compare/interface-blockstore-tests-8.0.0...interface-blockstore-tests-8.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [interface-blockstore-tests-v8.0.0](https://github.com/ipfs/js-stores/compare/interface-blockstore-tests-7.0.3...interface-blockstore-tests-8.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +## [interface-blockstore-tests-v7.0.3](https://github.com/ipfs/js-stores/compare/interface-blockstore-tests-7.0.2...interface-blockstore-tests-7.0.3) (2025-05-28) + +### Bug Fixes + +* improve abort signal support ([#350](https://github.com/ipfs/js-stores/issues/350)) ([e17d770](https://github.com/ipfs/js-stores/commit/e17d770cc2fcee77cb0152a855abf162e5a91a99)) + +## [interface-blockstore-tests-v7.0.2](https://github.com/ipfs/js-stores/compare/interface-blockstore-tests-7.0.1...interface-blockstore-tests-7.0.2) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [interface-blockstore-tests-v7.0.1](https://github.com/ipfs/js-stores/compare/interface-blockstore-tests-7.0.0...interface-blockstore-tests-7.0.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## interface-blockstore-tests [7.0.0](https://github.com/ipfs/js-stores/compare/interface-blockstore-tests-6.1.10...interface-blockstore-tests-7.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + + + +### Dependencies + +* **interface-blockstore:** upgraded to 5.3.0 + ## interface-blockstore-tests [6.1.10](https://github.com/ipfs/js-stores/compare/interface-blockstore-tests-v6.1.9...interface-blockstore-tests-6.1.10) (2024-02-12) diff --git a/packages/interface-blockstore-tests/CODE_OF_CONDUCT.md b/packages/interface-blockstore-tests/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/interface-blockstore-tests/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/interface-blockstore-tests/LICENSE b/packages/interface-blockstore-tests/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/interface-blockstore-tests/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/interface-blockstore-tests/LICENSE-APACHE b/packages/interface-blockstore-tests/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/interface-blockstore-tests/LICENSE-APACHE +++ b/packages/interface-blockstore-tests/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/interface-blockstore-tests/README.md b/packages/interface-blockstore-tests/README.md index a8bfbdb8..b636423b 100644 --- a/packages/interface-blockstore-tests/README.md +++ b/packages/interface-blockstore-tests/README.md @@ -9,6 +9,21 @@ # About + + A test suite that ensures a given implementation implements the Blockstore interface properly. ## Example @@ -43,8 +58,8 @@ $ npm i interface-blockstore-tests Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/interface-blockstore-tests/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/interface-blockstore-tests/LICENSE-MIT) / ) # Contribute diff --git a/packages/interface-blockstore-tests/package.json b/packages/interface-blockstore-tests/package.json index 16b241c2..64a021cf 100644 --- a/packages/interface-blockstore-tests/package.json +++ b/packages/interface-blockstore-tests/package.json @@ -1,6 +1,6 @@ { "name": "interface-blockstore-tests", - "version": "6.1.10", + "version": "8.0.2", "description": "Compliance tests for the blockstore interface", "license": "Apache-2.0 OR MIT", "homepage": "https://github.com/ipfs/js-stores/tree/main/packages/interface-blockstore-tests#readme", @@ -35,12 +35,98 @@ "import": "./dist/src/index.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true, - "sourceType": "module" - } + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "build": "aegir build", @@ -50,13 +136,14 @@ "release": "aegir release" }, "dependencies": { - "interface-blockstore": "^5.0.0", - "it-all": "^3.0.4", - "it-drain": "^3.0.5", - "multiformats": "^13.0.1", - "uint8arrays": "^5.0.2" + "interface-blockstore": "^6.0.0", + "it-all": "^3.0.8", + "it-drain": "^3.0.9", + "it-map": "^3.1.4", + "multiformats": "^13.3.6", + "uint8arrays": "^5.1.0" }, "devDependencies": { - "aegir": "^42.2.3" + "aegir": "^47.0.16" } } diff --git a/packages/interface-blockstore-tests/src/index.ts b/packages/interface-blockstore-tests/src/index.ts index 69a77146..c6afeb44 100644 --- a/packages/interface-blockstore-tests/src/index.ts +++ b/packages/interface-blockstore-tests/src/index.ts @@ -27,14 +27,24 @@ import { expect } from 'aegir/chai' import all from 'it-all' import drain from 'it-drain' +import map from 'it-map' import { base32 } from 'multiformats/bases/base32' import { CID } from 'multiformats/cid' import * as raw from 'multiformats/codecs/raw' import { sha256 } from 'multiformats/hashes/sha2' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' -import type { Blockstore, Pair } from 'interface-blockstore' +import type { Blockstore } from 'interface-blockstore' -async function getKeyValuePair (data?: string): Promise { +interface Data { + cid: CID + block: Uint8Array +} + +async function * toGenerator (buf: T): AsyncGenerator { + yield buf +} + +async function getKeyValuePair (data?: string): Promise { const block = uint8ArrayFromString(data ?? `data-${Math.random()}`) const multihash = await sha256.digest(block) const cid = CID.createV1(raw.code, multihash) @@ -42,7 +52,7 @@ async function getKeyValuePair (data?: string): Promise { return { cid, block } } -async function getKeyValuePairs (count: number): Promise { +async function getKeyValuePairs (count: number): Promise { return Promise.all( new Array(count).fill(0).map(async (_, i) => getKeyValuePair()) ) @@ -82,15 +92,36 @@ export function interfaceBlockstoreTests (te it('simple', async () => { const { cid, block } = await getKeyValuePair() - await store.put(cid, block) + await store.put(cid, toGenerator(block)) + }) + + it('supports abort signals', async () => { + const { cid, block } = await getKeyValuePair() + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + return store.put(cid, toGenerator(block), { + signal: controller.signal + }) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') }) it('parallel', async () => { const data = await getKeyValuePairs(100) - await Promise.all(data.map(async d => { await store.put(d.cid, d.block) })) + await Promise.all(data.map(async d => { + await store.put(d.cid, toGenerator(d.block)) + })) + + const res = await all(map(store.getMany(data.map(d => d.cid)), async ({ cid, bytes }) => ({ + cid, + block: (await all(bytes))[0] + }))) - const res = await all(store.getMany(data.map(d => d.cid))) expect(res).to.deep.equal(data) }) }) @@ -111,16 +142,35 @@ export function interfaceBlockstoreTests (te let index = 0 - for await (const cid of store.putMany(data)) { + for await (const cid of store.putMany(data.map(({ cid, block }) => ({ + cid, + bytes: toGenerator(block) + })))) { expect(data[index].cid).to.deep.equal(cid) index++ } expect(index).to.equal(data.length) - const res = await all(store.getMany(data.map(d => d.cid))) + const res = await all(map(store.getMany(data.map(d => d.cid)), async ({ cid, bytes }) => ({ + cid, + block: (await all(bytes))[0] + }))) expect(res).to.deep.equal(data) }) + + it('supports abort signals', async () => { + const { cid, block } = await getKeyValuePair() + + const controller = new AbortController() + controller.abort() + + await expect(all(store.putMany([{ cid, bytes: toGenerator(block) }], { + signal: controller.signal + }))).to.eventually.be.rejected + .with.property('message') + .that.include('abort') + }) }) describe('get', () => { @@ -139,10 +189,25 @@ export function interfaceBlockstoreTests (te cid, block } = await getKeyValuePair() - await store.put(cid, block) + await store.put(cid, toGenerator(block)) + + const res = await all(store.get(cid)) + expect(res).to.deep.equal([block]) + }) + + it('supports abort signals', async () => { + const { cid } = await getKeyValuePair() - const res = await store.get(cid) - expect(res).to.equalBytes(block) + const controller = new AbortController() + controller.abort() + + await expect(drain((async function * () { + yield * store.get(cid, { + signal: controller.signal + }) + })())).to.eventually.be.rejected + .with.property('message') + .that.include('abort') }) it('should throw error for missing key', async () => { @@ -151,9 +216,9 @@ export function interfaceBlockstoreTests (te } = await getKeyValuePair() try { - await store.get(cid) + await all(store.get(cid)) } catch (err) { - expect(err).to.have.property('code', 'ERR_NOT_FOUND') + expect(err).to.have.property('name', 'NotFoundError') return } @@ -177,13 +242,31 @@ export function interfaceBlockstoreTests (te cid, block } = await getKeyValuePair() - await store.put(cid, block) + await store.put(cid, toGenerator(block)) const source = [cid] const res = await all(store.getMany(source)) expect(res).to.have.lengthOf(1) expect(res[0].cid).to.deep.equal(cid) - expect(res[0].block).to.equalBytes(block) + expect(await all(res[0].bytes)).to.deep.equal([block]) + }) + + it('supports abort signals', async () => { + const { cid, block } = await getKeyValuePair() + + await store.put(cid, toGenerator(block)) + + const controller = new AbortController() + controller.abort() + + await expect(drain(map(store.getMany([cid], { + signal: controller.signal + }), async ({ cid, bytes }) => ({ + cid, + bytes: await all(bytes) + })))).to.eventually.be.rejected + .with.property('message') + .that.include('abort') }) it('should throw error for missing key', async () => { @@ -191,14 +274,11 @@ export function interfaceBlockstoreTests (te cid } = await getKeyValuePair() - try { - await drain(store.getMany([cid])) - } catch (err) { - expect(err).to.have.property('code', 'ERR_NOT_FOUND') - return - } - - throw new Error('expected error to be thrown') + await expect(drain(map(store.getMany([cid]), async ({ cid, bytes }) => ({ + cid, + bytes: await all(bytes) + })))).to.eventually.be.rejected + .with.property('name', 'NotFoundError') }) }) @@ -216,7 +296,10 @@ export function interfaceBlockstoreTests (te it('returns all blocks', async () => { const data = await getKeyValuePairs(100) - await drain(store.putMany(data)) + await drain(store.putMany(data.map(({ cid, block }) => ({ + cid, + bytes: toGenerator(block) + })))) const allBlocks = await all(store.getAll()) @@ -234,9 +317,26 @@ export function interfaceBlockstoreTests (te throw new Error('Could not find cid/block pair') } - expect(retrievedPair.block).to.equalBytes(block) + await expect(all(retrievedPair.bytes)).to.eventually.deep.equal([block]) } }) + + it('supports abort signals', async () => { + const { cid, block } = await getKeyValuePair() + + await store.put(cid, toGenerator(block)) + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + return all(store.getAll({ + signal: controller.signal + })) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') + }) }) describe('delete', () => { @@ -255,11 +355,28 @@ export function interfaceBlockstoreTests (te cid, block } = await getKeyValuePair() - await store.put(cid, block) - await store.get(cid) + await store.put(cid, toGenerator(block)) + await drain(store.get(cid)) await store.delete(cid) const exists = await store.has(cid) - expect(exists).to.be.eql(false) + expect(exists).to.be.false() + }) + + it('supports abort signals', async () => { + const { cid, block } = await getKeyValuePair() + + await store.put(cid, toGenerator(block)) + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + return store.delete(cid, { + signal: controller.signal + }) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') }) it('parallel', async () => { @@ -268,12 +385,12 @@ export function interfaceBlockstoreTests (te await Promise.all(data.map(async d => { await store.put(d.cid, d.block) })) const res0 = await Promise.all(data.map(async d => store.has(d.cid))) - res0.forEach(res => expect(res).to.be.eql(true)) + res0.forEach(res => expect(res).to.be.true()) await Promise.all(data.map(async d => { await store.delete(d.cid) })) const res1 = await Promise.all(data.map(async d => store.has(d.cid))) - res1.forEach(res => expect(res).to.be.eql(false)) + res1.forEach(res => expect(res).to.be.false()) }) }) @@ -291,10 +408,13 @@ export function interfaceBlockstoreTests (te it('streaming', async () => { const data = await getKeyValuePairs(100) - await drain(store.putMany(data)) + await drain(store.putMany(data.map(({ cid, block }) => ({ + cid, + bytes: toGenerator(block) + })))) const res0 = await Promise.all(data.map(async d => store.has(d.cid))) - res0.forEach(res => expect(res).to.be.eql(true)) + res0.forEach(res => expect(res).to.be.true()) let index = 0 @@ -306,7 +426,24 @@ export function interfaceBlockstoreTests (te expect(index).to.equal(data.length) const res1 = await Promise.all(data.map(async d => store.has(d.cid))) - res1.forEach(res => expect(res).to.be.eql(false)) + res1.forEach(res => expect(res).to.be.false()) + }) + + it('supports abort signals', async () => { + const { cid, block } = await getKeyValuePair() + + await store.put(cid, block) + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + return all(store.deleteMany([cid], { + signal: controller.signal + })) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') }) }) } diff --git a/packages/interface-blockstore-tests/typedoc.json b/packages/interface-blockstore-tests/typedoc.json index f599dc72..db0b0747 100644 --- a/packages/interface-blockstore-tests/typedoc.json +++ b/packages/interface-blockstore-tests/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts" ] diff --git a/packages/interface-blockstore/CHANGELOG.md b/packages/interface-blockstore/CHANGELOG.md index 4ca894b6..96b909c6 100644 --- a/packages/interface-blockstore/CHANGELOG.md +++ b/packages/interface-blockstore/CHANGELOG.md @@ -1,3 +1,43 @@ +## [interface-blockstore-v6.0.1](https://github.com/ipfs/js-stores/compare/interface-blockstore-6.0.0...interface-blockstore-6.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [interface-blockstore-v6.0.0](https://github.com/ipfs/js-stores/compare/interface-blockstore-5.3.2...interface-blockstore-6.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +## [interface-blockstore-v5.3.2](https://github.com/ipfs/js-stores/compare/interface-blockstore-5.3.1...interface-blockstore-5.3.2) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [interface-blockstore-v5.3.1](https://github.com/ipfs/js-stores/compare/interface-blockstore-5.3.0...interface-blockstore-5.3.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## interface-blockstore [5.3.0](https://github.com/ipfs/js-stores/compare/interface-blockstore-5.2.10...interface-blockstore-5.3.0) (2024-08-02) + + + +### Dependencies + +* **interface-store:** upgraded to 6.0.0 + ## interface-blockstore [5.2.10](https://github.com/ipfs/js-stores/compare/interface-blockstore-v5.2.9...interface-blockstore-5.2.10) (2024-02-12) diff --git a/packages/interface-blockstore/CODE_OF_CONDUCT.md b/packages/interface-blockstore/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/interface-blockstore/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/interface-blockstore/LICENSE b/packages/interface-blockstore/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/interface-blockstore/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/interface-blockstore/LICENSE-APACHE b/packages/interface-blockstore/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/interface-blockstore/LICENSE-APACHE +++ b/packages/interface-blockstore/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/interface-blockstore/README.md b/packages/interface-blockstore/README.md index b8980909..cc0428ec 100644 --- a/packages/interface-blockstore/README.md +++ b/packages/interface-blockstore/README.md @@ -9,6 +9,21 @@ # About + + A Blockstore is a key/value database that lets use CIDs to store/retrieve binary blobs. It is used by IPFS to store/retrieve the block that a given CID resolves to. @@ -35,8 +50,8 @@ $ npm i interface-blockstore Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/interface-blockstore/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/interface-blockstore/LICENSE-MIT) / ) # Contribute diff --git a/packages/interface-blockstore/package.json b/packages/interface-blockstore/package.json index aa8da379..962c40aa 100644 --- a/packages/interface-blockstore/package.json +++ b/packages/interface-blockstore/package.json @@ -1,6 +1,6 @@ { "name": "interface-blockstore", - "version": "5.2.10", + "version": "6.0.1", "description": "An interface for storing and retrieving blocks", "license": "Apache-2.0 OR MIT", "homepage": "https://github.com/ipfs/js-stores/tree/main/packages/interface-blockstore#readme", @@ -29,12 +29,98 @@ "import": "./dist/src/index.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true, - "sourceType": "module" - } + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "build": "aegir build", @@ -43,10 +129,10 @@ "release": "aegir release" }, "dependencies": { - "interface-store": "^5.0.0", - "multiformats": "^13.0.1" + "interface-store": "^7.0.0", + "multiformats": "^13.3.6" }, "devDependencies": { - "aegir": "^42.2.3" + "aegir": "^47.0.16" } } diff --git a/packages/interface-blockstore/src/index.ts b/packages/interface-blockstore/src/index.ts index 17fbdc96..12d6978d 100644 --- a/packages/interface-blockstore/src/index.ts +++ b/packages/interface-blockstore/src/index.ts @@ -1,9 +1,3 @@ -/* eslint-disable @typescript-eslint/ban-types */ -// this ignore is so we can use {} as the default value for the options -// extensions below - it normally means "any non-nullish value" but here -// we are using it as an intersection type - see the aside at the bottom: -// https://github.com/typescript-eslint/typescript-eslint/issues/2063#issuecomment-675156492 - /** * @packageDocumentation * @@ -22,6 +16,7 @@ import type { AbortOptions, + AwaitGenerator, AwaitIterable, Store } from 'interface-store' @@ -29,13 +24,18 @@ import type { CID } from 'multiformats/cid' export interface Pair { cid: CID - block: Uint8Array + bytes: AwaitGenerator +} + +export interface InputPair { + cid: CID + bytes: Uint8Array | AwaitIterable } export interface Blockstore extends Store extends Store, AwaitGenerator, InputPair, Pair, HasOptionsExtension, PutOptionsExtension, PutManyOptionsExtension, GetOptionsExtension, GetManyOptionsExtension, DeleteOptionsExtension, DeleteManyOptionsExtension> { @@ -44,11 +44,11 @@ DeleteOptionsExtension = {}, DeleteManyOptionsExtension = {}> extends Store got MultihashDigest('Qmfoo') Uint8Array[...] + * for await (const { multihash, bytes } of store.getAll()) { + * console.log('got:', multihash, bytes) + * // => got MultihashDigest('Qmfoo') [Uint8Array[...]...] * } * ``` */ - getAll(options?: AbortOptions & GetAllOptionsExtension): AwaitIterable + getAll(options?: AbortOptions & GetAllOptionsExtension): AwaitGenerator } diff --git a/packages/interface-blockstore/typedoc.json b/packages/interface-blockstore/typedoc.json index f599dc72..db0b0747 100644 --- a/packages/interface-blockstore/typedoc.json +++ b/packages/interface-blockstore/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts" ] diff --git a/packages/interface-datastore-tests/CHANGELOG.md b/packages/interface-datastore-tests/CHANGELOG.md index 42c86547..a7c1b062 100644 --- a/packages/interface-datastore-tests/CHANGELOG.md +++ b/packages/interface-datastore-tests/CHANGELOG.md @@ -1,3 +1,54 @@ +## [interface-datastore-tests-v6.0.5](https://github.com/ipfs/js-stores/compare/interface-datastore-tests-6.0.4...interface-datastore-tests-6.0.5) (2025-10-03) + +### Dependencies + +* bump level from 8.0.1 to 10.0.0 ([#356](https://github.com/ipfs/js-stores/issues/356)) ([c0ec61f](https://github.com/ipfs/js-stores/commit/c0ec61fe965e3bad9d607a0bd3a3c750f00f41d0)) + +## [interface-datastore-tests-v6.0.4](https://github.com/ipfs/js-stores/compare/interface-datastore-tests-6.0.3...interface-datastore-tests-6.0.4) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [interface-datastore-tests-v6.0.3](https://github.com/ipfs/js-stores/compare/interface-datastore-tests-6.0.2...interface-datastore-tests-6.0.3) (2025-05-28) + +### Bug Fixes + +* improve abort signal support ([#350](https://github.com/ipfs/js-stores/issues/350)) ([e17d770](https://github.com/ipfs/js-stores/commit/e17d770cc2fcee77cb0152a855abf162e5a91a99)) + +## [interface-datastore-tests-v6.0.2](https://github.com/ipfs/js-stores/compare/interface-datastore-tests-6.0.1...interface-datastore-tests-6.0.2) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [interface-datastore-tests-v6.0.1](https://github.com/ipfs/js-stores/compare/interface-datastore-tests-6.0.0...interface-datastore-tests-6.0.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## interface-datastore-tests [6.0.0](https://github.com/ipfs/js-stores/compare/interface-datastore-tests-5.1.8...interface-datastore-tests-6.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + + + +### Dependencies + +* **interface-datastore:** upgraded to 8.3.0 + ## interface-datastore-tests [5.1.8](https://github.com/ipfs/js-stores/compare/interface-datastore-tests-v5.1.7...interface-datastore-tests-5.1.8) (2024-02-12) diff --git a/packages/interface-datastore-tests/CODE_OF_CONDUCT.md b/packages/interface-datastore-tests/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/interface-datastore-tests/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/interface-datastore-tests/LICENSE b/packages/interface-datastore-tests/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/interface-datastore-tests/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/interface-datastore-tests/LICENSE-APACHE b/packages/interface-datastore-tests/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/interface-datastore-tests/LICENSE-APACHE +++ b/packages/interface-datastore-tests/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/interface-datastore-tests/README.md b/packages/interface-datastore-tests/README.md index 649f63fb..6a82a0dc 100644 --- a/packages/interface-datastore-tests/README.md +++ b/packages/interface-datastore-tests/README.md @@ -9,6 +9,21 @@ # About + + A test suite that ensures a given implementation implements the Datastore interface properly. ## Example @@ -43,8 +58,8 @@ $ npm i interface-datastore-tests Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/interface-datastore-tests/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/interface-datastore-tests/LICENSE-MIT) / ) # Contribute diff --git a/packages/interface-datastore-tests/package.json b/packages/interface-datastore-tests/package.json index 8fce8d9b..a05d458e 100644 --- a/packages/interface-datastore-tests/package.json +++ b/packages/interface-datastore-tests/package.json @@ -1,6 +1,6 @@ { "name": "interface-datastore-tests", - "version": "5.1.8", + "version": "6.0.5", "description": "Compliance tests for the datastore interface", "license": "Apache-2.0 OR MIT", "homepage": "https://github.com/ipfs/js-stores/tree/main/packages/interface-datastore-tests#readme", @@ -35,12 +35,98 @@ "import": "./dist/src/index.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true, - "sourceType": "module" - } + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "build": "aegir build", @@ -50,14 +136,14 @@ "release": "aegir release" }, "dependencies": { - "interface-datastore": "^8.0.0", + "interface-datastore": "^9.0.0", "iso-random-stream": "^2.0.2", - "it-all": "^3.0.4", - "it-drain": "^3.0.5", - "it-length": "^3.0.4", - "uint8arrays": "^5.0.2" + "it-all": "^3.0.8", + "it-drain": "^3.0.9", + "it-length": "^3.0.8", + "uint8arrays": "^5.1.0" }, "devDependencies": { - "aegir": "^42.2.3" + "aegir": "^47.0.16" } } diff --git a/packages/interface-datastore-tests/src/index.ts b/packages/interface-datastore-tests/src/index.ts index 8076c214..65c7c38b 100644 --- a/packages/interface-datastore-tests/src/index.ts +++ b/packages/interface-datastore-tests/src/index.ts @@ -25,12 +25,13 @@ */ import { expect } from 'aegir/chai' -import { type Datastore, Key, type KeyQueryFilter, type KeyQueryOrder, type Pair, type QueryFilter, type QueryOrder } from 'interface-datastore' +import { Key } from 'interface-datastore' import { randomBytes } from 'iso-random-stream' import all from 'it-all' import drain from 'it-drain' import length from 'it-length' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' +import type { Datastore, KeyQueryFilter, KeyQueryOrder, Pair, QueryFilter, QueryOrder } from 'interface-datastore' export interface InterfacDatastoreTest { setup(): D | Promise @@ -60,6 +61,22 @@ export function interfaceDatastoreTests (test: await store.put(k, uint8ArrayFromString('one')) }) + it('supports abort signals', async () => { + const key = new Key('/z/one') + const value = uint8ArrayFromString('hello') + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + return store.put(key, value, { + signal: controller.signal + }) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') + }) + it('parallel', async () => { const data: Pair[] = [] for (let i = 0; i < 100; i++) { @@ -100,6 +117,22 @@ export function interfaceDatastoreTests (test: const res = await all(store.getMany(data.map(d => d.key))) expect(res).to.deep.equal(data) }) + + it('supports abort signals', async () => { + const key = new Key('/z/one') + const value = uint8ArrayFromString('hello') + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + return all(store.putMany([{ key, value }], { + signal: controller.signal + })) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') + }) }) describe('get', () => { @@ -118,13 +151,30 @@ export function interfaceDatastoreTests (test: expect(res).to.be.eql(uint8ArrayFromString('hello')) }) + it('supports abort signals', async () => { + const key = new Key('/z/one') + const value = uint8ArrayFromString('hello') + await store.put(key, value) + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + return store.get(key, { + signal: controller.signal + }) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') + }) + it('should throw error for missing key', async () => { const k = new Key('/does/not/exist') try { await store.get(k) } catch (err) { - expect(err).to.have.property('code', 'ERR_NOT_FOUND') + expect(err).to.have.property('name', 'NotFoundError') return } @@ -152,13 +202,30 @@ export function interfaceDatastoreTests (test: expect(res[0].value).to.be.eql(uint8ArrayFromString('hello')) }) + it('supports abort signals', async () => { + const key = new Key('/z/one') + const value = uint8ArrayFromString('hello') + await store.put(key, value) + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + return all(store.getMany([key], { + signal: controller.signal + })) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') + }) + it('should throw error for missing key', async () => { const k = new Key('/does/not/exist') try { await drain(store.getMany([k])) } catch (err) { - expect(err).to.have.property('code', 'ERR_NOT_FOUND') + expect(err).to.have.property('name', 'NotFoundError') return } @@ -181,7 +248,24 @@ export function interfaceDatastoreTests (test: await store.get(k) await store.delete(k) const exists = await store.has(k) - expect(exists).to.be.eql(false) + expect(exists).to.be.false() + }) + + it('supports abort signals', async () => { + const key = new Key('/z/one') + const value = uint8ArrayFromString('hello') + await store.put(key, value) + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + return store.delete(key, { + signal: controller.signal + }) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') }) it('parallel', async () => { @@ -193,12 +277,12 @@ export function interfaceDatastoreTests (test: await Promise.all(data.map(async d => { await store.put(d[0], d[1]) })) const res0 = await Promise.all(data.map(async d => store.has(d[0]))) - res0.forEach(res => expect(res).to.be.eql(true)) + res0.forEach(res => expect(res).to.be.true()) await Promise.all(data.map(async d => { await store.delete(d[0]) })) const res1 = await Promise.all(data.map(async d => store.has(d[0]))) - res1.forEach(res => expect(res).to.be.eql(false)) + res1.forEach(res => expect(res).to.be.false()) }) }) @@ -220,7 +304,7 @@ export function interfaceDatastoreTests (test: await drain(store.putMany(data)) const res0 = await Promise.all(data.map(async d => store.has(d.key))) - res0.forEach(res => expect(res).to.be.eql(true)) + res0.forEach(res => expect(res).to.be.true()) let index = 0 @@ -232,7 +316,24 @@ export function interfaceDatastoreTests (test: expect(index).to.equal(data.length) const res1 = await Promise.all(data.map(async d => store.has(d.key))) - res1.forEach(res => expect(res).to.be.eql(false)) + res1.forEach(res => expect(res).to.be.false()) + }) + + it('supports abort signals', async () => { + const key = new Key('/z/one') + const value = uint8ArrayFromString('hello') + await store.put(key, value) + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + return all(store.deleteMany([key], { + signal: controller.signal + })) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') }) }) @@ -278,6 +379,31 @@ export function interfaceDatastoreTests (test: expect(await length(store.query({ prefix: '/z' }))).to.equal(count) expect(await length(store.query({ prefix: '/q' }))).to.equal(count) }) + + it('supports abort signals', async () => { + const key = new Key('/z/one') + const value = uint8ArrayFromString('hello') + await store.put(key, value) + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + const b = store.batch() + const count = 400 + for (let i = 0; i < count; i++) { + b.put(new Key(`/a/hello${i}`), randomBytes(32)) + b.put(new Key(`/q/hello${i}`), randomBytes(64)) + b.put(new Key(`/z/hello${i}`), randomBytes(128)) + } + + await b.commit({ + signal: controller.signal + }) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') + }) }) describe('query', () => { @@ -348,7 +474,7 @@ export function interfaceDatastoreTests (test: const exp = expected.sort(s) res.forEach((r, i) => { - expect(r.key.toString()).to.be.eql(exp[i].key.toString()) + expect(r.key.toString()).to.equal(exp[i].key.toString()) if (r.value == null) { expect(exp[i].value).to.not.exist() @@ -395,6 +521,23 @@ export function interfaceDatastoreTests (test: expect(results.length).to.be.greaterThan(0) await writePromise }) + + it('supports abort signals', async () => { + await store.put(new Key('/a/hello1'), randomBytes(32)) + await store.put(new Key('/q/hello2'), randomBytes(64)) + await store.put(new Key('/z/hello3'), randomBytes(128)) + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + await all(store.query({}, { + signal: controller.signal + })) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') + }) }) describe('queryKeys', () => { @@ -466,7 +609,7 @@ export function interfaceDatastoreTests (test: const exp = expected.sort(s) res.forEach((r, i) => { - expect(r.toString()).to.be.eql(exp[i].toString()) + expect(r.toString()).to.equal(exp[i].toString()) }) } else { expect(res).to.be.eql(expected) @@ -476,7 +619,7 @@ export function interfaceDatastoreTests (test: } })) - it('allows mutating the datastore during a query', async () => { + it('allows mutating the datastore during a key query', async () => { const hello3 = { key: new Key('/z/4hello3'), value: uint8ArrayFromString('4') } let firstIteration = true @@ -507,5 +650,22 @@ export function interfaceDatastoreTests (test: expect(results.length).to.be.greaterThan(0) await writePromise }) + + it('supports abort signals', async () => { + await store.put(new Key('/a/hello1'), randomBytes(32)) + await store.put(new Key('/q/hello2'), randomBytes(64)) + await store.put(new Key('/z/hello3'), randomBytes(128)) + + const controller = new AbortController() + controller.abort() + + await expect((async () => { + await all(store.queryKeys({}, { + signal: controller.signal + })) + })()).to.eventually.be.rejected + .with.property('message') + .that.include('abort') + }) }) } diff --git a/packages/interface-datastore-tests/typedoc.json b/packages/interface-datastore-tests/typedoc.json index f599dc72..db0b0747 100644 --- a/packages/interface-datastore-tests/typedoc.json +++ b/packages/interface-datastore-tests/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts" ] diff --git a/packages/interface-datastore/CHANGELOG.md b/packages/interface-datastore/CHANGELOG.md index a3208a35..271793a9 100644 --- a/packages/interface-datastore/CHANGELOG.md +++ b/packages/interface-datastore/CHANGELOG.md @@ -1,3 +1,49 @@ +## [interface-datastore-v9.0.2](https://github.com/ipfs/js-stores/compare/interface-datastore-9.0.1...interface-datastore-9.0.2) (2025-10-03) + +### Dependencies + +* bump level from 8.0.1 to 10.0.0 ([#356](https://github.com/ipfs/js-stores/issues/356)) ([c0ec61f](https://github.com/ipfs/js-stores/commit/c0ec61fe965e3bad9d607a0bd3a3c750f00f41d0)) + +## [interface-datastore-v9.0.1](https://github.com/ipfs/js-stores/compare/interface-datastore-9.0.0...interface-datastore-9.0.1) (2025-10-03) + +### Bug Fixes + +* update sibling deps ([3f73b3d](https://github.com/ipfs/js-stores/commit/3f73b3d53ea2d86d0f5c3f06785c0bfc30e8b5e9)) + +## [interface-datastore-v9.0.0](https://github.com/ipfs/js-stores/compare/interface-datastore-8.3.2...interface-datastore-9.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +## [interface-datastore-v8.3.2](https://github.com/ipfs/js-stores/compare/interface-datastore-8.3.1...interface-datastore-8.3.2) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [interface-datastore-v8.3.1](https://github.com/ipfs/js-stores/compare/interface-datastore-8.3.0...interface-datastore-8.3.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## interface-datastore [8.3.0](https://github.com/ipfs/js-stores/compare/interface-datastore-8.2.11...interface-datastore-8.3.0) (2024-08-02) + + + +### Dependencies + +* **interface-store:** upgraded to 6.0.0 + ## interface-datastore [8.2.11](https://github.com/ipfs/js-stores/compare/interface-datastore-v8.2.10...interface-datastore-8.2.11) (2024-02-12) diff --git a/packages/interface-datastore/CODE_OF_CONDUCT.md b/packages/interface-datastore/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/interface-datastore/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/interface-datastore/LICENSE b/packages/interface-datastore/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/interface-datastore/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/interface-datastore/LICENSE-APACHE b/packages/interface-datastore/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/interface-datastore/LICENSE-APACHE +++ b/packages/interface-datastore/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/interface-datastore/README.md b/packages/interface-datastore/README.md index 48062c9c..748222a1 100644 --- a/packages/interface-datastore/README.md +++ b/packages/interface-datastore/README.md @@ -9,6 +9,21 @@ # About + + A Datastore is a key/value database that lets store/retrieve binary blobs using namespaced Keys. It is used by IPFS to store/retrieve arbitrary metadata needed to run the node - DHT provider records, signed peer records, etc. @@ -111,7 +126,7 @@ $ npm i interface-datastore ## Browser ` @@ -125,8 +140,8 @@ Loading this module through a script tag will make it's exports available as `In Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/interface-datastore/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/interface-datastore/LICENSE-MIT) / ) # Contribute diff --git a/packages/interface-datastore/package.json b/packages/interface-datastore/package.json index a872fc82..a68ab5b5 100644 --- a/packages/interface-datastore/package.json +++ b/packages/interface-datastore/package.json @@ -1,6 +1,6 @@ { "name": "interface-datastore", - "version": "8.2.11", + "version": "9.0.2", "description": "datastore interface", "license": "Apache-2.0 OR MIT", "homepage": "https://github.com/ipfs/js-stores/tree/main/packages/interface-datastore#readme", @@ -55,12 +55,98 @@ "import": "./dist/src/key.js" } }, - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true, - "sourceType": "module" - } + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] }, "scripts": { "build": "aegir build", @@ -76,10 +162,10 @@ "release": "aegir release" }, "dependencies": { - "interface-store": "^5.0.0", - "uint8arrays": "^5.0.2" + "interface-store": "^7.0.0", + "uint8arrays": "^5.1.0" }, "devDependencies": { - "aegir": "^42.2.3" + "aegir": "^47.0.16" } } diff --git a/packages/interface-datastore/src/index.ts b/packages/interface-datastore/src/index.ts index 1ee4a57d..46f280f6 100644 --- a/packages/interface-datastore/src/index.ts +++ b/packages/interface-datastore/src/index.ts @@ -1,9 +1,3 @@ -/* eslint-disable @typescript-eslint/ban-types */ -// this ignore is so we can use {} as the default value for the options -// extensions below - it normally means "any non-nullish value" but here -// we are using it as an intersection type - see the aside at the bottom: -// https://github.com/typescript-eslint/typescript-eslint/issues/2063#issuecomment-675156492 - /** * @packageDocumentation * @@ -105,9 +99,9 @@ import { Key } from './key.js' import type { Await, - AwaitIterable, Store, - AbortOptions + AbortOptions, + AwaitGenerator } from 'interface-store' export interface Pair { @@ -127,7 +121,7 @@ GetOptionsExtension = {}, GetManyOptionsExtension = {}, DeleteOptionsExtension = {}, DeleteManyOptionsExtension = {}, QueryOptionsExtension = {}, QueryKeysOptionsExtension = {}, BatchOptionsExtension = {} -> extends Store extends Store, Pair, Pair, HasOptionsExtension, PutOptionsExtension, PutManyOptionsExtension, GetOptionsExtension, GetManyOptionsExtension, DeleteOptionsExtension, DeleteManyOptionsExtension> { @@ -161,7 +155,7 @@ BatchOptionsExtension = {} * console.log('ALL THE VALUES', list) * ``` */ - query(query: Query, options?: AbortOptions & QueryOptionsExtension): AwaitIterable + query(query: Query, options?: AbortOptions & QueryOptionsExtension): AwaitGenerator /** * Query the datastore. @@ -176,7 +170,7 @@ BatchOptionsExtension = {} * console.log('ALL THE KEYS', key) * ``` */ - queryKeys(query: KeyQuery, options?: AbortOptions & QueryKeysOptionsExtension): AwaitIterable + queryKeys(query: KeyQuery, options?: AbortOptions & QueryKeysOptionsExtension): AwaitGenerator } export interface QueryFilter { (item: Pair): boolean } diff --git a/packages/interface-datastore/src/key.ts b/packages/interface-datastore/src/key.ts index 6ef7ff69..4078f076 100644 --- a/packages/interface-datastore/src/key.ts +++ b/packages/interface-datastore/src/key.ts @@ -1,5 +1,6 @@ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' -import { type SupportedEncodings, toString as uint8ArrayToString } from 'uint8arrays/to-string' +import { toString as uint8ArrayToString } from 'uint8arrays/to-string' +import type { SupportedEncodings } from 'uint8arrays/to-string' const pathSepS = '/' const pathSepB = new TextEncoder().encode(pathSepS) @@ -53,7 +54,7 @@ export class Key { /** * Convert to the string representation * - * @param {import('uint8arrays/to-string').SupportedEncodings} [encoding='utf8'] - The encoding to use. + * @param {import('uint8arrays/to-string').SupportedEncodings} [encoding] - The encoding to use. * @returns {string} */ toString (encoding: SupportedEncodings = 'utf8'): string { @@ -201,7 +202,8 @@ export class Key { return this.list() } - /** Returns the "base" namespace of this key. + /** + * Returns the "base" namespace of this key. * * @returns {string} * diff --git a/packages/interface-datastore/test/key.spec.ts b/packages/interface-datastore/test/key.spec.ts index 07dfa61d..5859f58b 100644 --- a/packages/interface-datastore/test/key.spec.ts +++ b/packages/interface-datastore/test/key.spec.ts @@ -71,8 +71,8 @@ describe('Key', () => { const k1 = new Key('/A/B/C') const k2 = new Key('/A/B/C/D') - expect(k1.toString()).to.be.eql('/A/B/C') - expect(k2.toString()).to.be.eql('/A/B/C/D') + expect(k1.toString()).to.equal('/A/B/C') + expect(k2.toString()).to.equal('/A/B/C/D') const checks = [ k1.isAncestorOf(k2), diff --git a/packages/interface-datastore/typedoc.json b/packages/interface-datastore/typedoc.json index 35e9b851..79b0ab45 100644 --- a/packages/interface-datastore/typedoc.json +++ b/packages/interface-datastore/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts", "./src/key.ts" diff --git a/packages/interface-store/CHANGELOG.md b/packages/interface-store/CHANGELOG.md index f9331fa5..4256f694 100644 --- a/packages/interface-store/CHANGELOG.md +++ b/packages/interface-store/CHANGELOG.md @@ -1,3 +1,52 @@ +## [interface-store-v7.0.1](https://github.com/ipfs/js-stores/compare/interface-store-7.0.0...interface-store-7.0.1) (2025-10-03) + +### Bug Fixes + +* accept iterables, return generators ([a685552](https://github.com/ipfs/js-stores/commit/a685552f330c5871e60dcee2632c393900cf36f5)) + +## [interface-store-v7.0.0](https://github.com/ipfs/js-stores/compare/interface-store-6.0.3...interface-store-7.0.0) (2025-10-03) + +### ⚠ BREAKING CHANGES + +* blockstore.get and similar now return streams of bytes + +### Features + +* streaming blockstores ([#358](https://github.com/ipfs/js-stores/issues/358)) ([4dbb136](https://github.com/ipfs/js-stores/commit/4dbb1362d20fc87fcdd261568dca297972f9bc08)) + +## [interface-store-v6.0.3](https://github.com/ipfs/js-stores/compare/interface-store-6.0.2...interface-store-6.0.3) (2025-05-26) + +### Dependencies + +* bump aegir from 44.1.4 to 47.0.16 ([#349](https://github.com/ipfs/js-stores/issues/349)) ([d33d15f](https://github.com/ipfs/js-stores/commit/d33d15f0638856530d0e1868c723e5567abf27e6)) + +## [interface-store-v6.0.2](https://github.com/ipfs/js-stores/compare/interface-store-6.0.1...interface-store-6.0.2) (2024-09-13) + +### Bug Fixes + +* restore code property ([#320](https://github.com/ipfs/js-stores/issues/320)) ([414d4d0](https://github.com/ipfs/js-stores/commit/414d4d0a37a15bac6735539c504a0ff9cfd7862c)) + +## [interface-store-v6.0.1](https://github.com/ipfs/js-stores/compare/interface-store-6.0.0...interface-store-6.0.1) (2024-09-13) + +### Bug Fixes + +* restore release config to package.json ([#321](https://github.com/ipfs/js-stores/issues/321)) ([4f14fb0](https://github.com/ipfs/js-stores/commit/4f14fb09d65a3460b548b59557af108412dc9156)) + +### Dependencies + +* **dev:** bump aegir from 42.2.11 to 44.1.0 ([#316](https://github.com/ipfs/js-stores/issues/316)) ([581a467](https://github.com/ipfs/js-stores/commit/581a46720832916bea11efa2476eb85a00bae9d4)) + +## interface-store [6.0.0](https://github.com/ipfs/js-stores/compare/interface-store-5.1.8...interface-store-6.0.0) (2024-08-02) + + +### ⚠ BREAKING CHANGES + +* To detect the type of error thrown, use `.name` instead of `.code` + +### Features + +* use `.name` property for errors instead of `.code` ([#315](https://github.com/ipfs/js-stores/issues/315)) ([dacd6ce](https://github.com/ipfs/js-stores/commit/dacd6ce6f325262f1bc1451f20789e9e7cd9b9fd)) + ## interface-store [5.1.8](https://github.com/ipfs/js-stores/compare/interface-store-v5.1.7...interface-store-5.1.8) (2024-02-12) diff --git a/packages/interface-store/CODE_OF_CONDUCT.md b/packages/interface-store/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b0fa54c --- /dev/null +++ b/packages/interface-store/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +This project follows the [`IPFS Community Code of Conduct`](https://github.com/ipfs/community/blob/master/code-of-conduct.md) diff --git a/packages/interface-store/LICENSE b/packages/interface-store/LICENSE deleted file mode 100644 index 20ce483c..00000000 --- a/packages/interface-store/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This project is dual licensed under MIT and Apache-2.0. - -MIT: https://www.opensource.org/licenses/mit -Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/interface-store/LICENSE-APACHE b/packages/interface-store/LICENSE-APACHE index 14478a3b..b09cd785 100644 --- a/packages/interface-store/LICENSE-APACHE +++ b/packages/interface-store/LICENSE-APACHE @@ -1,5 +1,201 @@ -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 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -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. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/interface-store/README.md b/packages/interface-store/README.md index 2f152391..52fa8d03 100644 --- a/packages/interface-store/README.md +++ b/packages/interface-store/README.md @@ -9,6 +9,21 @@ # About + + An abstraction of the Datastore/Blockstore codebases. # Install @@ -25,8 +40,8 @@ $ npm i interface-store Licensed under either of -- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) -- MIT ([LICENSE-MIT](LICENSE-MIT) / ) +- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/js-stores/blob/main/packages/interface-store/LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](https://github.com/ipfs/js-stores/blob/main/packages/interface-store/LICENSE-MIT) / ) # Contribute diff --git a/packages/interface-store/package.json b/packages/interface-store/package.json index ba1690dc..6bdfac41 100644 --- a/packages/interface-store/package.json +++ b/packages/interface-store/package.json @@ -1,6 +1,6 @@ { "name": "interface-store", - "version": "5.1.8", + "version": "7.0.1", "description": "A generic interface for storing and retrieving data", "license": "Apache-2.0 OR MIT", "homepage": "https://github.com/ipfs/js-stores/tree/main/packages/interface-store#readme", @@ -15,40 +15,121 @@ "access": "public", "provenance": true }, - "main": "src/index.js", - "types": "dist/src/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "*", - "dist/*", - "dist/src/*" - ], - "src/*": [ - "*", - "dist/*", - "dist/src/*" - ] - } - }, + "type": "module", + "types": "./dist/src/index.d.ts", "files": [ "src", - "dist" + "dist", + "!dist/test", + "!**/*.tsbuildinfo" ], - "eslintConfig": { - "extends": "ipfs", - "parserOptions": { - "project": true + "exports": { + ".": { + "types": "./dist/src/index.d.ts", + "import": "./dist/src/index.js" } }, + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ] + } + ] + ] + }, "scripts": { "build": "aegir build", "lint": "aegir lint", "clean": "aegir clean", - "release": "aegir release", - "dep-check": "aegir dep-check" + "dep-check": "aegir dep-check", + "release": "aegir release" }, "devDependencies": { - "aegir": "^42.2.3" + "aegir": "^47.0.16" } } diff --git a/packages/interface-store/src/errors.ts b/packages/interface-store/src/errors.ts new file mode 100644 index 00000000..63f14d05 --- /dev/null +++ b/packages/interface-store/src/errors.ts @@ -0,0 +1,87 @@ +export class OpenFailedError extends Error { + static name = 'OpenFailedError' + static code = 'ERR_OPEN_FAILED' + name = OpenFailedError.name + code = OpenFailedError.code + + constructor (message = 'Open failed') { + super(message) + } +} + +export class CloseFailedError extends Error { + static name = 'CloseFailedError' + static code = 'ERR_CLOSE_FAILED' + name = CloseFailedError.name + code = CloseFailedError.code + + constructor (message = 'Close failed') { + super(message) + } +} + +export class PutFailedError extends Error { + static name = 'PutFailedError' + static code = 'ERR_PUT_FAILED' + name = PutFailedError.name + code = PutFailedError.code + + constructor (message = 'Put failed') { + super(message) + } +} + +export class GetFailedError extends Error { + static name = 'GetFailedError' + static code = 'ERR_GET_FAILED' + name = GetFailedError.name + code = GetFailedError.code + + constructor (message = 'Get failed') { + super(message) + } +} + +export class DeleteFailedError extends Error { + static name = 'DeleteFailedError' + static code = 'ERR_DELETE_FAILED' + name = DeleteFailedError.name + code = DeleteFailedError.code + + constructor (message = 'Delete failed') { + super(message) + } +} + +export class HasFailedError extends Error { + static name = 'HasFailedError' + static code = 'ERR_HAS_FAILED' + name = HasFailedError.name + code = HasFailedError.code + + constructor (message = 'Has failed') { + super(message) + } +} + +export class NotFoundError extends Error { + static name = 'NotFoundError' + static code = 'ERR_NOT_FOUND' + name = NotFoundError.name + code = NotFoundError.code + + constructor (message = 'Not Found') { + super(message) + } +} + +export class AbortError extends Error { + static name = 'AbortError' + static code = 'ERR_ABORTED' + name = AbortError.name + code = AbortError.code + + constructor (message = 'Aborted') { + super(message) + } +} diff --git a/packages/interface-store/src/index.ts b/packages/interface-store/src/index.ts index 5d84d4ca..b4d6609b 100644 --- a/packages/interface-store/src/index.ts +++ b/packages/interface-store/src/index.ts @@ -1,9 +1,3 @@ -/* eslint-disable @typescript-eslint/ban-types */ -// this ignore is so we can use {} as the default value for the options -// extensions below - it normally means "any non-nullish value" but here -// we are using it as an intersection type - see the aside at the bottom: -// https://github.com/typescript-eslint/typescript-eslint/issues/2063#issuecomment-675156492 - /** * @packageDocumentation * @@ -15,6 +9,11 @@ */ export type AwaitIterable = Iterable | AsyncIterable +/** + * A generator or async generator of values + */ +export type AwaitGenerator = Generator | AsyncGenerator + /** * A value or a promise of a value */ @@ -27,10 +26,11 @@ export interface AbortOptions { signal?: AbortSignal } -export interface Store { +export interface Store { /** * Check for the existence of a value for the passed key * @@ -56,7 +56,7 @@ export interface Store + put(key: Key, val: Input, options?: AbortOptions & PutOptionsExtension): Await /** * Store the given key/value pairs @@ -71,9 +71,9 @@ export interface Store, + source: AwaitIterable, options?: AbortOptions & PutManyOptionsExtension - ): AwaitIterable + ): AwaitGenerator /** * Retrieve the value stored under the given key @@ -85,7 +85,7 @@ export interface Store got content: datastore * ``` */ - get(key: Key, options?: AbortOptions & GetOptionsExtension): Await + get(key: Key, options?: AbortOptions & GetOptionsExtension): Output /** * Retrieve values for the passed keys @@ -101,7 +101,7 @@ export interface Store, options?: AbortOptions & GetManyOptionsExtension - ): AwaitIterable + ): AwaitGenerator /** * Remove the record for the passed key @@ -131,5 +131,7 @@ export interface Store, options?: AbortOptions & DeleteManyOptionsExtension - ): AwaitIterable + ): AwaitGenerator } + +export * from './errors.js' diff --git a/packages/interface-store/tsconfig.json b/packages/interface-store/tsconfig.json index 8988b1e0..5fe8ea40 100644 --- a/packages/interface-store/tsconfig.json +++ b/packages/interface-store/tsconfig.json @@ -1,8 +1,7 @@ { "extends": "aegir/src/config/tsconfig.aegir.json", "compilerOptions": { - "outDir": "dist", - "emitDeclarationOnly": true + "outDir": "dist" }, "include": [ "src" diff --git a/packages/interface-store/typedoc.json b/packages/interface-store/typedoc.json index f599dc72..db0b0747 100644 --- a/packages/interface-store/typedoc.json +++ b/packages/interface-store/typedoc.json @@ -1,4 +1,5 @@ { + "readme": "none", "entryPoints": [ "./src/index.ts" ] diff --git a/typedoc.json b/typedoc.json index f8981b84..74a54bc2 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,4 +1,9 @@ { "$schema": "https://typedoc.org/schema.json", - "name": "IPFS Stores" + "name": "stores", + "readme": "./README.md", + "headings": { + "readme": false, + "document": false + } }