├── .aegir.js ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ └── open_an_issue.md ├── config.yml ├── dependabot.yml └── workflows │ ├── automerge.yml │ ├── js-test-and-release.yml │ └── stale.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── package.json ├── src └── index.ts ├── test ├── browser.ts ├── fixtures │ └── test-level-iterator-destroy.ts ├── index.spec.ts └── node.ts └── tsconfig.json /.aegir.js: -------------------------------------------------------------------------------- 1 | /** @type {import('aegir').PartialOptions} */ 2 | export default { 3 | build: { 4 | bundlesizeMax: '67KB' 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Getting Help on IPFS 4 | url: https://ipfs.io/help 5 | about: All information about how and where to get help on IPFS. 6 | - name: IPFS Official Forum 7 | url: https://discuss.ipfs.io 8 | about: Please post general questions, support requests, and discussions here. 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/open_an_issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Open an issue 3 | about: Only for actionable issues relevant to this repository. 4 | title: '' 5 | labels: need/triage 6 | assignees: '' 7 | 8 | --- 9 | 20 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | # Configuration for welcome - https://github.com/behaviorbot/welcome 2 | 3 | # Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome 4 | # Comment to be posted to on first time issues 5 | newIssueWelcomeComment: > 6 | Thank you for submitting your first issue to this repository! A maintainer 7 | will be here shortly to triage and review. 8 | 9 | In the meantime, please double-check that you have provided all the 10 | necessary information to make this process easy! Any information that can 11 | help save additional round trips is useful! We currently aim to give 12 | initial feedback within **two business days**. If this does not happen, feel 13 | free to leave a comment. 14 | 15 | Please keep an eye on how this issue will be labeled, as labels give an 16 | overview of priorities, assignments and additional actions requested by the 17 | maintainers: 18 | 19 | - "Priority" labels will show how urgent this is for the team. 20 | - "Status" labels will show if this is ready to be worked on, blocked, or in progress. 21 | - "Need" labels will indicate if additional input or analysis is required. 22 | 23 | Finally, remember to use https://discuss.ipfs.io if you just need general 24 | support. 25 | 26 | # Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome 27 | # Comment to be posted to on PRs from first time contributors in your repository 28 | newPRWelcomeComment: > 29 | Thank you for submitting this PR! 30 | 31 | A maintainer will be here shortly to review it. 32 | 33 | We are super grateful, but we are also overloaded! Help us by making sure 34 | that: 35 | 36 | * The context for this PR is clear, with relevant discussion, decisions 37 | and stakeholders linked/mentioned. 38 | 39 | * Your contribution itself is clear (code comments, self-review for the 40 | rest) and in its best form. Follow the [code contribution 41 | guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md#code-contribution-guidelines) 42 | if they apply. 43 | 44 | Getting other community members to do a review would be great help too on 45 | complex PRs (you can ask in the chats/forums). If you are unsure about 46 | something, just leave us a comment. 47 | 48 | Next steps: 49 | 50 | * A maintainer will triage and assign priority to this PR, commenting on 51 | any missing things and potentially assigning a reviewer for high 52 | priority items. 53 | 54 | * The PR gets reviews, discussed and approvals as needed. 55 | 56 | * The PR is merged by maintainers when it has been approved and comments addressed. 57 | 58 | We currently aim to provide initial feedback/triaging within **two business 59 | days**. Please keep an eye on any labelling actions, as these will indicate 60 | priorities and status of your contribution. 61 | 62 | We are very grateful for your contribution! 63 | 64 | 65 | # Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge 66 | # Comment to be posted to on pull requests merged by a first time user 67 | # Currently disabled 68 | #firstPRMergeComment: "" 69 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | commit-message: 10 | prefix: "deps" 11 | prefix-development: "deps(dev)" 12 | -------------------------------------------------------------------------------- /.github/workflows/automerge.yml: -------------------------------------------------------------------------------- 1 | # File managed by web3-bot. DO NOT EDIT. 2 | # See https://github.com/protocol/.github/ for details. 3 | 4 | name: Automerge 5 | on: [ pull_request ] 6 | 7 | jobs: 8 | automerge: 9 | uses: protocol/.github/.github/workflows/automerge.yml@master 10 | with: 11 | job: 'automerge' 12 | -------------------------------------------------------------------------------- /.github/workflows/js-test-and-release.yml: -------------------------------------------------------------------------------- 1 | # File managed by web3-bot. DO NOT EDIT. 2 | # See https://github.com/protocol/.github/ for details. 3 | 4 | name: test & maybe release 5 | on: 6 | push: 7 | branches: 8 | - master 9 | pull_request: 10 | 11 | jobs: 12 | 13 | check: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: actions/setup-node@v3 18 | with: 19 | node-version: lts/* 20 | - uses: ipfs/aegir/actions/cache-node-modules@master 21 | - run: npm run --if-present lint 22 | - run: npm run --if-present dep-check 23 | 24 | test-node: 25 | needs: check 26 | runs-on: ${{ matrix.os }} 27 | strategy: 28 | matrix: 29 | os: [windows-latest, ubuntu-latest, macos-latest] 30 | node: [lts/*] 31 | fail-fast: true 32 | steps: 33 | - uses: actions/checkout@v3 34 | - uses: actions/setup-node@v3 35 | with: 36 | node-version: ${{ matrix.node }} 37 | - uses: ipfs/aegir/actions/cache-node-modules@master 38 | - run: npm run --if-present test:node 39 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 40 | with: 41 | flags: node 42 | 43 | test-chrome: 44 | needs: check 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v3 48 | - uses: actions/setup-node@v3 49 | with: 50 | node-version: lts/* 51 | - uses: ipfs/aegir/actions/cache-node-modules@master 52 | - run: npm run --if-present test:chrome 53 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 54 | with: 55 | flags: chrome 56 | 57 | test-chrome-webworker: 58 | needs: check 59 | runs-on: ubuntu-latest 60 | steps: 61 | - uses: actions/checkout@v3 62 | - uses: actions/setup-node@v3 63 | with: 64 | node-version: lts/* 65 | - uses: ipfs/aegir/actions/cache-node-modules@master 66 | - run: npm run --if-present test:chrome-webworker 67 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 68 | with: 69 | flags: chrome-webworker 70 | 71 | test-firefox: 72 | needs: check 73 | runs-on: ubuntu-latest 74 | steps: 75 | - uses: actions/checkout@v3 76 | - uses: actions/setup-node@v3 77 | with: 78 | node-version: lts/* 79 | - uses: ipfs/aegir/actions/cache-node-modules@master 80 | - run: npm run --if-present test:firefox 81 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 82 | with: 83 | flags: firefox 84 | 85 | test-firefox-webworker: 86 | needs: check 87 | runs-on: ubuntu-latest 88 | steps: 89 | - uses: actions/checkout@v3 90 | - uses: actions/setup-node@v3 91 | with: 92 | node-version: lts/* 93 | - uses: ipfs/aegir/actions/cache-node-modules@master 94 | - run: npm run --if-present test:firefox-webworker 95 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 96 | with: 97 | flags: firefox-webworker 98 | 99 | test-webkit: 100 | needs: check 101 | runs-on: ${{ matrix.os }} 102 | strategy: 103 | matrix: 104 | os: [ubuntu-latest, macos-latest] 105 | node: [lts/*] 106 | fail-fast: true 107 | steps: 108 | - uses: actions/checkout@v3 109 | - uses: actions/setup-node@v3 110 | with: 111 | node-version: lts/* 112 | - uses: ipfs/aegir/actions/cache-node-modules@master 113 | - run: npm run --if-present test:webkit 114 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 115 | with: 116 | flags: webkit 117 | 118 | test-webkit-webworker: 119 | needs: check 120 | runs-on: ${{ matrix.os }} 121 | strategy: 122 | matrix: 123 | os: [ubuntu-latest, macos-latest] 124 | node: [lts/*] 125 | fail-fast: true 126 | steps: 127 | - uses: actions/checkout@v3 128 | - uses: actions/setup-node@v3 129 | with: 130 | node-version: lts/* 131 | - uses: ipfs/aegir/actions/cache-node-modules@master 132 | - run: npm run --if-present test:webkit-webworker 133 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 134 | with: 135 | flags: webkit-webworker 136 | 137 | test-electron-main: 138 | needs: check 139 | runs-on: ubuntu-latest 140 | steps: 141 | - uses: actions/checkout@v3 142 | - uses: actions/setup-node@v3 143 | with: 144 | node-version: lts/* 145 | - uses: ipfs/aegir/actions/cache-node-modules@master 146 | - run: npx xvfb-maybe npm run --if-present test:electron-main 147 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 148 | with: 149 | flags: electron-main 150 | 151 | test-electron-renderer: 152 | needs: check 153 | runs-on: ubuntu-latest 154 | steps: 155 | - uses: actions/checkout@v3 156 | - uses: actions/setup-node@v3 157 | with: 158 | node-version: lts/* 159 | - uses: ipfs/aegir/actions/cache-node-modules@master 160 | - run: npx xvfb-maybe npm run --if-present test:electron-renderer 161 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 162 | with: 163 | flags: electron-renderer 164 | 165 | release: 166 | needs: [test-node, test-chrome, test-chrome-webworker, test-firefox, test-firefox-webworker, test-webkit, test-webkit-webworker, test-electron-main, test-electron-renderer] 167 | runs-on: ubuntu-latest 168 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 169 | steps: 170 | - uses: actions/checkout@v3 171 | with: 172 | fetch-depth: 0 173 | - uses: actions/setup-node@v3 174 | with: 175 | node-version: lts/* 176 | - uses: ipfs/aegir/actions/cache-node-modules@master 177 | - uses: ipfs/aegir/actions/docker-login@master 178 | with: 179 | docker-token: ${{ secrets.DOCKER_TOKEN }} 180 | docker-username: ${{ secrets.DOCKER_USERNAME }} 181 | - run: npm run --if-present release 182 | env: 183 | GITHUB_TOKEN: ${{ secrets.UCI_GITHUB_TOKEN || github.token }} 184 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 185 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close and mark stale issue 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | 7 | jobs: 8 | stale: 9 | 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | pull-requests: write 14 | 15 | steps: 16 | - uses: actions/stale@v3 17 | with: 18 | repo-token: ${{ secrets.GITHUB_TOKEN }} 19 | stale-issue-message: 'Oops, seems like we needed more information for this issue, please comment with more details or this issue will be closed in 7 days.' 20 | close-issue-message: 'This issue was closed because it is missing author input.' 21 | stale-issue-label: 'kind/stale' 22 | any-of-labels: 'need/author-input' 23 | exempt-issue-labels: 'need/triage,need/community-input,need/maintainer-input,need/maintainers-input,need/analysis,status/blocked,status/in-progress,status/ready,status/deferred,status/inactive' 24 | days-before-issue-stale: 6 25 | days-before-issue-close: 7 26 | enable-statistics: true 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | dist 4 | .docs 5 | .coverage 6 | node_modules 7 | package-lock.json 8 | yarn.lock 9 | .vscode 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [10.0.2](https://github.com/ipfs/js-datastore-level/compare/v10.0.1...v10.0.2) (2023-03-23) 2 | 3 | 4 | ### Dependencies 5 | 6 | * update interface-store to 5.x.x ([#176](https://github.com/ipfs/js-datastore-level/issues/176)) ([2c89f37](https://github.com/ipfs/js-datastore-level/commit/2c89f371a03019d5e811bbd7893abb11fe0ea46f)) 7 | 8 | ## [10.0.1](https://github.com/ipfs/js-datastore-level/compare/v10.0.0...v10.0.1) (2023-03-14) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * update project config ([#173](https://github.com/ipfs/js-datastore-level/issues/173)) ([57a69c7](https://github.com/ipfs/js-datastore-level/commit/57a69c79324cecd5966000e65b4734997c183c17)) 14 | 15 | ## [10.0.0](https://github.com/ipfs/js-datastore-level/compare/v9.0.4...v10.0.0) (2023-03-13) 16 | 17 | 18 | ### ⚠ BREAKING CHANGES 19 | 20 | * this module now implements interface-datastore@8.x.x 21 | 22 | ### Dependencies 23 | 24 | * update to interface-datastore 8.x.x ([#172](https://github.com/ipfs/js-datastore-level/issues/172)) ([178d235](https://github.com/ipfs/js-datastore-level/commit/178d235254805f2abdd919e0860bd86af5b48582)) 25 | 26 | ## [9.0.4](https://github.com/ipfs/js-datastore-level/compare/v9.0.3...v9.0.4) (2022-11-03) 27 | 28 | 29 | ### Dependencies 30 | 31 | * bump it-map from 1.0.6 to 2.0.0 ([#136](https://github.com/ipfs/js-datastore-level/issues/136)) ([049045a](https://github.com/ipfs/js-datastore-level/commit/049045a4aced05840ec7abce235f3e64c83d42bd)) 32 | 33 | ## [9.0.3](https://github.com/ipfs/js-datastore-level/compare/v9.0.2...v9.0.3) (2022-11-03) 34 | 35 | 36 | ### Dependencies 37 | 38 | * bump it-sort from 1.0.1 to 2.0.0 ([#137](https://github.com/ipfs/js-datastore-level/issues/137)) ([4ab7b70](https://github.com/ipfs/js-datastore-level/commit/4ab7b700642ad09548c3580129de9bb53270c33d)) 39 | 40 | ## [9.0.2](https://github.com/ipfs/js-datastore-level/compare/v9.0.1...v9.0.2) (2022-11-03) 41 | 42 | 43 | ### Dependencies 44 | 45 | * bump it-filter from 1.0.3 to 2.0.0 ([#138](https://github.com/ipfs/js-datastore-level/issues/138)) ([2b13ed0](https://github.com/ipfs/js-datastore-level/commit/2b13ed04723922093cd2a15fff0a04de15a59f69)) 46 | * bump it-take from 1.0.2 to 2.0.0 ([#139](https://github.com/ipfs/js-datastore-level/issues/139)) ([1052cdd](https://github.com/ipfs/js-datastore-level/commit/1052cdd68dbee91d2adfab578b04f741448b22ef)) 47 | * **dev:** bump @ipld/dag-cbor from 7.0.3 to 8.0.0 ([#142](https://github.com/ipfs/js-datastore-level/issues/142)) ([1180956](https://github.com/ipfs/js-datastore-level/commit/11809565db0b80ae307f8ce219402600f9ce5745)) 48 | * **dev:** bump multiformats from 9.9.0 to 10.0.2 ([#141](https://github.com/ipfs/js-datastore-level/issues/141)) ([8db833b](https://github.com/ipfs/js-datastore-level/commit/8db833bc97122334bb21bd3de6b52018b4c3e816)) 49 | 50 | ## [9.0.1](https://github.com/ipfs/js-datastore-level/compare/v9.0.0...v9.0.1) (2022-08-14) 51 | 52 | 53 | ### Bug Fixes 54 | 55 | * restore open options and support old level iterators ([#132](https://github.com/ipfs/js-datastore-level/issues/132)) ([78e4911](https://github.com/ipfs/js-datastore-level/commit/78e4911458371740f2c482c7b1907ec6c15d61d9)) 56 | 57 | ## [9.0.0](https://github.com/ipfs/js-datastore-level/compare/v8.0.0...v9.0.0) (2022-08-12) 58 | 59 | 60 | ### ⚠ BREAKING CHANGES 61 | 62 | * this module used to be published as ESM/CJS now it is just ESM 63 | 64 | ### Features 65 | 66 | * publish as ESM only ([#131](https://github.com/ipfs/js-datastore-level/issues/131)) ([0d3b6ab](https://github.com/ipfs/js-datastore-level/commit/0d3b6ab61b23c20587059b01e5446d9638fb569b)) 67 | 68 | 69 | ### Trivial Changes 70 | 71 | * Update .github/workflows/stale.yml [skip ci] ([f5b456e](https://github.com/ipfs/js-datastore-level/commit/f5b456e022d2b00edad0d913bbcc517ce0b90218)) 72 | 73 | ## [8.0.0](https://github.com/ipfs/js-datastore-level/compare/v7.0.1...v8.0.0) (2022-01-19) 74 | 75 | 76 | ### ⚠ BREAKING CHANGES 77 | 78 | * updates project config to use unified ci 79 | 80 | ### Trivial Changes 81 | 82 | * switch to unified ci ([#99](https://github.com/ipfs/js-datastore-level/issues/99)) ([8344486](https://github.com/ipfs/js-datastore-level/commit/8344486d6971e0b2f49ceaa6dbc63f6469e8ed12)) 83 | 84 | ## [7.0.1](https://github.com/ipfs/js-datastore-level/compare/v7.0.0...v7.0.1) (2021-09-09) 85 | 86 | 87 | 88 | # [7.0.0](https://github.com/ipfs/js-datastore-level/compare/v6.0.2...v7.0.0) (2021-09-08) 89 | 90 | 91 | ### chore 92 | 93 | * switch to ESM ([#87](https://github.com/ipfs/js-datastore-level/issues/87)) ([798e995](https://github.com/ipfs/js-datastore-level/commit/798e9958967c2fc279110eebe75e78523560f903)) 94 | 95 | 96 | ### BREAKING CHANGES 97 | 98 | * deep imports/requires are no longer possible 99 | 100 | 101 | 102 | ## [6.0.2](https://github.com/ipfs/js-datastore-level/compare/v6.0.1...v6.0.2) (2021-07-30) 103 | 104 | 105 | ### Features 106 | 107 | * add level options ([#82](https://github.com/ipfs/js-datastore-level/issues/82)) ([1a3e060](https://github.com/ipfs/js-datastore-level/commit/1a3e060d12bcebc8b5bdc8123d03b23bbc13de59)) 108 | 109 | 110 | 111 | ## [6.0.1](https://github.com/ipfs/js-datastore-level/compare/v6.0.0...v6.0.1) (2021-07-10) 112 | 113 | 114 | 115 | # [6.0.0](https://github.com/ipfs/js-datastore-level/compare/v5.0.1...v6.0.0) (2021-07-06) 116 | 117 | 118 | ### chore 119 | 120 | * update deps ([7e20056](https://github.com/ipfs/js-datastore-level/commit/7e20056b13db55e74c5fde986ec9afe186bba414)) 121 | 122 | 123 | ### BREAKING CHANGES 124 | 125 | * uses new interface-datastore interfaces 126 | 127 | 128 | 129 | ## [5.0.1](https://github.com/ipfs/js-datastore-level/compare/v5.0.0...v5.0.1) (2021-04-19) 130 | 131 | 132 | 133 | # [5.0.0](https://github.com/ipfs/js-datastore-level/compare/v4.0.0...v5.0.0) (2021-04-15) 134 | 135 | 136 | ### Features 137 | 138 | * split .query into .query and .queryKeys ([#70](https://github.com/ipfs/js-datastore-level/issues/70)) ([39ba735](https://github.com/ipfs/js-datastore-level/commit/39ba735c591524270740b49bfaa09fa4bcbb11d0)) 139 | 140 | 141 | 142 | # [4.0.0](https://github.com/ipfs/js-datastore-level/compare/v3.0.0...v4.0.0) (2021-01-29) 143 | 144 | 145 | ### chore 146 | 147 | * **deps:** bump level from 5.0.1 to 6.0.1 ([#31](https://github.com/ipfs/js-datastore-level/issues/31)) ([06853bd](https://github.com/ipfs/js-datastore-level/commit/06853bd389f1f0c8cd00d12219040a903ed48633)) 148 | 149 | 150 | ### BREAKING CHANGES 151 | 152 | * **deps:** requires an upgrade to existing datastores created in the browser with level-js@4 or below 153 | 154 | 155 | 156 | # [3.0.0](https://github.com/ipfs/js-datastore-level/compare/v2.0.0...v3.0.0) (2021-01-22) 157 | 158 | 159 | ### Bug Fixes 160 | 161 | * fix constructor ([#58](https://github.com/ipfs/js-datastore-level/issues/58)) ([621e425](https://github.com/ipfs/js-datastore-level/commit/621e42569d8c31c3d2b7311a8abd2594fa6621bd)) 162 | 163 | 164 | ### Features 165 | 166 | * types ([#53](https://github.com/ipfs/js-datastore-level/issues/53)) ([51cd55e](https://github.com/ipfs/js-datastore-level/commit/51cd55e34aa5139dd9dfdb8966df5283e3c5a324)) 167 | 168 | 169 | 170 | 171 | # [2.0.0](https://github.com/ipfs/js-datastore-level/compare/v1.1.0...v2.0.0) (2020-07-29) 172 | 173 | 174 | ### Bug Fixes 175 | 176 | * remove node buffers ([#39](https://github.com/ipfs/js-datastore-level/issues/39)) ([19fe886](https://github.com/ipfs/js-datastore-level/commit/19fe886)) 177 | 178 | 179 | ### BREAKING CHANGES 180 | 181 | * remove node buffers in favour of Uint8Arrays 182 | 183 | 184 | 185 | 186 | # [1.1.0](https://github.com/ipfs/js-datastore-level/compare/v1.0.0...v1.1.0) (2020-05-07) 187 | 188 | 189 | ### Bug Fixes 190 | 191 | * **ci:** add empty commit to fix lint checks on master ([60d14c0](https://github.com/ipfs/js-datastore-level/commit/60d14c0)) 192 | 193 | 194 | ### Features 195 | 196 | * add streaming/cancellable API ([#34](https://github.com/ipfs/js-datastore-level/issues/34)) ([6bfb51a](https://github.com/ipfs/js-datastore-level/commit/6bfb51a)) 197 | 198 | 199 | 200 | 201 | # [1.0.0](https://github.com/ipfs/js-datastore-level/compare/v0.14.1...v1.0.0) (2020-04-28) 202 | 203 | 204 | 205 | 206 | ## [0.14.1](https://github.com/ipfs/js-datastore-level/compare/v0.14.0...v0.14.1) (2020-01-14) 207 | 208 | 209 | ### Bug Fixes 210 | 211 | * leveldb iterator memory leak ([#26](https://github.com/ipfs/js-datastore-level/issues/26)) ([e503c1a](https://github.com/ipfs/js-datastore-level/commit/e503c1a)), closes [/github.com/Level/leveldown/blob/d3453fbde4d2a8aa04d9091101c25c999649069b/binding.cc#L545](https://github.com//github.com/Level/leveldown/blob/d3453fbde4d2a8aa04d9091101c25c999649069b/binding.cc/issues/L545) 212 | 213 | 214 | ### Performance Improvements 215 | 216 | * optimize prefix search ([#25](https://github.com/ipfs/js-datastore-level/issues/25)) ([8efa812](https://github.com/ipfs/js-datastore-level/commit/8efa812)) 217 | 218 | 219 | 220 | 221 | # [0.14.0](https://github.com/ipfs/js-datastore-level/compare/v0.13.0...v0.14.0) (2019-11-29) 222 | 223 | 224 | 225 | 226 | # [0.13.0](https://github.com/ipfs/js-datastore-level/compare/v0.12.1...v0.13.0) (2019-11-29) 227 | 228 | 229 | ### Bug Fixes 230 | 231 | * init db in overridable method for easier extending ([#21](https://github.com/ipfs/js-datastore-level/issues/21)) ([b21428c](https://github.com/ipfs/js-datastore-level/commit/b21428c)) 232 | 233 | 234 | 235 | 236 | ## [0.12.1](https://github.com/ipfs/js-datastore-level/compare/v0.12.0...v0.12.1) (2019-06-26) 237 | 238 | 239 | ### Bug Fixes 240 | 241 | * swap leveldown/level.js for level ([#20](https://github.com/ipfs/js-datastore-level/issues/20)) ([d16e212](https://github.com/ipfs/js-datastore-level/commit/d16e212)) 242 | 243 | 244 | 245 | 246 | # [0.12.0](https://github.com/ipfs/js-datastore-level/compare/v0.11.0...v0.12.0) (2019-05-29) 247 | 248 | 249 | ### Bug Fixes 250 | 251 | * remove unused var ([74d4a36](https://github.com/ipfs/js-datastore-level/commit/74d4a36)) 252 | * tests ([601599d](https://github.com/ipfs/js-datastore-level/commit/601599d)) 253 | 254 | 255 | 256 | 257 | # [0.11.0](https://github.com/ipfs/js-datastore-level/compare/v0.10.0...v0.11.0) (2019-04-29) 258 | 259 | 260 | 261 | 262 | # [0.10.0](https://github.com/ipfs/js-datastore-level/compare/v0.9.0...v0.10.0) (2018-10-24) 263 | 264 | 265 | 266 | 267 | # [0.9.0](https://github.com/ipfs/js-datastore-level/compare/v0.8.0...v0.9.0) (2018-09-19) 268 | 269 | 270 | ### Features 271 | 272 | * add basic error codes ([02a5146](https://github.com/ipfs/js-datastore-level/commit/02a5146)) 273 | 274 | 275 | 276 | 277 | # [0.8.0](https://github.com/ipfs/js-datastore-level/compare/v0.7.0...v0.8.0) (2018-05-29) 278 | 279 | 280 | ### Bug Fixes 281 | 282 | * add test and fix constructor ([396f657](https://github.com/ipfs/js-datastore-level/commit/396f657)) 283 | * update binary encoding for levelup 2 ([a5d7378](https://github.com/ipfs/js-datastore-level/commit/a5d7378)) 284 | * upgrade level libs to resolve node 10 failure ([a427eca](https://github.com/ipfs/js-datastore-level/commit/a427eca)) 285 | 286 | 287 | 288 | 289 | # [0.7.0](https://github.com/ipfs/js-datastore-level/compare/v0.6.0...v0.7.0) (2017-11-06) 290 | 291 | 292 | ### Bug Fixes 293 | 294 | * Windows interop ([#4](https://github.com/ipfs/js-datastore-level/issues/4)) ([5d67042](https://github.com/ipfs/js-datastore-level/commit/5d67042)) 295 | 296 | 297 | 298 | 299 | # [0.6.0](https://github.com/ipfs/js-datastore-level/compare/v0.5.0...v0.6.0) (2017-07-23) 300 | 301 | 302 | 303 | 304 | # [0.5.0](https://github.com/ipfs/js-datastore-level/compare/v0.4.2...v0.5.0) (2017-07-22) 305 | 306 | 307 | 308 | 309 | ## [0.4.2](https://github.com/ipfs/js-datastore-level/compare/v0.4.0...v0.4.2) (2017-05-24) 310 | 311 | 312 | ### Bug Fixes 313 | 314 | * Object.assign is now evil and no longer is behaving as spec says when Webpacked ([5e40f3b](https://github.com/ipfs/js-datastore-level/commit/5e40f3b)) 315 | * Object.assign is now evil and no longer is behaving as spec says when Webpacked ([c3f50ec](https://github.com/ipfs/js-datastore-level/commit/c3f50ec)) 316 | 317 | 318 | 319 | 320 | ## [0.4.1](https://github.com/ipfs/js-datastore-level/compare/v0.4.0...v0.4.1) (2017-05-24) 321 | 322 | 323 | ### Bug Fixes 324 | 325 | * Object.assign is now evil and no longer is behaving as spec says when Webpacked ([077bbc1](https://github.com/ipfs/js-datastore-level/commit/077bbc1)) 326 | 327 | 328 | 329 | 330 | # [0.4.0](https://github.com/ipfs/js-datastore-level/compare/v0.3.0...v0.4.0) (2017-05-23) 331 | 332 | 333 | 334 | 335 | # [0.3.0](https://github.com/ipfs/js-datastore-level/compare/v0.2.0...v0.3.0) (2017-03-23) 336 | 337 | 338 | 339 | 340 | # [0.2.0](https://github.com/ipfs/js-datastore-level/compare/v0.1.0...v0.2.0) (2017-03-23) 341 | 342 | 343 | ### Features 344 | 345 | * add open method ([fd12c6b](https://github.com/ipfs/js-datastore-level/commit/fd12c6b)) 346 | 347 | 348 | 349 | 350 | # 0.1.0 (2017-03-15) 351 | 352 | 353 | ### Bug Fixes 354 | 355 | * key handling ([682f8b3](https://github.com/ipfs/js-datastore-level/commit/682f8b3)) 356 | * working interop with go ([f5e03c6](https://github.com/ipfs/js-datastore-level/commit/f5e03c6)) 357 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This project is dual licensed under MIT and Apache-2.0. 2 | 3 | MIT: https://www.opensource.org/licenses/mit 4 | Apache-2.0: https://www.apache.org/licenses/license-2.0 5 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 2 | 3 | http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 6 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⛔️ This module is now part of https://github.com/ipfs/js-stores 2 | 3 | # datastore-level 4 | 5 | [![ipfs.tech](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.tech) 6 | [![Discuss](https://img.shields.io/discourse/https/discuss.ipfs.tech/posts.svg?style=flat-square)](https://discuss.ipfs.tech) 7 | [![codecov](https://img.shields.io/codecov/c/github/ipfs/js-datastore-level.svg?style=flat-square)](https://codecov.io/gh/ipfs/js-datastore-level) 8 | [![CI](https://img.shields.io/github/actions/workflow/status/ipfs/js-datastore-level/js-test-and-release.yml?branch=master\&style=flat-square)](https://github.com/ipfs/js-datastore-level/actions/workflows/js-test-and-release.yml?query=branch%3Amaster) 9 | 10 | > Datastore implementation with level(up|down) backend 11 | 12 | ## Table of contents 13 | 14 | - [Install](#install) 15 | - [Browser ` 34 | ``` 35 | 36 | ## Usage 37 | 38 | ```js 39 | import { LevelDatastore } from 'datastore-level' 40 | 41 | // Default using level as backend for node or the browser 42 | const store = new LevelDatastore('path/to/store') 43 | 44 | // another leveldown compliant backend like memory-level 45 | const memStore = new LevelDatastore( 46 | new MemoryLevel({ 47 | keyEncoding: 'utf8', 48 | valueEncoding: 'view' 49 | }) 50 | ) 51 | ``` 52 | 53 | ### Browser Shimming Leveldown 54 | 55 | `LevelStore` uses the `level` module to automatically use `level` if a modern bundler is used which can detect bundle targets based on the `pkg.browser` property in your `package.json`. 56 | 57 | If you are using a bundler that does not support `pkg.browser`, you will need to handle the shimming yourself, as was the case with versions of `LevelStore` 0.7.0 and earlier. 58 | 59 | ### Database names 60 | 61 | `level-js@3` changed the database prefix from `IDBWrapper-` to `level-js-`, so please specify the old prefix if you wish to continue using databases created using `datastore-level` prior to `v0.12.0`. E.g. 62 | 63 | ```javascript 64 | import leveljs from 'level-js' 65 | import browserStore = new LevelDatastore( 66 | new Level('my/db/name', { 67 | prefix: 'IDBWrapper-' 68 | }) 69 | }) 70 | ``` 71 | 72 | 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) 73 | 74 | ## License 75 | 76 | Licensed under either of 77 | 78 | - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) 79 | - MIT ([LICENSE-MIT](LICENSE-MIT) / ) 80 | 81 | ## Contribute 82 | 83 | Contributions welcome! Please check out [the issues](https://github.com/ipfs/js-datastore-level/issues). 84 | 85 | Also see our [contributing document](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md) for more information on how we work, and about contributing in general. 86 | 87 | Please be aware that all interactions related to this repo are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). 88 | 89 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 90 | 91 | [![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) 92 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "datastore-level", 3 | "version": "10.0.2", 4 | "description": "Datastore implementation with level(up|down) backend", 5 | "author": "Friedel Ziegelmayer", 6 | "license": "Apache-2.0 OR MIT", 7 | "homepage": "https://github.com/ipfs/js-datastore-level#readme", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/ipfs/js-datastore-level.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/ipfs/js-datastore-level/issues" 14 | }, 15 | "keywords": [ 16 | "datastore", 17 | "interface", 18 | "ipfs", 19 | "key-value", 20 | "leveldb", 21 | "leveldown", 22 | "levelup" 23 | ], 24 | "engines": { 25 | "node": ">=16.0.0", 26 | "npm": ">=7.0.0" 27 | }, 28 | "type": "module", 29 | "types": "./dist/src/index.d.ts", 30 | "files": [ 31 | "src", 32 | "dist", 33 | "!dist/test", 34 | "!**/*.tsbuildinfo" 35 | ], 36 | "exports": { 37 | ".": { 38 | "types": "./dist/src/index.d.ts", 39 | "import": "./dist/src/index.js" 40 | } 41 | }, 42 | "eslintConfig": { 43 | "extends": "ipfs", 44 | "parserOptions": { 45 | "sourceType": "module" 46 | } 47 | }, 48 | "release": { 49 | "branches": [ 50 | "master" 51 | ], 52 | "plugins": [ 53 | [ 54 | "@semantic-release/commit-analyzer", 55 | { 56 | "preset": "conventionalcommits", 57 | "releaseRules": [ 58 | { 59 | "breaking": true, 60 | "release": "major" 61 | }, 62 | { 63 | "revert": true, 64 | "release": "patch" 65 | }, 66 | { 67 | "type": "feat", 68 | "release": "minor" 69 | }, 70 | { 71 | "type": "fix", 72 | "release": "patch" 73 | }, 74 | { 75 | "type": "docs", 76 | "release": "patch" 77 | }, 78 | { 79 | "type": "test", 80 | "release": "patch" 81 | }, 82 | { 83 | "type": "deps", 84 | "release": "patch" 85 | }, 86 | { 87 | "scope": "no-release", 88 | "release": false 89 | } 90 | ] 91 | } 92 | ], 93 | [ 94 | "@semantic-release/release-notes-generator", 95 | { 96 | "preset": "conventionalcommits", 97 | "presetConfig": { 98 | "types": [ 99 | { 100 | "type": "feat", 101 | "section": "Features" 102 | }, 103 | { 104 | "type": "fix", 105 | "section": "Bug Fixes" 106 | }, 107 | { 108 | "type": "chore", 109 | "section": "Trivial Changes" 110 | }, 111 | { 112 | "type": "docs", 113 | "section": "Documentation" 114 | }, 115 | { 116 | "type": "deps", 117 | "section": "Dependencies" 118 | }, 119 | { 120 | "type": "test", 121 | "section": "Tests" 122 | } 123 | ] 124 | } 125 | } 126 | ], 127 | "@semantic-release/changelog", 128 | "@semantic-release/npm", 129 | "@semantic-release/github", 130 | "@semantic-release/git" 131 | ] 132 | }, 133 | "scripts": { 134 | "clean": "aegir clean", 135 | "lint": "aegir lint", 136 | "build": "aegir build", 137 | "release": "aegir release", 138 | "test": "aegir test", 139 | "test:node": "aegir test -t node --cov", 140 | "test:chrome": "aegir test -t browser --cov", 141 | "test:chrome-webworker": "aegir test -t webworker", 142 | "test:firefox": "aegir test -t browser -- --browser firefox", 143 | "test:firefox-webworker": "aegir test -t webworker -- --browser firefox", 144 | "test:electron-main": "aegir test -t electron-main", 145 | "dep-check": "aegir dep-check" 146 | }, 147 | "dependencies": { 148 | "datastore-core": "^9.0.4", 149 | "interface-datastore": "^8.1.2", 150 | "it-filter": "^2.0.0", 151 | "it-map": "^2.0.0", 152 | "it-sort": "^2.0.0", 153 | "it-take": "^2.0.0", 154 | "level": "^8.0.0" 155 | }, 156 | "devDependencies": { 157 | "aegir": "^38.1.7", 158 | "interface-datastore-tests": "^5.0.0", 159 | "ipfs-utils": "^9.0.4", 160 | "memory-level": "^1.0.0" 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Batch, Key, KeyQuery, Pair, Query } from 'interface-datastore' 2 | import { BaseDatastore, Errors } from 'datastore-core' 3 | import filter from 'it-filter' 4 | import map from 'it-map' 5 | import take from 'it-take' 6 | import sort from 'it-sort' 7 | import { Level } from 'level' 8 | import type { DatabaseOptions, OpenOptions, IteratorOptions } from 'level' 9 | 10 | interface BatchPut { 11 | type: 'put' 12 | key: string 13 | value: Uint8Array 14 | } 15 | 16 | interface BatchDel { 17 | type: 'del' 18 | key: string 19 | } 20 | 21 | type BatchOp = BatchPut | BatchDel 22 | 23 | /** 24 | * A datastore backed by leveldb 25 | */ 26 | export class LevelDatastore extends BaseDatastore { 27 | public db: Level 28 | private readonly opts: OpenOptions 29 | 30 | constructor (path: string | Level, opts: DatabaseOptions & OpenOptions = {}) { 31 | super() 32 | 33 | this.db = typeof path === 'string' 34 | ? new Level(path, { 35 | ...opts, 36 | keyEncoding: 'utf8', 37 | valueEncoding: 'view' 38 | }) 39 | : path 40 | 41 | this.opts = { 42 | createIfMissing: true, 43 | compression: false, // same default as go 44 | ...opts 45 | } 46 | } 47 | 48 | async open (): Promise { 49 | try { 50 | await this.db.open(this.opts) 51 | } catch (err: any) { 52 | throw Errors.dbOpenFailedError(err) 53 | } 54 | } 55 | 56 | async put (key: Key, value: Uint8Array): Promise { 57 | try { 58 | await this.db.put(key.toString(), value) 59 | 60 | return key 61 | } catch (err: any) { 62 | throw Errors.dbWriteFailedError(err) 63 | } 64 | } 65 | 66 | async get (key: Key): Promise { 67 | let data 68 | try { 69 | data = await this.db.get(key.toString()) 70 | } catch (err: any) { 71 | if (err.notFound != null) { 72 | throw Errors.notFoundError(err) 73 | } 74 | 75 | throw Errors.dbWriteFailedError(err) 76 | } 77 | return data 78 | } 79 | 80 | async has (key: Key): Promise { 81 | try { 82 | await this.db.get(key.toString()) 83 | } catch (err: any) { 84 | if (err.notFound != null) { 85 | return false 86 | } 87 | 88 | throw err 89 | } 90 | return true 91 | } 92 | 93 | async delete (key: Key): Promise { 94 | try { 95 | await this.db.del(key.toString()) 96 | } catch (err: any) { 97 | throw Errors.dbDeleteFailedError(err) 98 | } 99 | } 100 | 101 | async close (): Promise { 102 | await this.db.close() 103 | } 104 | 105 | batch (): Batch { 106 | const ops: BatchOp[] = [] 107 | 108 | return { 109 | put: (key, value) => { 110 | ops.push({ 111 | type: 'put', 112 | key: key.toString(), 113 | value 114 | }) 115 | }, 116 | delete: (key) => { 117 | ops.push({ 118 | type: 'del', 119 | key: key.toString() 120 | }) 121 | }, 122 | commit: async () => { 123 | if (this.db.batch == null) { 124 | throw new Error('Batch operations unsupported by underlying Level') 125 | } 126 | 127 | await this.db.batch(ops) 128 | } 129 | } 130 | } 131 | 132 | query (q: Query): AsyncIterable { 133 | let it = this._query({ 134 | values: true, 135 | prefix: q.prefix 136 | }) 137 | 138 | if (Array.isArray(q.filters)) { 139 | it = q.filters.reduce((it, f) => filter(it, f), it) 140 | } 141 | 142 | if (Array.isArray(q.orders)) { 143 | it = q.orders.reduce((it, f) => sort(it, f), it) 144 | } 145 | 146 | const { offset, limit } = q 147 | if (offset != null) { 148 | let i = 0 149 | it = filter(it, () => i++ >= offset) 150 | } 151 | 152 | if (limit != null) { 153 | it = take(it, limit) 154 | } 155 | 156 | return it 157 | } 158 | 159 | queryKeys (q: KeyQuery): AsyncIterable { 160 | let it = map(this._query({ 161 | values: false, 162 | prefix: q.prefix 163 | }), ({ key }) => key) 164 | 165 | if (Array.isArray(q.filters)) { 166 | it = q.filters.reduce((it, f) => filter(it, f), it) 167 | } 168 | 169 | if (Array.isArray(q.orders)) { 170 | it = q.orders.reduce((it, f) => sort(it, f), it) 171 | } 172 | 173 | const { offset, limit } = q 174 | if (offset != null) { 175 | let i = 0 176 | it = filter(it, () => i++ >= offset) 177 | } 178 | 179 | if (limit != null) { 180 | it = take(it, limit) 181 | } 182 | 183 | return it 184 | } 185 | 186 | _query (opts: { values: boolean, prefix?: string }): AsyncIterable { 187 | const iteratorOpts: IteratorOptions = { 188 | keys: true, 189 | keyEncoding: 'buffer', 190 | values: opts.values 191 | } 192 | 193 | // Let the db do the prefix matching 194 | if (opts.prefix != null) { 195 | const prefix = opts.prefix.toString() 196 | // Match keys greater than or equal to `prefix` and 197 | iteratorOpts.gte = prefix 198 | // less than `prefix` + \xFF (hex escape sequence) 199 | iteratorOpts.lt = prefix + '\xFF' 200 | } 201 | 202 | const iterator = this.db.iterator(iteratorOpts) 203 | 204 | if (iterator[Symbol.asyncIterator] != null) { 205 | return levelIteratorToIterator(iterator) 206 | } 207 | 208 | // @ts-expect-error support older level 209 | if (iterator.next != null && iterator.end != null) { 210 | // @ts-expect-error support older level 211 | return oldLevelIteratorToIterator(iterator) 212 | } 213 | 214 | throw new Error('Level returned incompatible iterator') 215 | } 216 | } 217 | 218 | async function * levelIteratorToIterator (li: AsyncIterable<[string, Uint8Array]> & { close: () => Promise }): AsyncIterable { 219 | for await (const [key, value] of li) { 220 | yield { key: new Key(key, false), value } 221 | } 222 | 223 | await li.close() 224 | } 225 | 226 | interface OldLevelIterator { 227 | next: (cb: (err: Error, key: string | Uint8Array | null, value: any) => void) => void 228 | end: (cb: (err: Error) => void) => void 229 | } 230 | 231 | function oldLevelIteratorToIterator (li: OldLevelIterator): AsyncIterable { 232 | return { 233 | [Symbol.asyncIterator] () { 234 | return { 235 | next: async () => await new Promise((resolve, reject) => { 236 | li.next((err, key, value) => { 237 | if (err != null) { 238 | reject(err); return 239 | } 240 | if (key == null) { 241 | li.end(err => { 242 | if (err != null) { 243 | reject(err) 244 | return 245 | } 246 | resolve({ done: true, value: undefined }) 247 | }); return 248 | } 249 | resolve({ done: false, value: { key: new Key(key, false), value } }) 250 | }) 251 | }), 252 | return: async () => await new Promise((resolve, reject) => { 253 | li.end(err => { 254 | if (err != null) { 255 | reject(err); return 256 | } 257 | resolve({ done: true, value: undefined }) 258 | }) 259 | }) 260 | } 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /test/browser.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { MountDatastore } from 'datastore-core' 4 | import { Key } from 'interface-datastore/key' 5 | import { LevelDatastore } from '../src/index.js' 6 | import { interfaceDatastoreTests } from 'interface-datastore-tests' 7 | 8 | describe('LevelDatastore', () => { 9 | describe('interface-datastore (leveljs)', () => { 10 | interfaceDatastoreTests({ 11 | setup: () => new LevelDatastore(`hello-${Math.random()}`), 12 | teardown: () => {} 13 | }) 14 | }) 15 | 16 | describe('interface-datastore (mount(leveljs, leveljs, leveljs))', () => { 17 | interfaceDatastoreTests({ 18 | setup () { 19 | return new MountDatastore([{ 20 | prefix: new Key('/a'), 21 | datastore: new LevelDatastore(`one-${Math.random()}`) 22 | }, { 23 | prefix: new Key('/q'), 24 | datastore: new LevelDatastore(`two-${Math.random()}`) 25 | }, { 26 | prefix: new Key('/z'), 27 | datastore: new LevelDatastore(`three-${Math.random()}`) 28 | }]) 29 | }, 30 | teardown () {} 31 | }) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /test/fixtures/test-level-iterator-destroy.ts: -------------------------------------------------------------------------------- 1 | import { Key } from 'interface-datastore/key' 2 | import { LevelDatastore } from '../../src/index.js' 3 | import tempdir from 'ipfs-utils/src/temp-dir.js' 4 | 5 | async function testLevelIteratorDestroy (): Promise { 6 | const store = new LevelDatastore(tempdir()) 7 | await store.open() 8 | await store.put(new Key(`/test/key${Date.now()}`), new TextEncoder().encode(`TESTDATA${Date.now()}`)) 9 | for await (const d of store.query({})) { 10 | console.log(d) // eslint-disable-line no-console 11 | } 12 | } 13 | 14 | // Will exit with: 15 | // > Assertion failed: (ended_), function ~Iterator, file ../binding.cc, line 546. 16 | // If iterator gets destroyed (in c++ land) and .end() was not called on it. 17 | void testLevelIteratorDestroy() 18 | -------------------------------------------------------------------------------- /test/index.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { expect } from 'aegir/chai' 4 | import { MemoryLevel } from 'memory-level' 5 | import { Level } from 'level' 6 | import { LevelDatastore } from '../src/index.js' 7 | import tempdir from 'ipfs-utils/src/temp-dir.js' 8 | import { interfaceDatastoreTests } from 'interface-datastore-tests' 9 | 10 | describe('LevelDatastore', () => { 11 | describe('initialization', () => { 12 | it('should default to a leveldown database', async () => { 13 | const levelStore = new LevelDatastore(`${tempdir()}/init-default-${Date.now()}`) 14 | await levelStore.open() 15 | 16 | expect(levelStore.db).to.be.an.instanceOf(Level) 17 | }) 18 | 19 | it('should be able to override the database', async () => { 20 | const levelStore = new LevelDatastore( 21 | // @ts-expect-error MemoryLevel does not implement the same interface as Level 22 | new MemoryLevel({ 23 | keyEncoding: 'utf8', 24 | valueEncoding: 'view' 25 | }) 26 | ) 27 | 28 | await levelStore.open() 29 | 30 | expect(levelStore.db).to.be.an.instanceOf(MemoryLevel) 31 | }) 32 | }) 33 | 34 | describe('interface-datastore MemoryLevel', () => { 35 | interfaceDatastoreTests({ 36 | async setup () { 37 | const store = new LevelDatastore( 38 | // @ts-expect-error MemoryLevel does not implement the same interface as Level 39 | new MemoryLevel({ 40 | keyEncoding: 'utf8', 41 | valueEncoding: 'view' 42 | }) 43 | ) 44 | await store.open() 45 | 46 | return store 47 | }, 48 | teardown () {} 49 | }) 50 | }) 51 | 52 | describe('interface-datastore Level', () => { 53 | interfaceDatastoreTests({ 54 | async setup () { 55 | const store = new LevelDatastore(tempdir()) 56 | await store.open() 57 | 58 | return store 59 | }, 60 | teardown () {} 61 | }) 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /test/node.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { expect } from 'aegir/chai' 4 | import path from 'path' 5 | import { Key } from 'interface-datastore/key' 6 | import { MountDatastore } from 'datastore-core' 7 | import childProcess from 'child_process' 8 | import { interfaceDatastoreTests } from 'interface-datastore-tests' 9 | import { LevelDatastore } from '../src/index.js' 10 | import tempdir from 'ipfs-utils/src/temp-dir.js' 11 | 12 | describe('LevelDatastore', () => { 13 | describe('interface-datastore (leveldown)', () => { 14 | interfaceDatastoreTests({ 15 | async setup () { 16 | const store = new LevelDatastore(tempdir()) 17 | await store.open() 18 | 19 | return store 20 | }, 21 | teardown () {} 22 | }) 23 | }) 24 | 25 | describe('interface-datastore (mount(leveldown, leveldown, leveldown))', () => { 26 | interfaceDatastoreTests({ 27 | async setup () { 28 | return new MountDatastore( 29 | await Promise.all( 30 | ['/a', '/q', '/z'].map(async prefix => { 31 | const datastore = new LevelDatastore(tempdir()) 32 | await datastore.open() 33 | 34 | return { 35 | prefix: new Key(prefix), 36 | datastore 37 | } 38 | }) 39 | ) 40 | ) 41 | }, 42 | async teardown () { 43 | 44 | } 45 | }) 46 | }) 47 | 48 | // The `.end()` method MUST be called on LevelDB iterators or they remain open, 49 | // leaking memory. 50 | // 51 | // This test exposes this problem by causing an error to be thrown on process 52 | // exit when an iterator is open AND leveldb is not closed. 53 | // 54 | // Normally when leveldb is closed it'll automatically clean up open iterators 55 | // but if you don't close the store this error will occur: 56 | // 57 | // > Assertion failed: (ended_), function ~Iterator, file ../binding.cc, line 546. 58 | // 59 | // This is thrown by a destructor function for iterator objects that asserts 60 | // the iterator has ended before cleanup. 61 | // 62 | // https://github.com/Level/leveldown/blob/d3453fbde4d2a8aa04d9091101c25c999649069b/binding.cc#L545 63 | it('should not leave iterators open and leak memory', (done) => { 64 | const cp = childProcess.fork(path.join(process.cwd(), '/dist/test/fixtures/test-level-iterator-destroy'), { stdio: 'pipe' }) 65 | 66 | let out = '' 67 | const { stdout, stderr } = cp 68 | stdout?.on('data', d => { out = `${out}${d}` }) 69 | stderr?.on('data', d => { out = `${out}${d}` }) 70 | 71 | cp.on('exit', code => { 72 | expect(code).to.equal(0) 73 | expect(out).to.not.include('Assertion failed: (ended_)') 74 | done() 75 | }) 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "aegir/src/config/tsconfig.aegir.json", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": [ 7 | "test", 8 | "src" 9 | ] 10 | } 11 | --------------------------------------------------------------------------------