├── .airtap.yml ├── .github ├── dependabot.yml └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── UPGRADING.md ├── index.js ├── package.json └── test └── index.js /.airtap.yml: -------------------------------------------------------------------------------- 1 | providers: 2 | - airtap-playwright 3 | 4 | browsers: 5 | - name: chromium 6 | - name: firefox 7 | - name: webkit -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: / 5 | schedule: 6 | interval: monthly 7 | ignore: 8 | - dependency-name: dependency-check 9 | - package-ecosystem: github-actions 10 | directory: / 11 | schedule: 12 | interval: monthly 13 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: ['*'] 5 | permissions: 6 | contents: write 7 | jobs: 8 | release: 9 | name: Release 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | - name: Create GitHub release 15 | uses: docker://antonyurchenko/git-release:v4 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | node: [10, 12, 14] 9 | name: Node ${{ matrix.node }} 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | - name: Use node ${{ matrix.node }} 14 | uses: actions/setup-node@v3 15 | with: 16 | node-version: ${{ matrix.node }} 17 | - name: Install 18 | run: npm install 19 | - name: Test 20 | run: npm test 21 | - name: Coverage 22 | run: npm run coverage 23 | - name: Codecov 24 | uses: codecov/codecov-action@v3 25 | with: 26 | file: coverage/lcov.info 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output/ 3 | coverage/ 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [7.1.0] - 2021-09-30 4 | 5 | ### Added 6 | 7 | - Add `db.getMany(keys)` ([#102](https://github.com/Level/encoding-down/issues/102)) ([`4038a30`](https://github.com/Level/encoding-down/commit/4038a30)) (Vincent Weevers). 8 | 9 | ## [7.0.0] - 2021-04-09 10 | 11 | _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ 12 | 13 | ### Changed 14 | 15 | - **Breaking:** drop node 6 and 8 ([Level/community#98](https://github.com/Level/community/issues/98)) ([`5c6752f`](https://github.com/Level/encoding-down/commit/5c6752f)) (Vincent Weevers) 16 | - **Breaking:** modernize syntax and bump `standard` ([Level/community#98](https://github.com/Level/community/issues/98)) ([`404f20f`](https://github.com/Level/encoding-down/commit/404f20f)) (Vincent Weevers) 17 | - Bump `abstract-leveldown`, `level-codec` and `level-errors` ([`83556fd`](https://github.com/Level/encoding-down/commit/83556fd)) (Vincent Weevers) 18 | - Add `files` to `package.json` ([`103fe95`](https://github.com/Level/encoding-down/commit/103fe95)) (Vincent Weevers) 19 | 20 | ### Added 21 | 22 | - Support encoding options on chained batch `put()` and `del()` ([`9690e52`](https://github.com/Level/encoding-down/commit/9690e52)) ([Level/levelup#633](https://github.com/Level/levelup/issues/633)) (Vincent Weevers) 23 | 24 | ### Removed 25 | 26 | - Remove default export ([Level/community#87](https://github.com/Level/community/issues/87)) ([`1111866`](https://github.com/Level/encoding-down/commit/1111866)) (Vincent Weevers) 27 | 28 | ## [6.3.0] - 2019-10-13 29 | 30 | ### Added 31 | 32 | - Add manifest ([Level/community#83](https://github.com/Level/community/issues/83)) and encode `compactRange()` ([#93](https://github.com/Level/encoding-down/issues/93)) ([**@vweevers**](https://github.com/vweevers)) 33 | - Add `type` property for `reachdown` ([Level/community#82](https://github.com/Level/community/issues/82)) ([`8a23848`](https://github.com/Level/encoding-down/commit/8a23848)) ([**@vweevers**](https://github.com/vweevers)) 34 | 35 | ## [6.2.0] - 2019-09-06 36 | 37 | ### Changed 38 | 39 | - Upgrade `hallmark` devDependency from `^0.1.0` to `^2.0.0` ([#85](https://github.com/Level/encoding-down/issues/85), [#91](https://github.com/Level/encoding-down/issues/91)) ([**@vweevers**](https://github.com/vweevers)) 40 | - Upgrade `standard` devDependency from `^12.0.0` to `^14.0.0` ([#84](https://github.com/Level/encoding-down/issues/84), [#90](https://github.com/Level/encoding-down/issues/90)) ([**@vweevers**](https://github.com/vweevers)) 41 | - Upgrade `memdown` devDependency from `^4.0.0` to `^5.0.0` ([#88](https://github.com/Level/encoding-down/issues/88)) ([**@vweevers**](https://github.com/vweevers)) 42 | 43 | ### Added 44 | 45 | - Support `db.clear()` ([#89](https://github.com/Level/encoding-down/issues/89)) ([**@vweevers**](https://github.com/vweevers)) 46 | 47 | ## [6.1.0] - 2019-06-22 48 | 49 | ### Changed 50 | 51 | - Upgrade `nyc` devDependency from `^13.2.0` to `^14.0.0` ([#81](https://github.com/Level/encoding-down/issues/81))) ([**@vweevers**](https://github.com/vweevers)) 52 | 53 | ### Added 54 | 55 | - Support seeking ([#82](https://github.com/Level/encoding-down/issues/82), [#83](https://github.com/Level/encoding-down/issues/83)) ([**@MeirionHughes**](https://github.com/MeirionHughes), [**@vweevers**](https://github.com/vweevers)) 56 | 57 | ## [6.0.2] - 2019-03-31 58 | 59 | ### Changed 60 | 61 | - Upgrade `memdown` devDependency from `^3.0.0` to `^4.0.0` ([#80](https://github.com/Level/encoding-down/issues/80)) ([**@vweevers**](https://github.com/vweevers)) 62 | - Upgrade `nyc` devDependency from `^12.0.2` to `^13.2.0` ([#79](https://github.com/Level/encoding-down/issues/79)) ([**@vweevers**](https://github.com/vweevers)) 63 | - Apply common project tweaks ([#77](https://github.com/Level/encoding-down/issues/77), [#78](https://github.com/Level/encoding-down/issues/78)) ([**@vweevers**](https://github.com/vweevers)) 64 | 65 | ## [6.0.1] - 2018-12-27 66 | 67 | ### Changed 68 | 69 | - Replace `remark-cli` devDependency with `hallmark` ([#76](https://github.com/Level/encoding-down/issues/76)) ([**@vweevers**](https://github.com/vweevers)) 70 | 71 | ### Added 72 | 73 | - Increase coverage to 100% ([#75](https://github.com/Level/encoding-down/issues/75)) ([**@vweevers**](https://github.com/vweevers)) 74 | 75 | ### Fixed 76 | 77 | - Fix `approximateSize()` to encode `start` and `end` arguments ([#75](https://github.com/Level/encoding-down/issues/75)) ([**@vweevers**](https://github.com/vweevers)) 78 | 79 | ## [6.0.0] - 2018-12-25 80 | 81 | _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ 82 | 83 | ### Changed 84 | 85 | - Prefer `const` over `var` in README ([**@ralphtheninja**](https://github.com/ralphtheninja)) 86 | - Upgrade `abstract-leveldown` dependency from `^5.0.0` to `^v6.0.0` ([#68](https://github.com/Level/encoding-down/issues/68)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) 87 | - Upgrade `standard` devDependency from `^11.0.0` to `^v12.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 88 | - Use full link references in README ([#60](https://github.com/Level/encoding-down/issues/60)) ([**@vweevers**](https://github.com/vweevers)) 89 | 90 | ### Added 91 | 92 | - Explain serialization ([#72](https://github.com/Level/encoding-down/issues/72)) ([**@vweevers**](https://github.com/vweevers)) 93 | - Add `nyc` and `coveralls` ([#64](https://github.com/Level/encoding-down/issues/64)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) 94 | 95 | ### Removed 96 | 97 | - Remove node 9 ([**@ralphtheninja**](https://github.com/ralphtheninja)) 98 | - Remove now superfluous `_setupIteratorOptions()` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 99 | 100 | ## [5.0.4] - 2018-06-22 101 | 102 | ### Added 103 | 104 | - Add `LICENSE.md` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 105 | - Add `CONTRIBUTORS.md` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 106 | - Add `remark` tooling ([**@ralphtheninja**](https://github.com/ralphtheninja)) 107 | 108 | ## [5.0.3] - 2018-05-30 109 | 110 | ### Changed 111 | 112 | - Replace `util.inherits` with `inherits` module ([**@ralphtheninja**](https://github.com/ralphtheninja)) 113 | 114 | ## [5.0.2] - 2018-05-23 115 | 116 | ### Added 117 | 118 | - Add `UPGRADING.md` ([**@vweevers**](https://github.com/vweevers)) 119 | 120 | ### Changed 121 | 122 | - Upgrade `abstract-leveldown` to `5.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 123 | - Upgrade `memdown` to `3.0.0` ([**@vweevers**](https://github.com/vweevers)) 124 | 125 | ## [5.0.1] - 2018-05-19 126 | 127 | ### Changed 128 | 129 | - Override `_setupIteratorOptions` to not clobber ranges ([**@ralphtheninja**](https://github.com/ralphtheninja), [**@dominictarr**](https://github.com/dominictarr)) 130 | 131 | ## [5.0.0] - 2018-05-13 132 | 133 | _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ 134 | 135 | ### Added 136 | 137 | - Add 10 to Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 138 | 139 | ### Changed 140 | 141 | - Update `level-errors` to `2.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 142 | - Update `level-codec` to `9.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 143 | 144 | ### Removed 145 | 146 | - Remove 4 from Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 147 | 148 | ## [4.0.1] - 2018-05-19 149 | 150 | ### Changed 151 | 152 | - Override `_setupIteratorOptions` to not clobber ranges ([**@ralphtheninja**](https://github.com/ralphtheninja), [**@dominictarr**](https://github.com/dominictarr)) 153 | 154 | ## [4.0.0] - 2018-02-12 155 | 156 | _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ 157 | 158 | ### Added 159 | 160 | - Add 9 to Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 161 | 162 | ### Changed 163 | 164 | - Update `abstract-leveldown` to `4.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 165 | - Update `memdown` to `2.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 166 | 167 | ### Removed 168 | 169 | - Remove 7 from Travis ([**@ralphtheninja**](https://github.com/ralphtheninja)) 170 | 171 | ## [3.0.1] - 2017-12-18 172 | 173 | ### Added 174 | 175 | - Test that default utf8 encoding stringifies numbers ([**@vweevers**](https://github.com/vweevers)) 176 | 177 | ### Fixed 178 | 179 | - Skip decoding if `options.keys` or `options.values` is false ([**@vweevers**](https://github.com/vweevers)) 180 | 181 | ## [3.0.0] - 2017-11-11 182 | 183 | _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ 184 | 185 | ### Added 186 | 187 | - README: add node badge (>= 4) ([**@vweevers**](https://github.com/vweevers)) 188 | 189 | ### Changed 190 | 191 | - Update `abstract-leveldown` to `3.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 192 | 193 | ### Removed 194 | 195 | - Remove 0.12 from Travis ([**@vweevers**](https://github.com/vweevers)) 196 | 197 | ## [2.3.4] - 2017-10-24 198 | 199 | ### Added 200 | 201 | - README: add example of npm installed encoding ([**@vweevers**](https://github.com/vweevers)) 202 | 203 | ## [2.3.3] - 2017-10-22 204 | 205 | ### Changed 206 | 207 | - README: fix `level-codec` links ([**@vweevers**](https://github.com/vweevers)) 208 | 209 | ## [2.3.2] - 2017-10-22 210 | 211 | ### Changed 212 | 213 | - README: tweak badges ([**@ralphtheninja**](https://github.com/ralphtheninja)) 214 | - README: add more code examples ([**@vweevers**](https://github.com/vweevers)) 215 | - Update `level-codec` to `8.0.0` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 216 | 217 | ### Fixed 218 | 219 | - Fix problems related to missing `asBuffer`, `keyAsBuffer` and `valueAsBuffer` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 220 | 221 | ## [2.3.1] - 2017-10-02 222 | 223 | ### Changed 224 | 225 | - Refactor typings ([**@MeirionHughes**](https://github.com/MeirionHughes)) 226 | 227 | ## [2.3.0] - 2017-09-24 228 | 229 | ### Added 230 | 231 | - Add default export ([**@zixia**](https://github.com/zixia)) 232 | 233 | ## [2.2.1] - 2017-09-13 234 | 235 | ### Fixed 236 | 237 | - Fix typings ([**@MeirionHughes**](https://github.com/MeirionHughes)) 238 | 239 | ## [2.2.0] - 2017-09-12 240 | 241 | ### Added 242 | 243 | - Add Typescript typings ([**@MeirionHughes**](https://github.com/MeirionHughes)) 244 | 245 | ### Changed 246 | 247 | - README: `AbstractLevelDOWN` -> `abstract-leveldown` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 248 | - Update `abstract-leveldown` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 249 | 250 | ## [2.1.5] - 2017-08-18 251 | 252 | ### Added 253 | 254 | - README: add api docs ([**@ralphtheninja**](https://github.com/ralphtheninja)) 255 | - Add basic tests ([**@ralphtheninja**](https://github.com/ralphtheninja)) 256 | 257 | ### Changed 258 | 259 | - Enable Travis for ci ([**@ralphtheninja**](https://github.com/ralphtheninja)) 260 | - Update dependencies ([**@ralphtheninja**](https://github.com/ralphtheninja)) 261 | - Use `safe-buffer` ([**@ralphtheninja**](https://github.com/ralphtheninja)) 262 | 263 | ## [2.1.4] - 2017-01-26 264 | 265 | ### Fixed 266 | 267 | - Rename methods to `_serializeKey()` and `_serializeValue()` ([**@juliangruber**](https://github.com/juliangruber)) 268 | 269 | ## [2.1.3] - 2017-01-26 270 | 271 | ### Added 272 | 273 | - Add `_encodeKey()` and `_encodeValue()` id functions ([**@juliangruber**](https://github.com/juliangruber)) 274 | 275 | ## [2.1.2] - 2017-01-26 276 | 277 | ### Fixed 278 | 279 | - Emit encoding errors in streams too ([**@juliangruber**](https://github.com/juliangruber)) 280 | 281 | ## [2.1.1] - 2017-01-26 282 | 283 | ### Fixed 284 | 285 | - Return encoding errors on get ([**@juliangruber**](https://github.com/juliangruber)) 286 | 287 | ## [2.1.0] - 2017-01-26 288 | 289 | ### Added 290 | 291 | - Add support for `approximateSize()` ([**@juliangruber**](https://github.com/juliangruber)) 292 | 293 | ## [2.0.8] - 2017-01-26 294 | 295 | ### Removed 296 | 297 | - Remove `Iterator.prototype.seek` ([**@juliangruber**](https://github.com/juliangruber)) 298 | 299 | ### Fixed 300 | 301 | - Fix encoding lt/get range options ([**@juliangruber**](https://github.com/juliangruber)) 302 | 303 | ## [2.0.7] - 2017-01-26 304 | 305 | ### Added 306 | 307 | - Add `'utf8'` as default encoding ([**@juliangruber**](https://github.com/juliangruber)) 308 | 309 | ## [2.0.6] - 2017-01-26 310 | 311 | ### Fixed 312 | 313 | - Fix `typof` -> `typeof` bug ([**@juliangruber**](https://github.com/juliangruber)) 314 | 315 | ## [2.0.5] - 2017-01-26 316 | 317 | ### Fixed 318 | 319 | - Fix bug in `iterator._next()` with undefined key or value ([**@juliangruber**](https://github.com/juliangruber)) 320 | 321 | ## [2.0.4] - 2017-01-26 322 | 323 | ### Changed 324 | 325 | - Update `level-codec` for utf8 fixes ([**@juliangruber**](https://github.com/juliangruber)) 326 | 327 | ## [2.0.3] - 2017-01-26 328 | 329 | ### Fixed 330 | 331 | - Fix bug with incorrect db ([**@juliangruber**](https://github.com/juliangruber)) 332 | 333 | ## [2.0.2] - 2017-01-26 334 | 335 | ### Fixed 336 | 337 | - Fix bug with incorrect db and missing new operator ([**@juliangruber**](https://github.com/juliangruber)) 338 | 339 | ## [2.0.1] - 2017-01-26 340 | 341 | ### Fixed 342 | 343 | - Fix bug with `AbstractChainedBatch` inheritance ([**@juliangruber**](https://github.com/juliangruber)) 344 | 345 | ## [2.0.0] - 2017-01-26 346 | 347 | ### Changed 348 | 349 | - Version bump ([**@juliangruber**](https://github.com/juliangruber)) 350 | 351 | ## [1.0.0] - 2017-01-26 352 | 353 | :seedling: Initial release. 354 | 355 | [7.1.0]: https://github.com/Level/encoding-down/releases/tag/v7.1.0 356 | 357 | [7.0.0]: https://github.com/Level/encoding-down/releases/tag/v7.0.0 358 | 359 | [6.3.0]: https://github.com/Level/encoding-down/releases/tag/v6.3.0 360 | 361 | [6.2.0]: https://github.com/Level/encoding-down/releases/tag/v6.2.0 362 | 363 | [6.1.0]: https://github.com/Level/encoding-down/releases/tag/v6.1.0 364 | 365 | [6.0.2]: https://github.com/Level/encoding-down/releases/tag/v6.0.2 366 | 367 | [6.0.1]: https://github.com/Level/encoding-down/releases/tag/v6.0.1 368 | 369 | [6.0.0]: https://github.com/Level/encoding-down/releases/tag/v6.0.0 370 | 371 | [5.0.4]: https://github.com/Level/encoding-down/releases/tag/v5.0.4 372 | 373 | [5.0.3]: https://github.com/Level/encoding-down/releases/tag/v5.0.3 374 | 375 | [5.0.2]: https://github.com/Level/encoding-down/releases/tag/v5.0.2 376 | 377 | [5.0.1]: https://github.com/Level/encoding-down/releases/tag/v5.0.1 378 | 379 | [5.0.0]: https://github.com/Level/encoding-down/releases/tag/v5.0.0 380 | 381 | [4.0.1]: https://github.com/Level/encoding-down/releases/tag/v4.0.1 382 | 383 | [4.0.0]: https://github.com/Level/encoding-down/releases/tag/v4.0.0 384 | 385 | [3.0.1]: https://github.com/Level/encoding-down/releases/tag/v3.0.1 386 | 387 | [3.0.0]: https://github.com/Level/encoding-down/releases/tag/v3.0.0 388 | 389 | [2.3.4]: https://github.com/Level/encoding-down/releases/tag/v2.3.4 390 | 391 | [2.3.3]: https://github.com/Level/encoding-down/releases/tag/v2.3.3 392 | 393 | [2.3.2]: https://github.com/Level/encoding-down/releases/tag/v2.3.2 394 | 395 | [2.3.1]: https://github.com/Level/encoding-down/releases/tag/v2.3.1 396 | 397 | [2.3.0]: https://github.com/Level/encoding-down/releases/tag/v2.3.0 398 | 399 | [2.2.1]: https://github.com/Level/encoding-down/releases/tag/v2.2.1 400 | 401 | [2.2.0]: https://github.com/Level/encoding-down/releases/tag/v2.2.0 402 | 403 | [2.1.5]: https://github.com/Level/encoding-down/releases/tag/v2.1.5 404 | 405 | [2.1.4]: https://github.com/Level/encoding-down/releases/tag/v2.1.4 406 | 407 | [2.1.3]: https://github.com/Level/encoding-down/releases/tag/v2.1.3 408 | 409 | [2.1.2]: https://github.com/Level/encoding-down/releases/tag/v2.1.2 410 | 411 | [2.1.1]: https://github.com/Level/encoding-down/releases/tag/v2.1.1 412 | 413 | [2.1.0]: https://github.com/Level/encoding-down/releases/tag/v2.1.0 414 | 415 | [2.0.8]: https://github.com/Level/encoding-down/releases/tag/v2.0.8 416 | 417 | [2.0.7]: https://github.com/Level/encoding-down/releases/tag/v2.0.7 418 | 419 | [2.0.6]: https://github.com/Level/encoding-down/releases/tag/v2.0.6 420 | 421 | [2.0.5]: https://github.com/Level/encoding-down/releases/tag/v2.0.5 422 | 423 | [2.0.4]: https://github.com/Level/encoding-down/releases/tag/v2.0.4 424 | 425 | [2.0.3]: https://github.com/Level/encoding-down/releases/tag/v2.0.3 426 | 427 | [2.0.2]: https://github.com/Level/encoding-down/releases/tag/v2.0.2 428 | 429 | [2.0.1]: https://github.com/Level/encoding-down/releases/tag/v2.0.1 430 | 431 | [2.0.0]: https://github.com/Level/encoding-down/releases/tag/v2.0.0 432 | 433 | [1.0.0]: https://github.com/Level/encoding-down/releases/tag/v1.0.0 434 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2012 The contributors to encoding-down. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # encoding-down 2 | 3 | **Superseded by [`abstract-level`](https://github.com/Level/abstract-level). Please see [Frequently Asked Questions](https://github.com/Level/community#faq).** 4 | 5 | ## Introduction 6 | 7 | Stores like [`leveldown`][leveldown] can only store strings and Buffers. Other types, though accepted, are [_serialized_](https://github.com/Level/abstract-leveldown#db_serializekeykey) before storage, which is an irreversible type conversion. For a richer set of data types you can wrap such a store with `encoding-down`. It allows you to specify an _encoding_ to use for keys and values independently. This not only widens the range of input types, but also limits the range of output types. The encoding is applied to all read and write operations: it encodes writes and decodes reads. 8 | 9 | [Many encodings are builtin][builtin-encodings] courtesy of [`level-codec`][level-codec]. The default encoding is `utf8` which ensures you'll always get back a string. You can also provide a custom encoding like `bytewise` - [or your own](#custom-encodings)! 10 | 11 | ## Usage 12 | 13 | Without any options, `encoding-down` defaults to the `utf8` encoding. 14 | 15 | ```js 16 | const levelup = require('levelup') 17 | const leveldown = require('leveldown') 18 | const encode = require('encoding-down') 19 | 20 | const db = levelup(encode(leveldown('./db1'))) 21 | 22 | db.put('example', Buffer.from('encoding-down'), function (err) { 23 | db.get('example', function (err, value) { 24 | console.log(typeof value, value) // 'string encoding-down' 25 | }) 26 | }) 27 | ``` 28 | 29 | Can we store objects? Yes! 30 | 31 | ```js 32 | const db = levelup(encode(leveldown('./db2'), { valueEncoding: 'json' })) 33 | 34 | db.put('example', { awesome: true }, function (err) { 35 | db.get('example', function (err, value) { 36 | console.log(value) // { awesome: true } 37 | console.log(typeof value) // 'object' 38 | }) 39 | }) 40 | ``` 41 | 42 | How about storing Buffers, but getting back a hex-encoded string? 43 | 44 | ```js 45 | const db = levelup(encode(leveldown('./db3'), { valueEncoding: 'hex' })) 46 | 47 | db.put('example', Buffer.from([0, 255]), function (err) { 48 | db.get('example', function (err, value) { 49 | console.log(typeof value, value) // 'string 00ff' 50 | }) 51 | }) 52 | ``` 53 | 54 | What if we previously stored binary data? 55 | 56 | ```js 57 | const db = levelup(encode(leveldown('./db4'), { valueEncoding: 'binary' })) 58 | 59 | db.put('example', Buffer.from([0, 255]), function (err) { 60 | db.get('example', function (err, value) { 61 | console.log(typeof value, value) // 'object ' 62 | }) 63 | 64 | // Override the encoding for this operation 65 | db.get('example', { valueEncoding: 'base64' }, function (err, value) { 66 | console.log(typeof value, value) // 'string AP8=' 67 | }) 68 | }) 69 | ``` 70 | 71 | And what about keys? 72 | 73 | ```js 74 | const db = levelup(encode(leveldown('./db5'), { keyEncoding: 'json' })) 75 | 76 | db.put({ awesome: true }, 'example', function (err) { 77 | db.get({ awesome: true }, function (err, value) { 78 | console.log(value) // 'example' 79 | }) 80 | }) 81 | ``` 82 | 83 | ```js 84 | const db = levelup(encode(leveldown('./db6'), { keyEncoding: 'binary' })) 85 | 86 | db.put(Buffer.from([0, 255]), 'example', function (err) { 87 | db.get('00ff', { keyEncoding: 'hex' }, function (err, value) { 88 | console.log(value) // 'example' 89 | }) 90 | }) 91 | ``` 92 | 93 | ## Usage with [`level`][level] 94 | 95 | The [`level`][level] module conveniently bundles `encoding-down` and passes its `options` to `encoding-down`. This means you can simply do: 96 | 97 | ```js 98 | const level = require('level') 99 | const db = level('./db7', { valueEncoding: 'json' }) 100 | 101 | db.put('example', 42, function (err) { 102 | db.get('example', function (err, value) { 103 | console.log(value) // 42 104 | console.log(typeof value) // 'number' 105 | }) 106 | }) 107 | ``` 108 | 109 | ## API 110 | 111 | ### `db = require('encoding-down')(db[, options])` 112 | 113 | - `db` must be an [`abstract-leveldown`][abstract-leveldown] compliant store 114 | - `options` are passed to [`level-codec`][level-codec]: 115 | - `keyEncoding`: encoding to use for keys 116 | - `valueEncoding`: encoding to use for values 117 | 118 | Both encodings default to `'utf8'`. They can be a string (builtin `level-codec` encoding) or an object (custom encoding). 119 | 120 | ## Custom encodings 121 | 122 | Please refer to [`level-codec` documentation][encoding-format] for a precise description of the format. Here's a quick example with `level` and `async/await` just for fun: 123 | 124 | ```js 125 | const level = require('level') 126 | const lexint = require('lexicographic-integer') 127 | 128 | async function main () { 129 | const db = level('./db8', { 130 | keyEncoding: { 131 | type: 'lexicographic-integer', 132 | encode: (n) => lexint.pack(n, 'hex'), 133 | decode: lexint.unpack, 134 | buffer: false 135 | } 136 | }) 137 | 138 | await db.put(2, 'example') 139 | await db.put(10, 'example') 140 | 141 | // Without our encoding, the keys would sort as 10, 2. 142 | db.createKeyStream().on('data', console.log) // 2, 10 143 | } 144 | 145 | main() 146 | ``` 147 | 148 | With an npm-installed encoding (modularity ftw!) we can reduce the above to: 149 | 150 | ```js 151 | const level = require('level') 152 | const lexint = require('lexicographic-integer-encoding')('hex') 153 | 154 | const db = level('./db8', { 155 | keyEncoding: lexint 156 | }) 157 | ``` 158 | 159 | ## Contributing 160 | 161 | [`Level/encoding-down`](https://github.com/Level/encoding-down) is an **OPEN Open Source Project**. This means that: 162 | 163 | > Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. 164 | 165 | See the [Contribution Guide](https://github.com/Level/community/blob/master/CONTRIBUTING.md) for more details. 166 | 167 | ## Donate 168 | 169 | Support us with a monthly donation on [Open Collective](https://opencollective.com/level) and help us continue our work. 170 | 171 | ## License 172 | 173 | [MIT](LICENSE) 174 | 175 | [level-badge]: https://leveljs.org/img/badge.svg 176 | 177 | [abstract-leveldown]: https://github.com/Level/abstract-leveldown 178 | 179 | [leveldown]: https://github.com/Level/leveldown 180 | 181 | [level]: https://github.com/Level/level 182 | 183 | [level-codec]: https://github.com/Level/codec 184 | 185 | [builtin-encodings]: https://github.com/Level/codec#builtin-encodings 186 | 187 | [encoding-format]: https://github.com/Level/codec#encoding-format 188 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | # Upgrade Guide 2 | 3 | This document describes breaking changes and how to upgrade. For a complete list of changes including minor and patch releases, please refer to the [changelog](CHANGELOG.md). 4 | 5 | ## 7.0.0 6 | 7 | Legacy range options have been removed ([Level/community#86](https://github.com/Level/community/issues/86)). If you previously did: 8 | 9 | ```js 10 | db.iterator({ start: 'a', end: 'z' }) 11 | ``` 12 | 13 | An error would now be thrown and you must instead do: 14 | 15 | ```js 16 | db.iterator({ gte: 'a', lte: 'z' }) 17 | ``` 18 | 19 | This release also drops support of legacy runtime environments ([Level/community#98](https://github.com/Level/community/issues/98)): 20 | 21 | - Node.js 6 and 8 22 | - Internet Explorer 11 23 | - Safari 9-11 24 | - Stock Android browser (AOSP). 25 | 26 | ## 6.0.0 27 | 28 | Upgraded `abstract-leveldown` to `v6.0.0`. Please see the corresponding [changelog entry](https://github.com/Level/abstract-leveldown/blob/master/CHANGELOG.md#600---2018-10-20) for more information. 29 | 30 | ## 5.0.0 31 | 32 | Dropped support for node 4. No other breaking changes. 33 | 34 | ## 4.0.0 35 | 36 | Dropped support for node 7. 37 | 38 | #### `.batch(array)` enforces objects 39 | 40 | This major release contains an upgrade to `abstract-leveldown` with a [breaking change](https://github.com/Level/abstract-leveldown/commit/a2621ad70571f6ade9d2be42632ece042e068805) for the array version of `.batch()`. This change ensures all elements in the batch array are objects. 41 | 42 | If you previously passed arrays to `.batch()` that contained `undefined` or `null`, they would be silently ignored. Now this will produce an error. 43 | 44 | ## 3.0.0 45 | 46 | Dropped support for node 0.12. No other breaking changes. 47 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const AbstractLevelDOWN = require('abstract-leveldown').AbstractLevelDOWN 4 | const AbstractChainedBatch = require('abstract-leveldown').AbstractChainedBatch 5 | const AbstractIterator = require('abstract-leveldown').AbstractIterator 6 | const inherits = require('inherits') 7 | const Codec = require('level-codec') 8 | const EncodingError = require('level-errors').EncodingError 9 | const rangeMethods = ['approximateSize', 'compactRange'] 10 | 11 | module.exports = DB 12 | 13 | function DB (db, opts) { 14 | if (!(this instanceof DB)) return new DB(db, opts) 15 | 16 | const manifest = db.supports || {} 17 | const additionalMethods = manifest.additionalMethods || {} 18 | 19 | AbstractLevelDOWN.call(this, manifest) 20 | 21 | this.supports.encodings = true 22 | this.supports.additionalMethods = {} 23 | 24 | rangeMethods.forEach(function (m) { 25 | // TODO (future major): remove this fallback 26 | const fallback = typeof db[m] === 'function' 27 | 28 | if (additionalMethods[m] || fallback) { 29 | this.supports.additionalMethods[m] = true 30 | 31 | this[m] = function (start, end, opts, cb) { 32 | start = this.codec.encodeKey(start, opts) 33 | end = this.codec.encodeKey(end, opts) 34 | return this.db[m](start, end, opts, cb) 35 | } 36 | } 37 | }, this) 38 | 39 | opts = opts || {} 40 | if (typeof opts.keyEncoding === 'undefined') opts.keyEncoding = 'utf8' 41 | if (typeof opts.valueEncoding === 'undefined') opts.valueEncoding = 'utf8' 42 | 43 | this.db = db 44 | this.codec = new Codec(opts) 45 | } 46 | 47 | inherits(DB, AbstractLevelDOWN) 48 | 49 | DB.prototype.type = 'encoding-down' 50 | 51 | DB.prototype._serializeKey = 52 | DB.prototype._serializeValue = function (datum) { 53 | return datum 54 | } 55 | 56 | DB.prototype._open = function (opts, cb) { 57 | this.db.open(opts, cb) 58 | } 59 | 60 | DB.prototype._close = function (cb) { 61 | this.db.close(cb) 62 | } 63 | 64 | DB.prototype._put = function (key, value, opts, cb) { 65 | key = this.codec.encodeKey(key, opts) 66 | value = this.codec.encodeValue(value, opts) 67 | this.db.put(key, value, opts, cb) 68 | } 69 | 70 | DB.prototype._get = function (key, opts, cb) { 71 | key = this.codec.encodeKey(key, opts) 72 | opts.asBuffer = this.codec.valueAsBuffer(opts) 73 | 74 | this.db.get(key, opts, (err, value) => { 75 | if (err) return cb(err) 76 | 77 | try { 78 | value = this.codec.decodeValue(value, opts) 79 | } catch (err) { 80 | return cb(new EncodingError(err)) 81 | } 82 | 83 | cb(null, value) 84 | }) 85 | } 86 | 87 | DB.prototype._getMany = function (keys, opts, cb) { 88 | keys = keys.map((key) => this.codec.encodeKey(key, opts)) 89 | opts.asBuffer = this.codec.valueAsBuffer(opts) 90 | 91 | this.db.getMany(keys, opts, (err, values) => { 92 | if (err) return cb(err) 93 | 94 | const decoded = new Array(values.length) 95 | 96 | for (let i = 0; i < values.length; i++) { 97 | if (values[i] === undefined) { 98 | decoded[i] = undefined 99 | continue 100 | } 101 | 102 | try { 103 | decoded[i] = this.codec.decodeValue(values[i], opts) 104 | } catch (err) { 105 | return cb(new EncodingError(err)) 106 | } 107 | } 108 | 109 | cb(null, decoded) 110 | }) 111 | } 112 | 113 | DB.prototype._del = function (key, opts, cb) { 114 | key = this.codec.encodeKey(key, opts) 115 | this.db.del(key, opts, cb) 116 | } 117 | 118 | DB.prototype._chainedBatch = function () { 119 | return new Batch(this) 120 | } 121 | 122 | DB.prototype._batch = function (ops, opts, cb) { 123 | ops = this.codec.encodeBatch(ops, opts) 124 | this.db.batch(ops, opts, cb) 125 | } 126 | 127 | DB.prototype._iterator = function (opts) { 128 | opts.keyAsBuffer = this.codec.keyAsBuffer(opts) 129 | opts.valueAsBuffer = this.codec.valueAsBuffer(opts) 130 | return new Iterator(this, opts) 131 | } 132 | 133 | DB.prototype._clear = function (opts, callback) { 134 | opts = this.codec.encodeLtgt(opts) 135 | this.db.clear(opts, callback) 136 | } 137 | 138 | function Iterator (db, opts) { 139 | AbstractIterator.call(this, db) 140 | this.codec = db.codec 141 | this.keys = opts.keys 142 | this.values = opts.values 143 | this.opts = this.codec.encodeLtgt(opts) 144 | this.it = db.db.iterator(this.opts) 145 | } 146 | 147 | inherits(Iterator, AbstractIterator) 148 | 149 | Iterator.prototype._next = function (cb) { 150 | this.it.next((err, key, value) => { 151 | if (err) return cb(err) 152 | 153 | try { 154 | if (this.keys && typeof key !== 'undefined') { 155 | key = this.codec.decodeKey(key, this.opts) 156 | } else { 157 | key = undefined 158 | } 159 | 160 | if (this.values && typeof value !== 'undefined') { 161 | value = this.codec.decodeValue(value, this.opts) 162 | } else { 163 | value = undefined 164 | } 165 | } catch (err) { 166 | return cb(new EncodingError(err)) 167 | } 168 | 169 | cb(null, key, value) 170 | }) 171 | } 172 | 173 | Iterator.prototype._seek = function (key) { 174 | key = this.codec.encodeKey(key, this.opts) 175 | this.it.seek(key) 176 | } 177 | 178 | Iterator.prototype._end = function (cb) { 179 | this.it.end(cb) 180 | } 181 | 182 | function Batch (db, codec) { 183 | AbstractChainedBatch.call(this, db) 184 | this.codec = db.codec 185 | this.batch = db.db.batch() 186 | } 187 | 188 | inherits(Batch, AbstractChainedBatch) 189 | 190 | Batch.prototype._put = function (key, value, options) { 191 | key = this.codec.encodeKey(key, options) 192 | value = this.codec.encodeValue(value, options) 193 | this.batch.put(key, value) 194 | } 195 | 196 | Batch.prototype._del = function (key, options) { 197 | key = this.codec.encodeKey(key, options) 198 | this.batch.del(key) 199 | } 200 | 201 | Batch.prototype._clear = function () { 202 | this.batch.clear() 203 | } 204 | 205 | Batch.prototype._write = function (opts, cb) { 206 | this.batch.write(opts, cb) 207 | } 208 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "encoding-down", 3 | "version": "7.1.0", 4 | "description": "An abstract-leveldown implementation that wraps another store to encode keys and values", 5 | "license": "MIT", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "standard && hallmark && nyc node test", 9 | "test-browsers-local": "airtap --coverage test/index.js", 10 | "coverage": "nyc report -r lcovonly", 11 | "hallmark": "hallmark --fix", 12 | "dependency-check": "dependency-check . test/*.js", 13 | "prepublishOnly": "npm run dependency-check" 14 | }, 15 | "files": [ 16 | "index.js", 17 | "CHANGELOG.md", 18 | "UPGRADING.md" 19 | ], 20 | "dependencies": { 21 | "abstract-leveldown": "^7.2.0", 22 | "inherits": "^2.0.3", 23 | "level-codec": "^10.0.0", 24 | "level-errors": "^3.0.0" 25 | }, 26 | "devDependencies": { 27 | "airtap": "^4.0.3", 28 | "airtap-playwright": "^1.0.1", 29 | "dependency-check": "^3.3.0", 30 | "hallmark": "^4.0.0", 31 | "memdown": "^6.1.0", 32 | "nyc": "^15.1.0", 33 | "standard": "^17.0.0", 34 | "tape": "^5.0.1" 35 | }, 36 | "repository": "Level/encoding-down", 37 | "homepage": "https://github.com/Level/encoding-down", 38 | "keywords": [ 39 | "level" 40 | ], 41 | "engines": { 42 | "node": ">=10" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const test = require('tape') 4 | const encdown = require('..') 5 | const suite = require('abstract-leveldown/test') 6 | const memdown = require('memdown') 7 | const Buffer = require('buffer').Buffer 8 | const hasOwnProperty = Object.prototype.hasOwnProperty 9 | const noop = function () {} 10 | 11 | const testCommon = suite.common({ 12 | test: test, 13 | factory: function () { 14 | return encdown(memdown()) 15 | }, 16 | 17 | encodings: true, 18 | 19 | // Unsupported features 20 | createIfMissing: false, 21 | errorIfExists: false, 22 | 23 | // Opt-in to new tests 24 | clear: true, 25 | getMany: true 26 | }) 27 | 28 | // Test abstract-leveldown compliance 29 | suite(testCommon) 30 | 31 | // Custom tests 32 | test('opens and closes the underlying db', function (t) { 33 | const _db = { 34 | open: function (opts, cb) { 35 | t.pass('open called') 36 | setImmediate(cb) 37 | }, 38 | close: function (cb) { 39 | t.pass('close called') 40 | setImmediate(cb) 41 | } 42 | } 43 | const db = encdown(_db) 44 | db.open(function (err) { 45 | t.error(err, 'no error') 46 | db.close(function (err) { 47 | t.error(err, 'no error') 48 | t.end() 49 | }) 50 | }) 51 | }) 52 | 53 | test('encodings default to utf8', function (t) { 54 | const db = encdown(memdown()) 55 | t.ok(db.db, '.db should be set') 56 | t.ok(db.codec, '.codec should be set') 57 | t.deepEqual(db.codec.opts, { 58 | keyEncoding: 'utf8', 59 | valueEncoding: 'utf8' 60 | }, 'correct defaults') 61 | t.end() 62 | }) 63 | 64 | test('default utf8 encoding stringifies numbers', function (t) { 65 | t.plan(3) 66 | 67 | const db = encdown({ 68 | put: function (key, value, callback) { 69 | t.is(key, '1') 70 | t.is(value, '2') 71 | }, 72 | batch: function (ops, options, callback) { 73 | t.same(ops, [{ type: 'put', key: '3', value: '4' }]) 74 | } 75 | }) 76 | 77 | db.put(1, 2, noop) 78 | db.batch([{ type: 'put', key: 3, value: 4 }], noop) 79 | }) 80 | 81 | test('test safe decode in get', function (t) { 82 | const memdb = memdown() 83 | const db = encdown(memdb, { valueEncoding: 'utf8' }) 84 | db.put('foo', 'this {} is [] not : json', function (err) { 85 | t.error(err, 'no error') 86 | const db2 = encdown(memdb, { valueEncoding: 'json' }) 87 | db2.get('foo', function (err, value) { 88 | t.equals('EncodingError', err.name) 89 | memdb.close(t.end.bind(t)) 90 | }) 91 | }) 92 | }) 93 | 94 | test('can decode from string to json', function (t) { 95 | const memdb = memdown() 96 | const db = encdown(memdb, { valueEncoding: 'utf8' }) 97 | const data = { thisis: 'json' } 98 | db.put('foo', JSON.stringify(data), function (err) { 99 | t.error(err, 'no error') 100 | const db2 = encdown(memdb, { valueEncoding: 'json' }) 101 | db2.get('foo', function (err, value) { 102 | t.error(err, 'no error') 103 | t.deepEqual(value, data, 'JSON.parse') 104 | memdb.close(t.end.bind(t)) 105 | }) 106 | }) 107 | }) 108 | 109 | test('can decode from json to string', function (t) { 110 | const memdb = memdown() 111 | const db = encdown(memdb, { valueEncoding: 'json' }) 112 | const data = { thisis: 'json' } 113 | db.put('foo', data, function (err) { 114 | t.error(err, 'no error') 115 | const db2 = encdown(memdb, { valueEncoding: 'utf8' }) 116 | db2.get('foo', function (err, value) { 117 | t.error(err, 'no error') 118 | t.deepEqual(value, JSON.stringify(data), 'JSON.stringify') 119 | memdb.close(t.end.bind(t)) 120 | }) 121 | }) 122 | }) 123 | 124 | test('binary encoding, using batch', function (t) { 125 | const data = [ 126 | { 127 | type: 'put', 128 | key: Buffer.from([1, 2, 3]), 129 | value: Buffer.from([4, 5, 6]) 130 | }, 131 | { 132 | type: 'put', 133 | key: Buffer.from([7, 8, 9]), 134 | value: Buffer.from([10, 11, 12]) 135 | } 136 | ] 137 | const db = encdown(memdown(), { 138 | keyEncoding: 'binary', 139 | valueEncoding: 'binary' 140 | }) 141 | db.batch(data, function (err) { 142 | t.error(err, 'no error') 143 | db.get(data[0].key, function (err, value) { 144 | t.error(err, 'no error') 145 | t.deepEqual(value, data[0].value) 146 | db.get(data[1].key, function (err, value) { 147 | t.error(err, 'no error') 148 | t.deepEqual(value, data[1].value) 149 | db.close(t.end.bind(t)) 150 | }) 151 | }) 152 | }) 153 | }) 154 | 155 | test('default encoding retrieves a string from underlying store', function (t) { 156 | t.plan(1) 157 | 158 | const down = { 159 | get: function (key, options, cb) { 160 | t.is(options.asBuffer, false, '.asBuffer is false') 161 | } 162 | } 163 | 164 | const db = encdown(down) 165 | 166 | db.get('key', noop) 167 | }) 168 | 169 | test('custom value encoding that retrieves a string from underlying store', function (t) { 170 | t.plan(1) 171 | 172 | const down = { 173 | get: function (key, options, cb) { 174 | t.is(options.asBuffer, false, '.asBuffer is false') 175 | } 176 | } 177 | 178 | const db = encdown(down, { 179 | valueEncoding: { 180 | buffer: false 181 | } 182 | }) 183 | 184 | db.get('key', noop) 185 | }) 186 | 187 | test('get() forwards error from underlying store', function (t) { 188 | t.plan(1) 189 | 190 | const down = { 191 | get: function (key, options, cb) { 192 | process.nextTick(cb, new Error('error from store')) 193 | } 194 | } 195 | 196 | encdown(down).get('key', function (err) { 197 | t.is(err.message, 'error from store') 198 | }) 199 | }) 200 | 201 | test('getMany() skips decoding not-found values', function (t) { 202 | t.plan(6) 203 | 204 | const valueEncoding = { 205 | encode: JSON.stringify, 206 | decode (value) { 207 | t.is(value, JSON.stringify(data)) 208 | return JSON.parse(value) 209 | }, 210 | buffer: false, 211 | type: 'test' 212 | } 213 | 214 | const data = { beep: 'boop' } 215 | const db = encdown(memdown(), { valueEncoding }) 216 | 217 | db.open(function (err) { 218 | t.error(err, 'no open() error') 219 | 220 | db.put('foo', data, function (err) { 221 | t.error(err, 'no put() error') 222 | 223 | db.getMany(['foo', 'bar'], function (err, values) { 224 | t.error(err, 'no getMany() error') 225 | t.same(values, [data, undefined]) 226 | 227 | db.close(t.error.bind(t)) 228 | }) 229 | }) 230 | }) 231 | }) 232 | 233 | test('getMany() forwards decode error', function (t) { 234 | const valueEncoding = { 235 | encode: (v) => v, 236 | decode: (v) => { throw new Error('decode error') }, 237 | buffer: false, 238 | type: 'test' 239 | } 240 | 241 | const db = encdown(memdown(), { valueEncoding }) 242 | 243 | db.open(function (err) { 244 | t.error(err, 'no open() error') 245 | 246 | db.put('foo', 'bar', function (err) { 247 | t.error(err, 'no put() error') 248 | 249 | db.getMany(['foo'], function (err, values) { 250 | t.is(err && err.message, 'decode error') 251 | t.is(values, undefined) 252 | 253 | db.close(t.end.bind(t)) 254 | }) 255 | }) 256 | }) 257 | }) 258 | 259 | test('_del() encodes key', function (t) { 260 | t.plan(1) 261 | 262 | const down = { 263 | del: function (key, options, cb) { 264 | t.is(key, '2') 265 | } 266 | } 267 | 268 | encdown(down).del(2, noop) 269 | }) 270 | 271 | test('chainedBatch.put() encodes key and value', function (t) { 272 | t.plan(2) 273 | 274 | const down = { 275 | batch: function () { 276 | return { 277 | put: function (key, value) { 278 | t.is(key, '1') 279 | t.is(value, '2') 280 | } 281 | } 282 | } 283 | } 284 | 285 | encdown(down).batch().put(1, 2) 286 | }) 287 | 288 | test('chainedBatch.put() takes custom encoding', function (t) { 289 | t.plan(4) 290 | 291 | const expectedKeys = ['"a"', 'a'] 292 | const expectedValues = ['"b"', 'b'] 293 | 294 | const down = { 295 | batch: function () { 296 | return { 297 | put: function (key, value) { 298 | t.is(key, expectedKeys.shift()) 299 | t.is(value, expectedValues.shift()) 300 | } 301 | } 302 | } 303 | } 304 | 305 | encdown(down).batch() 306 | .put('a', 'b', { keyEncoding: 'json', valueEncoding: 'json' }) 307 | .put('a', 'b') 308 | }) 309 | 310 | test('chainedBatch.del() encodes key', function (t) { 311 | t.plan(1) 312 | 313 | const down = { 314 | batch: function () { 315 | return { 316 | del: function (key) { 317 | t.is(key, '1') 318 | } 319 | } 320 | } 321 | } 322 | 323 | encdown(down).batch().del(1) 324 | }) 325 | 326 | test('chainedBatch.del() takes custom encoding', function (t) { 327 | t.plan(2) 328 | 329 | const expectedKeys = ['"a"', 'a'] 330 | 331 | const down = { 332 | batch: function () { 333 | return { 334 | del: function (key) { 335 | t.is(key, expectedKeys.shift()) 336 | } 337 | } 338 | } 339 | } 340 | 341 | encdown(down).batch() 342 | .del('a', { keyEncoding: 'json' }) 343 | .del('a') 344 | }) 345 | 346 | test('chainedBatch.clear() is forwarded to underlying store', function (t) { 347 | t.plan(1) 348 | 349 | const down = { 350 | batch: function () { 351 | return { 352 | clear: function () { 353 | t.pass('called') 354 | } 355 | } 356 | } 357 | } 358 | 359 | encdown(down).batch().clear() 360 | }) 361 | 362 | test('chainedBatch.write() is forwarded to underlying store', function (t) { 363 | t.plan(1) 364 | 365 | const down = { 366 | batch: function () { 367 | return { 368 | write: function () { 369 | t.pass('called') 370 | } 371 | } 372 | } 373 | } 374 | 375 | encdown(down).batch().write(noop) 376 | }) 377 | 378 | test('custom value encoding that retrieves a buffer from underlying store', function (t) { 379 | t.plan(1) 380 | 381 | const down = { 382 | get: function (key, options, cb) { 383 | t.is(options.asBuffer, true, '.asBuffer is true') 384 | } 385 | } 386 | 387 | const db = encdown(down, { 388 | valueEncoding: { 389 | buffer: true 390 | } 391 | }) 392 | 393 | db.get('key', noop) 394 | }) 395 | 396 | test('.keyAsBuffer and .valueAsBuffer defaults to false', function (t) { 397 | t.plan(2) 398 | 399 | const down = { 400 | iterator: function (options) { 401 | t.is(options.keyAsBuffer, false) 402 | t.is(options.valueAsBuffer, false) 403 | } 404 | } 405 | 406 | encdown(down).iterator() 407 | }) 408 | 409 | test('.keyAsBuffer and .valueAsBuffer as buffers if encoding says so', function (t) { 410 | t.plan(2) 411 | 412 | const down = { 413 | iterator: function (options) { 414 | t.is(options.keyAsBuffer, true) 415 | t.is(options.valueAsBuffer, true) 416 | } 417 | } 418 | 419 | const db = encdown(down, { 420 | keyEncoding: { 421 | buffer: true 422 | }, 423 | valueEncoding: { 424 | buffer: true 425 | } 426 | }) 427 | 428 | db.iterator() 429 | }) 430 | 431 | test('.keyAsBuffer and .valueAsBuffer as strings if encoding says so', function (t) { 432 | t.plan(2) 433 | 434 | const down = { 435 | iterator: function (options) { 436 | t.is(options.keyAsBuffer, false) 437 | t.is(options.valueAsBuffer, false) 438 | } 439 | } 440 | 441 | const db = encdown(down, { 442 | keyEncoding: { 443 | buffer: false 444 | }, 445 | valueEncoding: { 446 | buffer: false 447 | } 448 | }) 449 | 450 | db.iterator() 451 | }) 452 | 453 | test('iterator options.keys and options.values default to true', function (t) { 454 | t.plan(2) 455 | 456 | const down = { 457 | iterator: function (options) { 458 | t.is(options.keys, true) 459 | t.is(options.values, true) 460 | } 461 | } 462 | 463 | encdown(down).iterator() 464 | }) 465 | 466 | test('iterator skips keys if options.keys is false', function (t) { 467 | t.plan(4) 468 | 469 | const down = { 470 | iterator: function (options) { 471 | t.is(options.keys, false) 472 | 473 | return { 474 | next: function (callback) { 475 | callback(null, '', 'value') 476 | } 477 | } 478 | } 479 | } 480 | 481 | const keyEncoding = { 482 | decode: function (key) { 483 | t.fail('should not be called') 484 | } 485 | } 486 | 487 | const db = encdown(down, { keyEncoding }) 488 | const it = db.iterator({ keys: false }) 489 | 490 | it.next(function (err, key, value) { 491 | t.ifError(err, 'no next error') 492 | t.is(key, undefined, 'normalized key to undefined') 493 | t.is(value, 'value', 'got value') 494 | }) 495 | }) 496 | 497 | test('iterator skips values if options.values is false', function (t) { 498 | t.plan(4) 499 | 500 | const down = { 501 | iterator: function (options) { 502 | t.is(options.values, false) 503 | 504 | return { 505 | next: function (callback) { 506 | callback(null, 'key', '') 507 | } 508 | } 509 | } 510 | } 511 | 512 | const valueEncoding = { 513 | decode: function (value) { 514 | t.fail('should not be called') 515 | } 516 | } 517 | 518 | const db = encdown(down, { valueEncoding }) 519 | const it = db.iterator({ values: false }) 520 | 521 | it.next(function (err, key, value) { 522 | t.ifError(err, 'no next error') 523 | t.is(key, 'key', 'got key') 524 | t.is(value, undefined, 'normalized value to undefined') 525 | }) 526 | }) 527 | 528 | test('iterator encodes range options', function (t) { 529 | t.plan(5) 530 | 531 | const keyEncoding = { 532 | encode: function (key) { 533 | return 'encoded_' + key 534 | }, 535 | buffer: false 536 | } 537 | 538 | const db = encdown({ 539 | iterator: function (options) { 540 | t.is(options.gt, 'encoded_3') 541 | t.is(options.gte, 'encoded_4') 542 | t.is(options.lt, 'encoded_5') 543 | t.is(options.lte, 'encoded_6') 544 | t.is(options.foo, 7) 545 | } 546 | }, { keyEncoding }) 547 | 548 | db.iterator({ gt: 3, gte: 4, lt: 5, lte: 6, foo: 7 }) 549 | }) 550 | 551 | test('iterator does not strip nullish range options', function (t) { 552 | t.plan(12) 553 | 554 | encdown({ 555 | iterator: function (options) { 556 | t.is(options.gt, null) 557 | t.is(options.gte, null) 558 | t.is(options.lt, null) 559 | t.is(options.lte, null) 560 | } 561 | }).iterator({ 562 | gt: null, 563 | gte: null, 564 | lt: null, 565 | lte: null 566 | }) 567 | 568 | encdown({ 569 | iterator: function (options) { 570 | t.ok(hasOwnProperty.call(options, 'gt')) 571 | t.ok(hasOwnProperty.call(options, 'gte')) 572 | t.ok(hasOwnProperty.call(options, 'lt')) 573 | t.ok(hasOwnProperty.call(options, 'lte')) 574 | 575 | t.is(options.gt, undefined) 576 | t.is(options.gte, undefined) 577 | t.is(options.lt, undefined) 578 | t.is(options.lte, undefined) 579 | } 580 | }).iterator({ 581 | gt: undefined, 582 | gte: undefined, 583 | lt: undefined, 584 | lte: undefined 585 | }) 586 | }) 587 | 588 | test('iterator does not add nullish range options', function (t) { 589 | t.plan(4) 590 | 591 | encdown({ 592 | iterator: function (options) { 593 | t.notOk(hasOwnProperty.call(options, 'gt')) 594 | t.notOk(hasOwnProperty.call(options, 'gte')) 595 | t.notOk(hasOwnProperty.call(options, 'lt')) 596 | t.notOk(hasOwnProperty.call(options, 'lte')) 597 | } 598 | }).iterator({}) 599 | }) 600 | 601 | test('iterator forwards next() error from underlying iterator', function (t) { 602 | t.plan(1) 603 | 604 | const down = { 605 | iterator: function () { 606 | return { 607 | next: function (callback) { 608 | process.nextTick(callback, new Error('from underlying iterator')) 609 | } 610 | } 611 | } 612 | } 613 | 614 | const db = encdown(down) 615 | const it = db.iterator() 616 | 617 | it.next(function (err, key, value) { 618 | t.is(err.message, 'from underlying iterator') 619 | }) 620 | }) 621 | 622 | test('iterator forwards end() to underlying iterator', function (t) { 623 | t.plan(2) 624 | 625 | const down = { 626 | iterator: function () { 627 | return { 628 | end: function (callback) { 629 | t.pass('called') 630 | process.nextTick(callback) 631 | } 632 | } 633 | } 634 | } 635 | 636 | const db = encdown(down) 637 | const it = db.iterator() 638 | 639 | it.end(function () { 640 | t.pass('called') 641 | }) 642 | }) 643 | 644 | test('iterator catches decoding error from keyEncoding', function (t) { 645 | t.plan(5) 646 | 647 | const down = { 648 | iterator: function () { 649 | return { 650 | next: function (callback) { 651 | process.nextTick(callback, null, 'key', 'value') 652 | } 653 | } 654 | } 655 | } 656 | 657 | const db = encdown(down, { 658 | keyEncoding: { 659 | decode: function (key) { 660 | t.is(key, 'key') 661 | throw new Error('from codec') 662 | } 663 | } 664 | }) 665 | 666 | db.iterator().next(function (err, key, value) { 667 | t.is(err.message, 'from codec') 668 | t.is(err.name, 'EncodingError') 669 | t.is(key, undefined) 670 | t.is(value, undefined) 671 | }) 672 | }) 673 | 674 | test('iterator catches decoding error from valueEncoding', function (t) { 675 | t.plan(5) 676 | 677 | const down = { 678 | iterator: function () { 679 | return { 680 | next: function (callback) { 681 | process.nextTick(callback, null, 'key', 'value') 682 | } 683 | } 684 | } 685 | } 686 | 687 | const db = encdown(down, { 688 | valueEncoding: { 689 | decode: function (value) { 690 | t.is(value, 'value') 691 | throw new Error('from codec') 692 | } 693 | } 694 | }) 695 | 696 | db.iterator().next(function (err, key, value) { 697 | t.is(err.message, 'from codec') 698 | t.is(err.name, 'EncodingError') 699 | t.is(key, undefined) 700 | t.is(value, undefined) 701 | }) 702 | }) 703 | 704 | test('proxies approximateSize() if it exists', function (t) { 705 | t.is(typeof encdown({ approximateSize: noop }).approximateSize, 'function') 706 | t.ok(encdown({ approximateSize: noop }).supports.additionalMethods.approximateSize) 707 | t.is(encdown({}).approximateSize, undefined) 708 | t.notOk(encdown({}).supports.additionalMethods.approximateSize) 709 | t.end() 710 | }) 711 | 712 | test('proxies compactRange() if it exists', function (t) { 713 | t.is(typeof encdown({ compactRange: noop }).compactRange, 'function') 714 | t.ok(encdown({ compactRange: noop }).supports.additionalMethods.compactRange) 715 | t.is(encdown({}).compactRange, undefined) 716 | t.notOk(encdown({}).supports.additionalMethods.compactRange) 717 | t.end() 718 | }) 719 | 720 | test('encodes start and end of approximateSize()', function (t) { 721 | const db = encdown({ 722 | approximateSize: function (start, end) { 723 | t.is(start, '1') 724 | t.is(end, '2') 725 | t.end() 726 | } 727 | }) 728 | 729 | db.approximateSize(1, 2, noop) 730 | }) 731 | 732 | test('encodes start and end of compactRange()', function (t) { 733 | const db = encdown({ 734 | compactRange: function (start, end) { 735 | t.is(start, '1') 736 | t.is(end, '2') 737 | t.end() 738 | } 739 | }) 740 | 741 | db.compactRange(1, 2, noop) 742 | }) 743 | 744 | test('encodes start and end of approximateSize() with custom encoding', function (t) { 745 | const db = encdown({ 746 | approximateSize: function (start, end) { 747 | t.is(start, '"a"') 748 | t.is(end, '"b"') 749 | t.end() 750 | } 751 | }) 752 | 753 | db.approximateSize('a', 'b', { keyEncoding: 'json' }, noop) 754 | }) 755 | 756 | test('encodes start and end of compactRange() with custom encoding', function (t) { 757 | const db = encdown({ 758 | compactRange: function (start, end) { 759 | t.is(start, '"a"') 760 | t.is(end, '"b"') 761 | t.end() 762 | } 763 | }) 764 | 765 | db.compactRange('a', 'b', { keyEncoding: 'json' }, noop) 766 | }) 767 | 768 | test('encodes seek target', function (t) { 769 | t.plan(1) 770 | 771 | const db = encdown({ 772 | iterator: function () { 773 | return { 774 | seek: function (target) { 775 | t.is(target, '123', 'encoded number') 776 | } 777 | } 778 | } 779 | }, { keyEncoding: 'json' }) 780 | 781 | db.iterator().seek(123) 782 | }) 783 | 784 | test('encodes seek target with custom encoding', function (t) { 785 | t.plan(1) 786 | 787 | const targets = [] 788 | const db = encdown({ 789 | iterator: function () { 790 | return { 791 | seek: function (target) { 792 | targets.push(target) 793 | } 794 | } 795 | } 796 | }) 797 | 798 | db.iterator().seek('a') 799 | db.iterator({ keyEncoding: 'json' }).seek('a') 800 | 801 | t.same(targets, ['a', '"a"'], 'encoded targets') 802 | }) 803 | 804 | test('encodes nullish seek target', function (t) { 805 | t.plan(1) 806 | 807 | const targets = [] 808 | const db = encdown({ 809 | iterator: function () { 810 | return { 811 | seek: function (target) { 812 | targets.push(target) 813 | } 814 | } 815 | } 816 | }, { keyEncoding: { encode: String } }) 817 | 818 | // Unlike keys, nullish targets should not be rejected; 819 | // assume that the encoding gives these types meaning. 820 | db.iterator().seek(null) 821 | db.iterator().seek(undefined) 822 | 823 | t.same(targets, ['null', 'undefined'], 'encoded') 824 | }) 825 | 826 | test('clear() forwards default options', function (t) { 827 | t.plan(3) 828 | 829 | const down = { 830 | clear: function (options, callback) { 831 | t.is(options.reverse, false) 832 | t.is(options.limit, -1) 833 | t.is(callback, noop) 834 | } 835 | } 836 | 837 | encdown(down).clear(noop) 838 | }) 839 | 840 | test('clear() forwards error from underlying store', function (t) { 841 | t.plan(1) 842 | 843 | const down = { 844 | clear: function (options, cb) { 845 | process.nextTick(cb, new Error('error from store')) 846 | } 847 | } 848 | 849 | encdown(down).clear(function (err) { 850 | t.is(err.message, 'error from store') 851 | }) 852 | }) 853 | 854 | test('clear() encodes range options', function (t) { 855 | t.plan(5) 856 | 857 | const keyEncoding = { 858 | encode: function (key) { 859 | return 'encoded_' + key 860 | }, 861 | buffer: false 862 | } 863 | 864 | const db = encdown({ 865 | clear: function (options) { 866 | t.is(options.gt, 'encoded_1') 867 | t.is(options.gte, 'encoded_2') 868 | t.is(options.lt, 'encoded_3') 869 | t.is(options.lte, 'encoded_4') 870 | t.is(options.foo, 5) 871 | } 872 | }, { keyEncoding }) 873 | 874 | db.clear({ gt: 1, gte: 2, lt: 3, lte: 4, foo: 5 }, noop) 875 | }) 876 | 877 | test('clear() does not strip nullish range options', function (t) { 878 | t.plan(12) 879 | 880 | encdown({ 881 | clear: function (options) { 882 | t.is(options.gt, null) 883 | t.is(options.gte, null) 884 | t.is(options.lt, null) 885 | t.is(options.lte, null) 886 | } 887 | }).clear({ 888 | gt: null, 889 | gte: null, 890 | lt: null, 891 | lte: null 892 | }, noop) 893 | 894 | encdown({ 895 | clear: function (options) { 896 | t.ok(hasOwnProperty.call(options, 'gt')) 897 | t.ok(hasOwnProperty.call(options, 'gte')) 898 | t.ok(hasOwnProperty.call(options, 'lt')) 899 | t.ok(hasOwnProperty.call(options, 'lte')) 900 | 901 | t.is(options.gt, undefined) 902 | t.is(options.gte, undefined) 903 | t.is(options.lt, undefined) 904 | t.is(options.lte, undefined) 905 | } 906 | }).clear({ 907 | gt: undefined, 908 | gte: undefined, 909 | lt: undefined, 910 | lte: undefined 911 | }, noop) 912 | }) 913 | 914 | test('clear() does not add nullish range options', function (t) { 915 | t.plan(4) 916 | 917 | encdown({ 918 | clear: function (options) { 919 | t.notOk(hasOwnProperty.call(options, 'gt')) 920 | t.notOk(hasOwnProperty.call(options, 'gte')) 921 | t.notOk(hasOwnProperty.call(options, 'lt')) 922 | t.notOk(hasOwnProperty.call(options, 'lte')) 923 | } 924 | }).clear({}, noop) 925 | }) 926 | --------------------------------------------------------------------------------