Repository: maxogden/level.js Branch: master Commit: d510999ee56a Files: 21 Total size: 70.9 KB Directory structure: gitextract_dn_f2s6r/ ├── .airtap.yml ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ ├── release.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── UPGRADING.md ├── index.js ├── iterator.js ├── package.json ├── test/ │ ├── custom-test.js │ ├── index.js │ ├── support-test.js │ └── upgrade-test.js └── util/ ├── clear.js ├── deserialize.js ├── key-range.js ├── serialize.js └── support.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .airtap.yml ================================================ providers: - airtap-sauce browsers: - name: chrome - name: firefox - name: safari version: 12..latest - name: ios_saf version: 12..latest - name: chrome for android version: 6..latest - name: msedge presets: local: providers: - airtap-playwright browsers: - name: chromium - name: firefox ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: npm directory: / schedule: interval: monthly ignore: - dependency-name: dependency-check - dependency-name: uuid - package-ecosystem: github-actions directory: / schedule: interval: monthly ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: tags: ['*'] permissions: contents: write jobs: release: name: Release runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Create GitHub release uses: docker://antonyurchenko/git-release:v4 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/test.yml ================================================ name: Test on: push concurrency: sauce-labs jobs: test: runs-on: ubuntu-latest name: Sauce Labs steps: - name: Checkout uses: actions/checkout@v3 - name: Use node 14 uses: actions/setup-node@v3 with: node-version: 14 - name: Install run: npm install env: # Download Sauce Connect binary now instead of on first run SAUCE_CONNECT_DOWNLOAD_ON_INSTALL: true - name: Add host run: echo "127.0.0.1 airtap.local" | sudo tee -a /etc/hosts - name: Test run: npm run test-browsers env: SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} - name: Coverage run: npm run coverage - name: Codecov uses: codecov/codecov-action@v3 with: file: coverage/lcov.info ================================================ FILE: .gitignore ================================================ node_modules .DS_Store npm-debug.log package-lock.json .nyc_output/ ================================================ FILE: CHANGELOG.md ================================================ # Changelog ## [6.1.0] - 2021-09-28 ### Added - Add `db.getMany(keys)` ([#214](https://github.com/Level/level-js/issues/214)) ([`f5a3ca3`](https://github.com/Level/level-js/commit/f5a3ca3)) (Vincent Weevers). ## [6.0.0] - 2021-04-09 _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ ### Changed - **Breaking:** bump `abstract-leveldown` ([`720aced`](https://github.com/Level/level-js/commit/720aced)) (Vincent Weevers) - **Breaking:** bump `buffer` from 5.x to 6.x ([#210](https://github.com/Level/level-js/issues/210)) ([`cc68b21`](https://github.com/Level/level-js/commit/cc68b21)) (Alex Potsides) - Modernize syntax and bump `standard` ([Level/community#98](https://github.com/Level/community/issues/98)) ([`0ce815f`](https://github.com/Level/level-js/commit/0ce815f)) (Vincent Weevers) - Every browser in our test matrix now supports binary keys ([`2c20127`](https://github.com/Level/level-js/commit/2c20127)) (Vincent Weevers) ## [5.0.2] - 2020-04-03 ### Changed - Use `nextTick` of `abstract-leveldown` ([#195](https://github.com/Level/level-js/issues/195)) ([**@vweevers**](https://github.com/vweevers)) (same underlying code) - Upgrade `nyc` devDependency from `^14.0.0` to `^15.0.0` ([#187](https://github.com/Level/level-js/issues/187)) ([**@vweevers**](https://github.com/vweevers)) - Upgrade `airtap` devDependency from `^2.0.0` to `^3.0.0` ([#189](https://github.com/Level/level-js/issues/189)) ([**@vweevers**](https://github.com/vweevers)) ### Fixed - Add `buffer` for browsers ([#191](https://github.com/Level/level-js/issues/191)) ([**@hugomrdias**](https://github.com/hugomrdias)) ## [5.0.1] - 2019-11-29 ### Fixed - Restore support of empty prefix option ([#184](https://github.com/Level/level-js/issues/184)) ([**@achingbrain**](https://github.com/achingbrain)). This restores a previous behavior (of `level-js` < 3) that unknown to us, was provided by the since-removed `IDBWrapper`. ## [5.0.0] - 2019-10-04 _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ ### Changed - **Breaking**: Drop support of key & value types other than string and Buffer ([#179](https://github.com/Level/level-js/issues/179)) ([**@vweevers**](https://github.com/vweevers)) - Replace mentions of `level-browserify` with `level` ([`58b3e07`](https://github.com/Level/level-js/commit/58b3e07)) ([**@vweevers**](https://github.com/vweevers)) - Upgrade `hallmark` devDependency from `^0.1.0` to `^2.0.0` ([#172](https://github.com/Level/level-js/issues/172), [#177](https://github.com/Level/level-js/issues/177)) ([**@vweevers**](https://github.com/vweevers)) - Upgrade `nyc` devDependency from `^13.1.0` to `^14.0.0` ([#169](https://github.com/Level/level-js/issues/169)) ([**@vweevers**](https://github.com/vweevers)) - Upgrade `standard` devDependency from `^12.0.1` to `^14.0.2` ([#171](https://github.com/Level/level-js/issues/171), [`aacb0ea`](https://github.com/Level/level-js/commit/aacb0ea)) ([**@vweevers**](https://github.com/vweevers), [**@ralphtheninja**](https://github.com/ralphtheninja)) ### Added - Add manifest ([Level/community#83](https://github.com/Level/community/issues/83)) ([#183](https://github.com/Level/level-js/issues/183)) ([**@vweevers**](https://github.com/vweevers)) - Support `clear()` ([Level/community#79](https://github.com/Level/community/issues/79)) ([#182](https://github.com/Level/level-js/issues/182)) ([**@vweevers**](https://github.com/vweevers)) ## [4.0.1] - 2019-03-31 ### Changed - Apply common project tweaks ([#164](https://github.com/Level/level-js/issues/164), [#165](https://github.com/Level/level-js/issues/165)) ([**@vweevers**](https://github.com/vweevers)) ### Removed - Remove outdated sentence about nullish values from README ([#166](https://github.com/Level/level-js/issues/166)) ([**@vweevers**](https://github.com/vweevers)) ## [4.0.0] - 2018-12-30 _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ ### Changed - Upgrade `abstract-leveldown` from `~5.0.0` to `~6.0.1` ([#155](https://github.com/Level/level-js/issues/155), [#157](https://github.com/Level/level-js/issues/157)) ([**@vweevers**](https://github.com/vweevers)) - Don't serialize boolean or `NaN` keys, have IDB reject them ([#155](https://github.com/Level/level-js/issues/155)) ([**@vweevers**](https://github.com/vweevers)) - Update test of `key cannot be an empty Array` error ([#155](https://github.com/Level/level-js/issues/155)) ([**@vweevers**](https://github.com/vweevers)) - Change `iterator.db` to reference `level-js` instance, not IDB ([#155](https://github.com/Level/level-js/issues/155)) ([**@vweevers**](https://github.com/vweevers)) - Handle `location` in constructor, as it was removed from `abstract-leveldown` ([#155](https://github.com/Level/level-js/issues/155)) ([**@vweevers**](https://github.com/vweevers)) - Use `level-concat-iterator` and `testCommon.factory()` in custom tests ([#155](https://github.com/Level/level-js/issues/155)) ([**@vweevers**](https://github.com/vweevers)) - Invoke abstract tests from single function ([#155](https://github.com/Level/level-js/issues/155)) ([**@vweevers**](https://github.com/vweevers)) - Upgrade `airtap` devDependency from `0.0.7` to `^2.0.0` ([`2b71337`](https://github.com/Level/level-js/commit/2b71337), [#161](https://github.com/Level/level-js/issues/161)) ([**@ralphtheninja**](https://github.com/ralphtheninja), [**@vweevers**](https://github.com/vweevers)) - Upgrade `standard` devDependency from `^11.0.1` to `^12.0.1` ([#153](https://github.com/Level/level-js/issues/153)) ([**@vweevers**](https://github.com/vweevers), [**@ralphtheninja**](https://github.com/ralphtheninja)) - Replace `remark-cli` devDependency with `hallmark` ([#151](https://github.com/Level/level-js/issues/151), [#153](https://github.com/Level/level-js/issues/153)) ([**@vweevers**](https://github.com/vweevers)) ### Added - Test and document native sort order ([#157](https://github.com/Level/level-js/issues/157)) ([**@vweevers**](https://github.com/vweevers)) - Add iPhone and Android `latest` to test matrix ([#162](https://github.com/Level/level-js/issues/162)) ([**@vweevers**](https://github.com/vweevers)) - Add `nyc` and `coveralls` devDependencies ([#150](https://github.com/Level/level-js/issues/150), [#153](https://github.com/Level/level-js/issues/153)) ([`eb1aead`](https://github.com/Level/level-js/commit/eb1aead)) ([**@ralphtheninja**](https://github.com/ralphtheninja), [**@vweevers**](https://github.com/vweevers)) - Add Contributing section to README ([`c94a9a4`](https://github.com/Level/level-js/commit/c94a9a4)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) ### Removed - Remove now irrelevant serialization of nullish values ([#155](https://github.com/Level/level-js/issues/155)) ([**@vweevers**](https://github.com/vweevers)) - Remove unused `IndexedDBShim` from tests ([#162](https://github.com/Level/level-js/issues/162)) ([**@vweevers**](https://github.com/vweevers)) ## [3.0.0] - 2018-06-17 _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._ ### Changed - Destroy with `location` and `prefix` only ([#116](https://github.com/Level/level-js/issues/116)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) - Replace `util.inherits` with `inherits` module ([`8db16c1`](https://github.com/Level/level-js/commit/8db16c1)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) - Change copyright years to "2012-present" ([`7017edd`](https://github.com/Level/level-js/commit/7017edd)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) - Simplify license description ([#141](https://github.com/Level/level-js/issues/141)) ([**@vweevers**](https://github.com/vweevers)) - Update `package.json` description and keywords ([#141](https://github.com/Level/level-js/issues/141)) ([**@vweevers**](https://github.com/vweevers)) ### Added - Add `CHANGELOG.md` ([#107](https://github.com/Level/level-js/issues/107), [#115](https://github.com/Level/level-js/issues/115)) ([**@ralphtheninja**](https://github.com/ralphtheninja), [**@vweevers**](https://github.com/vweevers)) - Add `UPGRADING.md` ([#143](https://github.com/Level/level-js/issues/143)) ([**@vweevers**](https://github.com/vweevers)) - Add `CONTRIBUTORS.md` (replaces `COLLABORATORS.md`) ([#141](https://github.com/Level/level-js/issues/141)) ([**@vweevers**](https://github.com/vweevers)) - Add `standard` ([#112](https://github.com/Level/level-js/issues/112)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) - Document constructor ([#119](https://github.com/Level/level-js/issues/119)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) - Document type support ([#125](https://github.com/Level/level-js/issues/125), [#143](https://github.com/Level/level-js/issues/143)) ([**@vweevers**](https://github.com/vweevers)) - Add `remark` tooling ([#141](https://github.com/Level/level-js/issues/141), [#143](https://github.com/Level/level-js/issues/143), [#147](https://github.com/Level/level-js/issues/147)) ([**@vweevers**](https://github.com/vweevers)) - Test default and custom prefix ([#124](https://github.com/Level/level-js/issues/124)) ([**@vweevers**](https://github.com/vweevers)) - Test all key types of IndexedDB Second Edition ([#130](https://github.com/Level/level-js/issues/130)) ([**@vweevers**](https://github.com/vweevers)) - Test illegal value types ([#118](https://github.com/Level/level-js/issues/118)) ([**@vweevers**](https://github.com/vweevers)) - Test illegal and stringified key types ([#139](https://github.com/Level/level-js/issues/139)) ([**@vweevers**](https://github.com/vweevers)) - Test `Buffer`, `ArrayBuffer` and `Uint8Array` values with `asBuffer` option ([#146](https://github.com/Level/level-js/issues/146)) ([**@vweevers**](https://github.com/vweevers)) ### Fixed - Add original copyright owner (Max Ogden) ([#141](https://github.com/Level/level-js/issues/141)) ([**@vweevers**](https://github.com/vweevers)) - Replace `level.js` in documentation to match npm name `level-js` ([#121](https://github.com/Level/level-js/issues/121)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) - Force airtap's browserify to use latest `buffer@5` ([#122](https://github.com/Level/level-js/issues/122)) ([**@vweevers**](https://github.com/vweevers)) - Don't stringify keys (except fallbacks, booleans and `NaN`) ([#130](https://github.com/Level/level-js/issues/130)) ([**@vweevers**](https://github.com/vweevers)) - Fix conversion of `ArrayBuffer` cursor key to `Buffer` ([#130](https://github.com/Level/level-js/issues/130)) ([**@vweevers**](https://github.com/vweevers)) - Catch IndexedDB key and value errors ([#139](https://github.com/Level/level-js/issues/139)) ([**@vweevers**](https://github.com/vweevers)) - Use `setImmediate` with callback in `_close()` ([#111](https://github.com/Level/level-js/issues/111)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) - Whitelist npm package files ([#126](https://github.com/Level/level-js/issues/126)) ([**@vweevers**](https://github.com/vweevers)) - Avoid `instanceof Date` for cross-realm support ([#129](https://github.com/Level/level-js/issues/129)) ([**@vweevers**](https://github.com/vweevers)) - Fix wrong release date for `3.0.0-rc1` ([`43a702b`](https://github.com/Level/level-js/commit/43a702b)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) ### Removed - Remove `test/levelup-test.js` ([#134](https://github.com/Level/level-js/issues/134)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) - Remove `levelup` from destroy tests ([#136](https://github.com/Level/level-js/issues/136)) ([**@ralphtheninja**](https://github.com/ralphtheninja)) ## [3.0.0-rc1] - 2018-05-26 ### Changed - Upgrade `abstract-leveldown` from `0.12.0` to `5.0.0` ([**@vweevers**](https://github.com/vweevers)) - Upgrade `typedarray-to-buffer` from `1.0.0` to `3.1.5` ([**@vweevers**](https://github.com/vweevers)) - Upgrade `levelup` devDependency from `0.18.2` to `3.0.0` ([**@vweevers**](https://github.com/vweevers)) - Upgrade `browserify` devDependency from `4.1.2` to `16.2.2` ([**@vweevers**](https://github.com/vweevers)) - Switch license from BSD to MIT ([**@ralphtheninja**](https://github.com/ralphtheninja)) - Replace `IDBWrapper` with straight IndexedDB code ([**@vweevers**](https://github.com/vweevers)) - Change default database prefix from `IDBWrapper-` to `level-js-` ([**@vweevers**](https://github.com/vweevers)) - Implement abstract `#_serializeKey` with support of all IndexedDB Second Edition types including binary keys (as Buffers) ([**@vweevers**](https://github.com/vweevers)) - Implement abstract `#_serializeValue` with support of all types of the structured clone algorithm except for `null` and `undefined` ([**@vweevers**](https://github.com/vweevers)) - Use `immediate` module for consistent microtask behavior ([**@vweevers**](https://github.com/vweevers)) - Replace `Buffer()` with `Buffer.from()` ([**@vweevers**](https://github.com/vweevers)) - Rename `Iterator#iterator` to `#transaction` ([**@vweevers**](https://github.com/vweevers)) - Replace `beefy` with `airtap --local` for local testing ([**@vweevers**](https://github.com/vweevers)) - Homogenize README title, description and headers ([**@vweevers**](https://github.com/vweevers)) - Make real `tape` tests out of `test-levelup.js` ([**@vweevers**](https://github.com/vweevers)) - Restructure custom tests to follow abstract test suite format ([**@vweevers**](https://github.com/vweevers)) ### Added - Add continuous browser tests with `airtap` and Sauce Labs ([**@vweevers**](https://github.com/vweevers)) - Add `prefix` and `version` options to constructor ([**@vweevers**](https://github.com/vweevers)) - Detect binary key support and fallback to `String(buffer)` ([**@vweevers**](https://github.com/vweevers)) - Detect array key support and fallback to `String(array)` ([**@vweevers**](https://github.com/vweevers)) - Test all value types of the structured clone algorithm ([**@vweevers**](https://github.com/vweevers)) - Catch `DataCloneError` if the environment does not support serializing the type of a key or value ([**@vweevers**](https://github.com/vweevers)) - Include Promise polyfill for `levelup` integration tests ([**@vweevers**](https://github.com/vweevers)) - Test that `Iterator` stringifies `Buffer.from()` argument ([**@vweevers**](https://github.com/vweevers)) - Add README badges, new goals and a code example with `levelup` ([**@vweevers**](https://github.com/vweevers)) - Add npm files to `.gitignore` ([**@vweevers**](https://github.com/vweevers)) ### Fixed - Start `Iterator` cursor immediately and fill an in-memory cache to fulfill `abstract-leveldown` snapshot guarantees ([**@vweevers**](https://github.com/vweevers)) - Stop advancing `Iterator` cursor when `options.limit` is reached ([**@vweevers**](https://github.com/vweevers)) - Rename public `#iterator` to private `#_iterator` ([**@vweevers**](https://github.com/vweevers)) - Fix `#_iterator({ limit: 0 })` to yield 0 entries ([**@vweevers**](https://github.com/vweevers)) - Handle transaction errors in `Iterator` ([**@vweevers**](https://github.com/vweevers)) - Fix constructor to call super ([**@vweevers**](https://github.com/vweevers)) - Make one request at a time in a batch transaction, saving CPU time ([**@vweevers**](https://github.com/vweevers)) - Properly close and destroy db's in custom tests ([**@vweevers**](https://github.com/vweevers)) - Update README links ([**@vweevers**](https://github.com/vweevers)) ### Removed - Remove support of `ArrayBuffer` values in favor of `Buffer` ([**@vweevers**](https://github.com/vweevers)) - Remove now unneeded `raw` option from `#_get()` and `#_iterator()` ([**@vweevers**](https://github.com/vweevers)) - Run tests without `IndexedDBShim` ([**@vweevers**](https://github.com/vweevers)) - Remove `Buffer` to `Uint8Array` conversion in `#_put()` and `#_batch()` ([**@vweevers**](https://github.com/vweevers)) - Remove obsolete `#_approximateSize` ([**@vweevers**](https://github.com/vweevers)) - Remove obsolete `#_isBuffer` ([**@vweevers**](https://github.com/vweevers)) - Remove obsolete `testBuffer` from abstract tests ([**@vweevers**](https://github.com/vweevers)) - Remove obsolete writestream test from `test-levelup.js` ([**@vweevers**](https://github.com/vweevers)) - Rely on `abstract-leveldown` defaults in `Iterator` constructor ([**@vweevers**](https://github.com/vweevers)) - Rely on `abstract-leveldown` callback defaults ([**@vweevers**](https://github.com/vweevers)) - Remove testling from `package.json` ([**@vweevers**](https://github.com/vweevers)) - Remove `level.js` logo ([**@vweevers**](https://github.com/vweevers)) ## [2.2.4] - 2016-05-09 ### Changed - Use `toArrayBuffer()` only when present ([**@substack**](https://github.com/substack)) ## [2.2.3] - 2015-12-10 ### Changed - Update `ltgt` to `^2.1.2` ([**@ryanramage**](https://github.com/ryanramage)) ## [2.2.2] - 2015-09-12 _This release introduced `this._keyRangeError`._ ### Added - Add [**@nolanlawson**](https://github.com/nolanlawson) to collaborators ([**@maxogden**](https://github.com/maxogden)) ### Fixed - Fix iterator when start > end ([**@nolanlawson**](https://github.com/nolanlawson)) ## [2.2.1] - 2015-07-05 ### Changed - Update collaborators ([**@maxogden**](https://github.com/maxogden)) - Roll back `abstract-leveldown` to `~0.12.0` ([**@maxogden**](https://github.com/maxogden)) ## [2.2.0] - 2015-07-03 ### Added - Add `Collaborators` section to README ([**@maxogden**](https://github.com/maxogden)) ### Changed - Update syntax highlighting in README ([**@yoshuawuyts**](https://github.com/yoshuawuyts)) - Update `idb-wrapper` to `^1.5.0` ([**@JamesKyburz**](https://github.com/JamesKyburz)) - Update `abstract-leveldown` to `^2.4.0` ([**@maxogden**](https://github.com/maxogden)) - Update `tape` to `^4.0.0` ([**@maxogden**](https://github.com/maxogden)) - Move `tape` to devDependencies ([**@maxogden**](https://github.com/maxogden)) - Change license from BSD to BSD-2-Clause ([**@maxogden**](https://github.com/maxogden)) ### Removed - Remove Testling badge ([**@maxogden**](https://github.com/maxogden)) ## [2.1.6] - 2014-06-15 ### Fixed - Avoid using keyword in `cursor.continue()` ([**@nolanlawson**](https://github.com/nolanlawson)) ## [2.1.5] - 2014-05-29 ### Changed - Use `ltgt` module to handle ranges ([**@dominictarr**](https://github.com/dominictarr)) ## [2.1.4] - 2014-05-13 ### Changed - Update `browserify` to `^4.1.2` ([**@maxogden**](https://github.com/maxogden)) - Move `browserify` to devDependencies ([**@maxogden**](https://github.com/maxogden)) ## [2.1.3] - 2014-04-09 ### Added - Use `typedarray-to-buffer` to avoid copying to Buffer ([**@mafintosh**](https://github.com/mafintosh)) ## [2.1.2] - 2014-04-05 ### Added - Add link to [**@brycebaril**](https://github.com/brycebaril)'s presentation to README ([**@maxogden**](https://github.com/maxogden)) ### Changed - Update browser configuration for Testling ([**@maxogden**](https://github.com/maxogden)) ## [2.1.1] - 2014-03-12 _This was not published to npm. There's also a gap between `2.1.1` and `2.0.0` that is inconsistent. The `options.raw` property was introduced in this release._ ### Changed - Update browser configuration for Testling ([**@maxogden**](https://github.com/maxogden)) - Update `abstract-leveldown` to `~0.12.0` ([**@maxogden**](https://github.com/maxogden)) - Update `levelup` to `~0.18.2` ([**@maxogden**](https://github.com/maxogden)) - Make sure to store `Uint8Array` ([**@maxogden**](https://github.com/maxogden)) - Test storing native JS types with raw = true ([**@maxogden**](https://github.com/maxogden)) ## [2.0.0] - 2014-03-09 _For some reason both `tape` and `browserify` were moved from devDependencies to dependencies. This release only had one commit._ ### Changed - Update `browserify` to `~3.32.0` ([**@maxogden**](https://github.com/maxogden)) - Update `tape` to `~2.10.2` ([**@maxogden**](https://github.com/maxogden)) - Change default encoding of values to strings to more closely match `leveldown` ([**@maxogden**](https://github.com/maxogden)) ### Fixed - Add missing `xtend` dependency ([**@maxogden**](https://github.com/maxogden)) ## [1.2.0] - 2014-03-09 ### Added - Add `IndexedDBShim` to tests ([**@maxogden**](https://github.com/maxogden)) - Add `Level.destroy()` ([**@qs44**](https://github.com/qs44)) - Add prefix to pass `PouchDB` tests ([**@qs44**](https://github.com/qs44)) - Test `Level.destroy()` ([**@calvinmetcalf**](https://github.com/calvinmetcalf)) ### Changed - Update browser configuration for Testling ([**@maxogden**](https://github.com/maxogden)) - Pass through open options to idbwrapper ([**@maxogden**](https://github.com/maxogden)) ### Fixed - Don't use `indexedDB.webkitGetDatabasesNames()` in tests ([**@maxogden**](https://github.com/maxogden)) ## [1.1.2] - 2014-02-02 ### Removed - Remove global leaks ([**@mcollina**](https://github.com/mcollina)) ## [1.1.1] - 2014-02-02 ### Changed - Modify a copy of the batch array, not the original ([**@nrw**](https://github.com/nrw)) ### Fixed - Fix broken `package.json` ([**@maxogden**](https://github.com/maxogden)) - Fix testling path ([**@maxogden**](https://github.com/maxogden)) ## [1.1.0] - 2014-01-30 _In this time period `bops` shows up and gets removed. Also, `._isBuffer()` uses `Buffer.isBuffer()` in favor of `is-buffer` module._ ### Added - Add Testling ([**@maxogden**](https://github.com/maxogden)) - Add npm badge ([**@maxogden**](https://github.com/maxogden)) - Test ranges ([**@rvagg**](https://github.com/rvagg), [**@maxogden**](https://github.com/maxogden)) ### Changed - Update README ([**@maxogden**](https://github.com/maxogden)) - Update `abstract-leveldown` to `~0.11.0` ([**@rvagg**](https://github.com/rvagg), [**@maxogden**](https://github.com/maxogden)) - Update to work with `abstract-leveldown@0.11.2` ([**@shama**](https://github.com/shama), [**@maxogden**](https://github.com/maxogden)) - Update iterator to pass all range tests ([**@shama**](https://github.com/shama), [**@maxogden**](https://github.com/maxogden)) ### Fixed - Fix incorrect version of `abstract-leveldown` ([**@maxogden**](https://github.com/maxogden)) - Pass error to callback in `approximateSize()` ([**@mcollina**](https://github.com/mcollina)) ### Removed - Remove unnecessary factor in tests ([**@rvagg**](https://github.com/rvagg), [**@maxogden**](https://github.com/maxogden)) ## [1.0.8] - 2013-08-12 ### Changed - Move `levelup` to devDependencies ([**@juliangruber**](https://github.com/juliangruber)) ### Removed - Remove fn#bind from iterator ([**@juliangruber**](https://github.com/juliangruber)) ## [1.0.7] - 2013-07-02 ### Changed - Implement full batch support ([**@mcollina**](https://github.com/mcollina)) ### Fixed - Fix git url to `abstract-leveldown` ([**@maxogden**](https://github.com/maxogden)) ## [1.0.6] - 2013-05-31 ### Changed - Update `idb-wrapper` to `1.2.0` ([**@maxogden**](https://github.com/maxogden)) - Switch `abstract-leveldown#master` ([**@maxogden**](https://github.com/maxogden)) - Disable batch and chainable batch tests ([**@maxogden**](https://github.com/maxogden)) ## [1.0.5] - 2013-05-30 ### Changed - Use upstream `idb-wrapper` ([**@maxogden**](https://github.com/maxogden)) ## [1.0.4] - 2013-05-30 ### Added - Test batch and chainable batch ([**@rvagg**](https://github.com/rvagg)) ### Changed - Update `abstract-leveldown` to `~0.7.1` ([**@rvagg**](https://github.com/rvagg)) - Update `levelup` to `~0.9.0` ([**@brycebaril**](https://github.com/brycebaril)) ## [1.0.3] - 2013-05-14 ### Changed - Use `is-buffer` ([**@juliangruber**](https://github.com/juliangruber)) ## [1.0.2] - 2013-05-04 ### Fixed - Don't convert `ArrayBuffer` and typed arrays to strings ([**@maxogden**](https://github.com/maxogden)) ## [1.0.1] - 2013-05-03 ### Added - Add optional options argument to `.open()` ([**@rvagg**](https://github.com/rvagg)) - Add `test-levelup.js` ([**@maxogden**](https://github.com/maxogden)) ### Changed - Update README ([**@maxogden**](https://github.com/maxogden)) - Use `npm test` instead of `npm start` ([**@shama**](https://github.com/shama)) - Properly delete test dbs ([**@maxogden**](https://github.com/maxogden)) - Inherit from `abstract-leveldown` ([**@rvagg**](https://github.com/rvagg)) ## [1.0.0] - 2013-05-03 :seedling: Initial release. [6.1.0]: https://github.com/Level/level-js/releases/tag/v6.1.0 [6.0.0]: https://github.com/Level/level-js/releases/tag/v6.0.0 [5.0.2]: https://github.com/Level/level-js/releases/tag/v5.0.2 [5.0.1]: https://github.com/Level/level-js/releases/tag/v5.0.1 [5.0.0]: https://github.com/Level/level-js/releases/tag/v5.0.0 [4.0.1]: https://github.com/Level/level-js/releases/tag/v4.0.1 [4.0.0]: https://github.com/Level/level-js/releases/tag/v4.0.0 [3.0.0]: https://github.com/Level/level-js/releases/tag/v3.0.0 [3.0.0-rc1]: https://github.com/Level/level-js/releases/tag/v3.0.0-rc1 [2.2.4]: https://github.com/Level/level-js/releases/tag/v2.2.4 [2.2.3]: https://github.com/Level/level-js/releases/tag/v2.2.3 [2.2.2]: https://github.com/Level/level-js/releases/tag/v2.2.2 [2.2.1]: https://github.com/Level/level-js/releases/tag/v2.2.1 [2.2.0]: https://github.com/Level/level-js/releases/tag/v2.2.0 [2.1.6]: https://github.com/Level/level-js/releases/tag/v2.1.6 [2.1.5]: https://github.com/Level/level-js/releases/tag/v2.1.5 [2.1.4]: https://github.com/Level/level-js/releases/tag/v2.1.4 [2.1.3]: https://github.com/Level/level-js/releases/tag/v2.1.3 [2.1.2]: https://github.com/Level/level-js/releases/tag/v2.1.2 [2.1.1]: https://github.com/Level/level-js/releases/tag/v2.1.1 [2.0.0]: https://github.com/Level/level-js/releases/tag/v2.0.0 [1.2.0]: https://github.com/Level/level-js/releases/tag/v1.2.0 [1.1.2]: https://github.com/Level/level-js/releases/tag/v1.1.2 [1.1.1]: https://github.com/Level/level-js/releases/tag/v1.1.1 [1.1.0]: https://github.com/Level/level-js/releases/tag/v1.1.0 [1.0.8]: https://github.com/Level/level-js/releases/tag/v1.0.8 [1.0.7]: https://github.com/Level/level-js/releases/tag/v1.0.7 [1.0.6]: https://github.com/Level/level-js/releases/tag/v1.0.6 [1.0.5]: https://github.com/Level/level-js/releases/tag/v1.0.5 [1.0.4]: https://github.com/Level/level-js/releases/tag/v1.0.4 [1.0.3]: https://github.com/Level/level-js/releases/tag/v1.0.3 [1.0.2]: https://github.com/Level/level-js/releases/tag/v1.0.2 [1.0.1]: https://github.com/Level/level-js/releases/tag/v1.0.1 [1.0.0]: https://github.com/Level/level-js/releases/tag/v1.0.0 ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright © 2012 Max Ogden and the contributors to level-js. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # level-js **Superseded by [`browser-level`](https://github.com/Level/browser-level). Please see [Frequently Asked Questions](https://github.com/Level/community#faq).** ## Background Here are the goals of `level-js`: - Store large amounts of data in modern browsers - Pass the full [`abstract-leveldown`][abstract-leveldown] test suite - Support string and [`Buffer`][buffer] keys and values - Be as fast as possible - ~~Sync with [multilevel](https://github.com/juliangruber/multilevel) over ASCII or binary transports.~~ Being `abstract-leveldown` compliant means you can use many of the [Level modules][awesome] on top of this library. ## Example **If you are upgrading:** please see [UPGRADING.md](UPGRADING.md). ```js const levelup = require('levelup') const leveljs = require('level-js') const db = levelup(leveljs('bigdata')) db.put('hello', Buffer.from('world'), function (err) { if (err) throw err db.get('hello', function (err, value) { if (err) throw err console.log(value.toString()) // 'world' }) }) ``` With `async/await`: ```js const levelup = require('levelup') const leveljs = require('level-js') const db = levelup(leveljs('bigdata')) await db.put('hello', Buffer.from('world')) const value = await db.get('hello') ``` ## Type Support Keys and values can be a string or [`Buffer`][buffer]. Any other type will be irreversibly stringified. The only exceptions are `null` and `undefined`. Keys and values of that type are rejected. In order to sort string and Buffer keys the same way, for compatibility with `leveldown` and the larger ecosystem, `level-js` internally converts keys and values to binary before passing them to IndexedDB. If you desire non-destructive encoding (e.g. to store and retrieve numbers as-is), wrap `level-js` with [`encoding-down`][encoding-down]. Alternatively install [`level`][level] which conveniently bundles [`levelup`][levelup], `level-js` and `encoding-down`. Such an approach is also recommended if you want to achieve universal (isomorphic) behavior. For example, you could have [`leveldown`][leveldown] in a backend and `level-js` in the frontend. The `level` package does exactly that. When getting or iterating keys and values, regardless of the type with which they were stored, keys and values will return as a Buffer unless the `asBuffer`, `keyAsBuffer` or `valueAsBuffer` options are set, in which case strings are returned. Setting these options is not needed when `level-js` is wrapped with `encoding-down`, which determines the optimal return type by the chosen encoding. ```js db.get('key', { asBuffer: false }) db.iterator({ keyAsBuffer: false, valueAsBuffer: false }) ``` ## Install With [npm](https://npmjs.org) do: ```bash npm install level-js ``` Not to be confused with [leveljs](https://www.npmjs.com/package/leveljs). This library is best used with [browserify](http://browserify.org). ## API ### `db = leveljs(location[, options])` Returns a new `leveljs` instance. `location` is the string name of the [`IDBDatabase`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase) to be opened, as well as the object store within that database. The database name will be prefixed with `options.prefix`. #### `options` The optional `options` argument may contain: - `prefix` _(string, default: `'level-js-'`)_: Prefix for `IDBDatabase` name. - `version` _(string | number, default: `1`)_: The version to open the database with. See [`IDBFactory#open`](https://developer.mozilla.org/en-US/docs/Web/API/IDBFactory/open) for more details. ## Big Thanks Cross-browser Testing Platform and Open Source ♥ Provided by [Sauce Labs](https://saucelabs.com). [![Sauce Labs logo](./sauce-labs.svg)](https://saucelabs.com) ## Contributing [`Level/level-js`](https://github.com/Level/level-js) is an **OPEN Open Source Project**. This means that: > 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. See the [Contribution Guide](https://github.com/Level/community/blob/master/CONTRIBUTING.md) for more details. ## Donate Support us with a monthly donation on [Open Collective](https://opencollective.com/level) and help us continue our work. ## License [MIT](LICENSE) [indexeddb]: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API [buffer]: https://nodejs.org/api/buffer.html [awesome]: https://github.com/Level/awesome [abstract-leveldown]: https://github.com/Level/abstract-leveldown [levelup]: https://github.com/Level/levelup [leveldown]: https://github.com/Level/leveldown [level]: https://github.com/Level/level [encoding-down]: https://github.com/Level/encoding-down ================================================ FILE: UPGRADING.md ================================================ # Upgrade Guide 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]. ## 6.0.0 Legacy range options have been removed ([Level/community#86](https://github.com/Level/community/issues/86)). If you previously did: ```js db.iterator({ start: 'a', end: 'z' }) ``` An error would now be thrown and you must instead do: ```js db.iterator({ gte: 'a', lte: 'z' }) ``` This release also drops support of legacy runtime environments ([Level/community#98](https://github.com/Level/community/issues/98)): - Internet Explorer 11 - Safari 9-11. Lastly, and less likely to be a breaking change, the [`immediate`](https://github.com/calvinmetcalf/immediate) browser shim for `process.nextTick()` has been replaced with the smaller [`queue-microtask`](https://github.com/feross/queue-microtask). ## 5.0.0 Support of keys & values other than strings and Buffers has been dropped. Internally `level-js` now stores keys & values as binary which solves a number of compatibility issues ([Level/memdown#186](https://github.com/Level/memdown/issues/186)). If you pass in a key or value that isn't a string or Buffer, it will be irreversibly stringified. Existing IndexedDB databases created with `level-js@4` can be read only if they used binary keys and string or binary values. Other types will come out stringified, and string keys will sort incorrectly. Use the included `upgrade()` utility to convert stored data to binary (in so far the environment supports it): ```js var leveljs = require('level-js') var db = leveljs('my-db') db.open(function (err) { if (err) throw err db.upgrade(function (err) { if (err) throw err }) }) ``` Or with (the upcoming release of) `level`: ```js var level = require('level') var reachdown = require('reachdown') var db = level('my-db') db.open(function (err) { if (err) throw err reachdown(db, 'level-js').upgrade(function (err) { if (err) throw err }) }) ``` ## 4.0.0 This is an upgrade to `abstract-leveldown@6` which solves long-standing issues around serialization and type support. ### Range options are now serialized Previously, range options like `lt` were passed through as-is by `abstract-leveldown`, unlike keys. For `level-js` it means that Buffers and arrays, if not supported by the environment (e.g. Microsoft Edge), will be stringified. ### The rules for range options have been relaxed Because `null`, `undefined`, zero-length strings and zero-length buffers are significant types in encodings like `bytewise` and `charwise`, they became valid as range options in `abstract-leveldown`. This means `db.iterator({ gt: undefined })` is not the same as `db.iterator({})`. In the case of `level-js`, when used by itself, the aforementioned change means that `db.iterator({ gt: undefined })` will throw an error as `undefined` is not a valid IndexedDB key type. On the other hand `db.iterator({ gt: '' })` is valid and thus now supported. For details on sort order (which is richer than in `leveldown`) please see [the readme](README.md). ### Nullish values are rejected In addition to rejecting `null` and `undefined` as _keys_, `abstract-leveldown` now also rejects these types as _values_, due to preexisting significance in streams and iterators. ### Zero-length array keys are rejected Though this was already the case (both in IndexedDB and `abstract-leveldown`), `abstract-leveldown` has replaced the behavior with an explicit `Array.isArray()` check and a new error message. ### Boolean and `NaN` keys (as well as range options) are rejected Previously, for compliance with `abstract-leveldown` tests that have since been removed, they were stringified. As of `level-js@4` they are rejected (by IndexedDB). ### Added mobile browser support iPhone and Android `latest` are now officially supported. At the time of writing that's iPhone 12.0 and Android 7.1 (note that's Chrome for Android, not the old stock browser). Older versions (iPhone 10+ and Android 6+) did pass our tests but are not included in the test matrix going forward. Feel free to open an issue if you need/want these versions to be supported. ### The value of `iterator#db` has changed Though this was undocumented and only for internal use, the `db` property on an iterator pointed to an `IDBDatabase`. To comply with `abstract-leveldown` the `db` property now points to the `level-js` instance that created that iterator. ## 3.0.0 This release brings `level-js` up to par with latest [`levelup`][levelup] (v2), [`abstract-leveldown`][abstract-leveldown] (v5) and IndexedDB Second Edition. It targets modern `browserify` preferring [`Buffer`][buffer] over `ArrayBuffer`. Lastly, [`IDBWrapper`][idbwrapper] has been replaced with straight IndexedDB code. ### Usage with [`levelup`][levelup] Usage has changed to: ```js const levelup = require('levelup') const leveljs = require('leveljs') const db = levelup(leveljs('mydb')) ``` From the old: ```js const db = levelup('mydb', { db: leveljs }) ``` Friendly reminder: encodings have moved from [`levelup`][levelup] to [`encoding-down`][encoding-down]. To get identical functionality to `levelup < 2` please use the [`level-browserify`][level-browserify] convenience package or wrap `level-js` with `encoding-down`: ```js const encode = require('encoding-down') const db = levelup(encode(leveljs('mydb'))) ``` ### New database prefix The default prefix of the [`IDBDatabase`][idbdatabase] name has changed from `IDBWrapper-` to `level-js-`. To access databases created using `level-js < 3`, pass a custom prefix to the `level-js` constructor: ```js const db = levelup(leveljs('mydb', { prefix: 'IDBWrapper-' })) ``` ### Browser support As a result of removing [`IDBWrapper`][idbwrapper], only modern browsers with a non-prefixed `window.indexedDB` are supported in this release. The current test matrix of `level-js` includes the latest versions of Chrome, Firefox, Safari, Edge and IE. :fire: Internet Explorer 10 is no longer supported. ### Type support All value types of the [structured clone algorithm][structured-clone-algorithm] and all key types of IndexedDB Second Edition are now supported. This means you can store almost any JavaScript type without the need for [`encoding-down`][encoding-down]. In addition, you can use [`Buffer`][buffer] for both keys and values. For details and caveats please see the [readme][readme]. ### No backpressure In `level-js`, iterators are powered by IndexedDB cursors. To fulfill [`abstract-leveldown`][abstract-leveldown] snapshot guarantees (reads not being affected by simultaneous writes) cursors are started immediately and continuously read from, filling an in-memory cache. Though `level-js` now passes the full [`abstract-leveldown`][abstract-leveldown] test suite, fulfilling the snapshot guarantee means a loss of backpressure. Memory consumption might increase if an iterator is not consumed fast enough. A future release will have an option to favor backpressure over snapshot guarantees. ### Removed `raw` option Because `level-js` no longer stringifies values, the `raw` option (which bypassed conversion) became unnecessary and has been removed. If you use [`level-browserify`][level-browserify] or [`levelup`][levelup] with [`encoding-down`][encoding-down], you can store and retrieve raw values (as returned by IndexedDB) using the `id` encoding. Please refer to the [readme][readme] for an example. ### New `destroy()` function signature Previously, a `level-js` instance could be passed to `destroy()`: ```js leveljs.destroy(db, callback) ``` This was useful to destroy a database that used a custom prefix. The new signature is `destroy(location[, prefix], callback)`. ### Strict `.batch(array)` The upgrade to [`abstract-leveldown`][abstract-leveldown] comes 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. If you previously passed arrays to `.batch()` that contained `undefined` or `null`, they would be silently ignored. Now this will produce an error. [readme]: README.md [changelog]: CHANGELOG.md [buffer]: https://nodejs.org/api/buffer.html [idbwrapper]: https://www.npmjs.com/package/idb-wrapper [abstract-leveldown]: https://github.com/Level/abstract-leveldown [levelup]: https://github.com/Level/levelup [encoding-down]: https://github.com/Level/encoding-down [level-browserify]: https://github.com/Level/level-browserify [idbdatabase]: https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase [structured-clone-algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm ================================================ FILE: index.js ================================================ /* global indexedDB */ 'use strict' module.exports = Level const AbstractLevelDOWN = require('abstract-leveldown').AbstractLevelDOWN const inherits = require('inherits') const parallel = require('run-parallel-limit') const Iterator = require('./iterator') const serialize = require('./util/serialize') const deserialize = require('./util/deserialize') const support = require('./util/support') const clear = require('./util/clear') const createKeyRange = require('./util/key-range') const DEFAULT_PREFIX = 'level-js-' function Level (location, opts) { if (!(this instanceof Level)) return new Level(location, opts) AbstractLevelDOWN.call(this, { bufferKeys: support.bufferKeys(indexedDB), snapshots: true, permanence: true, clear: true, getMany: true }) opts = opts || {} if (typeof location !== 'string') { throw new Error('constructor requires a location string argument') } this.location = location this.prefix = opts.prefix == null ? DEFAULT_PREFIX : opts.prefix this.version = parseInt(opts.version || 1, 10) } inherits(Level, AbstractLevelDOWN) Level.prototype.type = 'level-js' Level.prototype._open = function (options, callback) { const req = indexedDB.open(this.prefix + this.location, this.version) req.onerror = function () { callback(req.error || new Error('unknown error')) } req.onsuccess = () => { this.db = req.result callback() } req.onupgradeneeded = (ev) => { const db = ev.target.result if (!db.objectStoreNames.contains(this.location)) { db.createObjectStore(this.location) } } } Level.prototype.store = function (mode) { const transaction = this.db.transaction([this.location], mode) return transaction.objectStore(this.location) } Level.prototype.await = function (request, callback) { const transaction = request.transaction // Take advantage of the fact that a non-canceled request error aborts // the transaction. I.e. no need to listen for "request.onerror". transaction.onabort = function () { callback(transaction.error || new Error('aborted by user')) } transaction.oncomplete = function () { callback(null, request.result) } } Level.prototype._get = function (key, options, callback) { const store = this.store('readonly') let req try { req = store.get(key) } catch (err) { return this._nextTick(callback, err) } this.await(req, function (err, value) { if (err) return callback(err) if (value === undefined) { // 'NotFound' error, consistent with LevelDOWN API return callback(new Error('NotFound')) } callback(null, deserialize(value, options.asBuffer)) }) } Level.prototype._getMany = function (keys, options, callback) { const asBuffer = options.asBuffer const store = this.store('readonly') const tasks = keys.map((key) => (next) => { let request try { request = store.get(key) } catch (err) { return next(err) } request.onsuccess = () => { const value = request.result next(null, value === undefined ? value : deserialize(value, asBuffer)) } request.onerror = (ev) => { ev.stopPropagation() next(request.error) } }) parallel(tasks, 16, callback) } Level.prototype._del = function (key, options, callback) { const store = this.store('readwrite') let req try { req = store.delete(key) } catch (err) { return this._nextTick(callback, err) } this.await(req, callback) } Level.prototype._put = function (key, value, options, callback) { const store = this.store('readwrite') let req try { // Will throw a DataError or DataCloneError if the environment // does not support serializing the key or value respectively. req = store.put(value, key) } catch (err) { return this._nextTick(callback, err) } this.await(req, callback) } Level.prototype._serializeKey = function (key) { return serialize(key, this.supports.bufferKeys) } Level.prototype._serializeValue = function (value) { return serialize(value, true) } Level.prototype._iterator = function (options) { return new Iterator(this, this.location, options) } Level.prototype._batch = function (operations, options, callback) { if (operations.length === 0) return this._nextTick(callback) const store = this.store('readwrite') const transaction = store.transaction let index = 0 let error transaction.onabort = function () { callback(error || transaction.error || new Error('aborted by user')) } transaction.oncomplete = function () { callback() } // Wait for a request to complete before making the next, saving CPU. function loop () { const op = operations[index++] const key = op.key let req try { req = op.type === 'del' ? store.delete(key) : store.put(op.value, key) } catch (err) { error = err transaction.abort() return } if (index < operations.length) { req.onsuccess = loop } } loop() } Level.prototype._clear = function (options, callback) { let keyRange let req try { keyRange = createKeyRange(options) } catch (e) { // The lower key is greater than the upper key. // IndexedDB throws an error, but we'll just do nothing. return this._nextTick(callback) } if (options.limit >= 0) { // IDBObjectStore#delete(range) doesn't have such an option. // Fall back to cursor-based implementation. return clear(this, this.location, keyRange, options, callback) } try { const store = this.store('readwrite') req = keyRange ? store.delete(keyRange) : store.clear() } catch (err) { return this._nextTick(callback, err) } this.await(req, callback) } Level.prototype._close = function (callback) { this.db.close() this._nextTick(callback) } // NOTE: remove in a next major release Level.prototype.upgrade = function (callback) { if (this.status !== 'open') { return this._nextTick(callback, new Error('cannot upgrade() before open()')) } const it = this.iterator() const batchOptions = {} const self = this it._deserializeKey = it._deserializeValue = identity next() function next (err) { if (err) return finish(err) it.next(each) } function each (err, key, value) { if (err || key === undefined) { return finish(err) } const newKey = self._serializeKey(deserialize(key, true)) const newValue = self._serializeValue(deserialize(value, true)) // To bypass serialization on the old key, use _batch() instead of batch(). // NOTE: if we disable snapshotting (#86) this could lead to a loop of // inserting and then iterating those same entries, because the new keys // possibly sort after the old keys. self._batch([ { type: 'del', key: key }, { type: 'put', key: newKey, value: newValue } ], batchOptions, next) } function finish (err) { it.end(function (err2) { callback(err || err2) }) } function identity (data) { return data } } Level.destroy = function (location, prefix, callback) { if (typeof prefix === 'function') { callback = prefix prefix = DEFAULT_PREFIX } const request = indexedDB.deleteDatabase(prefix + location) request.onsuccess = function () { callback() } request.onerror = function (err) { callback(err) } } ================================================ FILE: iterator.js ================================================ 'use strict' const inherits = require('inherits') const AbstractIterator = require('abstract-leveldown').AbstractIterator const createKeyRange = require('./util/key-range') const deserialize = require('./util/deserialize') const noop = function () {} module.exports = Iterator function Iterator (db, location, options) { AbstractIterator.call(this, db) this._limit = options.limit this._count = 0 this._callback = null this._cache = [] this._completed = false this._aborted = false this._error = null this._transaction = null this._keys = options.keys this._values = options.values this._keyAsBuffer = options.keyAsBuffer this._valueAsBuffer = options.valueAsBuffer if (this._limit === 0) { this._completed = true return } let keyRange try { keyRange = createKeyRange(options) } catch (e) { // The lower key is greater than the upper key. // IndexedDB throws an error, but we'll just return 0 results. this._completed = true return } this.createIterator(location, keyRange, options.reverse) } inherits(Iterator, AbstractIterator) Iterator.prototype.createIterator = function (location, keyRange, reverse) { const transaction = this.db.db.transaction([location], 'readonly') const store = transaction.objectStore(location) const req = store.openCursor(keyRange, reverse ? 'prev' : 'next') req.onsuccess = (ev) => { const cursor = ev.target.result if (cursor) this.onItem(cursor) } this._transaction = transaction // If an error occurs (on the request), the transaction will abort. transaction.onabort = () => { this.onAbort(this._transaction.error || new Error('aborted by user')) } transaction.oncomplete = () => { this.onComplete() } } Iterator.prototype.onItem = function (cursor) { this._cache.push(cursor.key, cursor.value) if (this._limit <= 0 || ++this._count < this._limit) { cursor.continue() } this.maybeNext() } Iterator.prototype.onAbort = function (err) { this._aborted = true this._error = err this.maybeNext() } Iterator.prototype.onComplete = function () { this._completed = true this.maybeNext() } Iterator.prototype.maybeNext = function () { if (this._callback) { this._next(this._callback) this._callback = null } } Iterator.prototype._next = function (callback) { if (this._aborted) { // The error should be picked up by either next() or end(). const err = this._error this._error = null this._nextTick(callback, err) } else if (this._cache.length > 0) { let key = this._cache.shift() let value = this._cache.shift() if (this._keys && key !== undefined) { key = this._deserializeKey(key, this._keyAsBuffer) } else { key = undefined } if (this._values && value !== undefined) { value = this._deserializeValue(value, this._valueAsBuffer) } else { value = undefined } this._nextTick(callback, null, key, value) } else if (this._completed) { this._nextTick(callback) } else { this._callback = callback } } // Exposed for the v4 to v5 upgrade utility Iterator.prototype._deserializeKey = deserialize Iterator.prototype._deserializeValue = deserialize Iterator.prototype._end = function (callback) { if (this._aborted || this._completed) { return this._nextTick(callback, this._error) } // Don't advance the cursor anymore, and the transaction will complete // on its own in the next tick. This approach is much cleaner than calling // transaction.abort() with its unpredictable event order. this.onItem = noop this.onAbort = callback this.onComplete = callback } ================================================ FILE: package.json ================================================ { "name": "level-js", "version": "6.1.0", "description": "An abstract-leveldown compliant store on top of IndexedDB", "author": "max ogden", "license": "MIT", "main": "index.js", "scripts": { "test": "standard && hallmark && airtap -p local --coverage test/index.js", "test-browsers": "standard && airtap --coverage test/index.js", "coverage": "nyc report -r lcovonly", "hallmark": "hallmark --fix", "dependency-check": "dependency-check --no-dev -i buffer .", "prepublishOnly": "npm run dependency-check" }, "files": [ "index.js", "iterator.js", "util", "CHANGELOG.md", "UPGRADING.md", "sauce-labs.svg" ], "dependencies": { "abstract-leveldown": "^7.2.0", "buffer": "^6.0.3", "inherits": "^2.0.3", "ltgt": "^2.1.2", "run-parallel-limit": "^1.1.0" }, "devDependencies": { "airtap": "^4.0.1", "airtap-playwright": "^1.0.1", "airtap-sauce": "^1.1.0", "dependency-check": "^4.1.0", "hallmark": "^4.0.0", "level-concat-iterator": "^3.0.0", "nyc": "^15.0.0", "standard": "^16.0.3", "tape": "^5.0.0", "uuid": "^3.3.2" }, "repository": { "type": "git", "url": "https://github.com/Level/level-js.git" }, "homepage": "https://github.com/Level/level-js", "keywords": [ "level", "leveldb", "indexeddb", "abstract-leveldown" ] } ================================================ FILE: test/custom-test.js ================================================ 'use strict' const concat = require('level-concat-iterator') module.exports = function (leveljs, test, testCommon) { test('setUp', testCommon.setUp) test('default prefix', function (t) { const db = testCommon.factory() t.ok(db.location, 'instance has location property') t.is(db.prefix, 'level-js-', 'instance has prefix property') db.open(function (err) { t.notOk(err, 'no open error') const idb = db.db const databaseName = idb.name const storeNames = idb.objectStoreNames t.is(databaseName, 'level-js-' + db.location, 'database name is prefixed') t.is(storeNames.length, 1, 'created 1 object store') t.is(storeNames.item(0), db.location, 'object store name equals location') db.close(t.end.bind(t)) }) }) test('custom prefix', function (t) { const db = testCommon.factory({ prefix: 'custom-' }) t.ok(db.location, 'instance has location property') t.is(db.prefix, 'custom-', 'instance has prefix property') db.open(function (err) { t.notOk(err, 'no open error') const idb = db.db const databaseName = idb.name const storeNames = idb.objectStoreNames t.is(databaseName, 'custom-' + db.location, 'database name is prefixed') t.is(storeNames.length, 1, 'created 1 object store') t.is(storeNames.item(0), db.location, 'object store name equals location') db.close(t.end.bind(t)) }) }) test('empty prefix', function (t) { const db = testCommon.factory({ prefix: '' }) t.ok(db.location, 'instance has location property') t.is(db.prefix, '', 'instance has prefix property') db.open(function (err) { t.notOk(err, 'no open error') const idb = db.db const databaseName = idb.name const storeNames = idb.objectStoreNames t.is(databaseName, db.location, 'database name is prefixed') t.is(storeNames.length, 1, 'created 1 object store') t.is(storeNames.item(0), db.location, 'object store name equals location') db.close(t.end.bind(t)) }) }) test('put Buffer value, get Buffer value', function (t) { const level = testCommon.factory() level.open(function (err) { t.notOk(err, 'no error') level.put('key', Buffer.from('00ff', 'hex'), function (err) { t.notOk(err, 'no error') level.get('key', function (err, value) { t.notOk(err, 'no error') t.ok(Buffer.isBuffer(value), 'is buffer') t.same(value, Buffer.from('00ff', 'hex')) level.close(t.end.bind(t)) }) }) }) }) test('put Buffer value, get string value', function (t) { const level = testCommon.factory() level.open(function (err) { t.notOk(err, 'no error') level.put('key', Buffer.from('abc'), function (err) { t.notOk(err, 'no error') level.get('key', { asBuffer: false }, function (err, value) { t.notOk(err, 'no error') t.is(value, 'abc') level.close(t.end.bind(t)) }) }) }) }) test('put utf8 string, get utf8 string', function (t) { const level = testCommon.factory() level.open(function (err) { t.notOk(err, 'no error') level.put('💩', '💩', function (err) { t.notOk(err, 'no error') level.get('💩', { asBuffer: false }, function (err, value) { t.notOk(err, 'no error') t.is(value, '💩') level.close(t.end.bind(t)) }) }) }) }) // This should be covered by abstract-leveldown tests, but that's // prevented by process.browser checks (Level/abstract-leveldown#121). // This test is adapted from memdown. test('buffer keys', function (t) { const db = testCommon.factory() if (!db.supports.bufferKeys) { t.fail('environment does not support buffer keys') return t.end() } db.open(function (err) { t.ifError(err, 'no open error') const one = Buffer.from('80', 'hex') const two = Buffer.from('c0', 'hex') t.ok(two.toString() === one.toString(), 'would be equal when not buffer-aware') t.ok(Buffer.compare(two, one) > 0, 'but greater when buffer-aware') db.put(one, 'one', function (err) { t.notOk(err, 'no error') db.get(one, { asBuffer: false }, function (err, value) { t.notOk(err, 'no error') t.equal(value, 'one', 'value one ok') db.put(two, 'two', function (err) { t.notOk(err, 'no error') db.get(one, { asBuffer: false }, function (err, value) { t.notOk(err, 'no error') t.equal(value, 'one', 'value one is the same') db.close(function (err) { t.ifError(err, 'no close error') t.end() }) }) }) }) }) }) }) // This should be covered by abstract-leveldown tests, but that's // prevented by process.browser checks (Level/abstract-leveldown#121). test('iterator yields buffer keys', function (t) { const db = testCommon.factory() if (!db.supports.bufferKeys) { t.fail('environment does not support buffer keys') return t.end() } db.open(function (err) { t.ifError(err, 'no open error') db.batch([ { type: 'put', key: Buffer.from([0]), value: '0' }, { type: 'put', key: Buffer.from([1]), value: '1' } ], function (err) { t.ifError(err, 'no batch error') const it = db.iterator({ valueAsBuffer: false }) concat(it, function (err, entries) { t.ifError(err, 'no iterator error') t.same(entries, [ { key: Buffer.from([0]), value: '0' }, { key: Buffer.from([1]), value: '1' } ], 'keys are Buffers') db.close(function (err) { t.ifError(err, 'no close error') t.end() }) }) }) }) }) test('buffer range option', function (t) { const db = testCommon.factory() if (!db.supports.bufferKeys) { t.fail('environment does not support buffer keys') return t.end() } db.open(function (err) { t.ifError(err, 'no open error') const one = Buffer.from('80', 'hex') const two = Buffer.from('c0', 'hex') db.batch([ { type: 'put', key: one, value: one }, { type: 'put', key: two, value: two } ], function (err) { t.ifError(err, 'no batch error') concat(db.iterator({ gt: one }), function (err, entries) { t.ifError(err, 'no iterator error') t.same(entries, [{ key: two, value: two }]) db.close(function (err) { t.ifError(err, 'no close error') t.end() }) }) }) }) }) // Adapted from a memdown test. test('iterator stringifies buffer input', function (t) { t.plan(6) const db = testCommon.factory() db.open(function (err) { t.ifError(err, 'no open error') db.put(1, 2, function (err) { t.ifError(err, 'no put error') concat(db.iterator(), function (err, entries) { t.ifError(err, 'no iterator error') t.same(entries[0].key, Buffer.from('1'), 'key is stringified') t.same(entries[0].value, Buffer.from('2'), 'value is stringified') db.close(function (err) { t.ifError(err, 'no close error') }) }) }) }) }) // NOTE: in chrome (at least) indexeddb gets buggy if you try and destroy a db, // then create it again, then try and destroy it again. these avoid doing that test('test .destroy', function (t) { const db = testCommon.factory() const location = db.location db.open(function (err) { t.notOk(err, 'no error') db.put('key', 'value', function (err) { t.notOk(err, 'no error') db.get('key', { asBuffer: false }, function (err, value) { t.notOk(err, 'no error') t.equal(value, 'value', 'should have value') db.close(function (err) { t.notOk(err, 'no error') leveljs.destroy(location, function (err) { t.notOk(err, 'no error') const db2 = leveljs(location) db2.open(function (err) { t.notOk(err, 'no error') db2.get('key', { asBuffer: false }, function (err, value) { t.is(err.message, 'NotFound', 'key is not there') db2.close(t.end.bind(t)) }) }) }) }) }) }) }) }) test('test .destroy and custom prefix', function (t) { const prefix = 'custom-' const db = testCommon.factory({ prefix: prefix }) const location = db.location db.open(function (err) { t.notOk(err, 'no error') db.put('key', 'value', function (err) { t.notOk(err, 'no error') db.get('key', { asBuffer: false }, function (err, value) { t.notOk(err, 'no error') t.equal(value, 'value', 'should have value') db.close(function (err) { t.notOk(err, 'no error') leveljs.destroy(location, prefix, function (err) { t.notOk(err, 'no error') const db2 = leveljs(location, { prefix: prefix }) db2.open(function (err) { t.notOk(err, 'no error') db2.get('key', { asBuffer: false }, function (err, value) { t.is(err.message, 'NotFound', 'key is not there') db2.close(t.end.bind(t)) }) }) }) }) }) }) }) }) // TODO: move to abstract-leveldown test suite (and add to iterator tests too) test('clear() with lower key greater than upper key', function (t) { const db = testCommon.factory() db.open(function (err) { t.ifError(err, 'no open error') db.put('a', 'a', function (err) { t.ifError(err, 'no put error') db.clear({ gt: 'b', lt: 'a' }, function (err) { t.ifError(err, 'no clear error') db.get('a', { asBuffer: false }, function (err, value) { t.ifError(err, 'no get error') t.is(value, 'a') db.close(t.end.bind(t)) }) }) }) }) }) test('teardown', testCommon.tearDown) } ================================================ FILE: test/index.js ================================================ 'use strict' const test = require('tape') const uuid = require('uuid/v4') const suite = require('abstract-leveldown/test') const leveljs = require('..') // Test feature detection require('./support-test')(leveljs, test) const testCommon = suite.common({ test: test, factory: function (opts) { return leveljs(uuid(), opts) }, // Unsupported features createIfMissing: false, errorIfExists: false, seek: false, // Support of buffer keys depends on environment bufferKeys: leveljs(uuid()).supports.bufferKeys, // Opt-in to new tests clear: true, getMany: true }) // Test abstract-leveldown compliance suite(testCommon) // Additional tests for this implementation require('./custom-test')(leveljs, test, testCommon) require('./upgrade-test')(leveljs, test, testCommon) ================================================ FILE: test/support-test.js ================================================ 'use strict' const support = require('../util/support') const pos = function () { } const neg = function () { throw new Error() } module.exports = function (leveljs, test) { test('mock bufferKeys support', function (t) { t.ok(support.bufferKeys({ cmp: pos })) t.notOk(support.bufferKeys({ cmp: neg })) t.end() }) } ================================================ FILE: test/upgrade-test.js ================================================ 'use strict' const concat = require('level-concat-iterator') module.exports = function (leveljs, test, testCommon) { test('upgrade', function (t) { const db = testCommon.factory() const input = [ { key: -1, value: 'a' }, { key: '0', value: ab('b') }, { key: '1', value: 1 }, { key: ab('2'), value: new Uint8Array(ab('2')) } ] const output = [ { key: ab('-1'), value: new Uint8Array(ab('a')) }, { key: ab('0'), value: new Uint8Array(ab('b')) }, { key: ab('1'), value: new Uint8Array(ab('1')) }, { key: ab('2'), value: new Uint8Array(ab('2')) } ] db.open(function (err) { t.ifError(err, 'no open error') // To bypass serialization, use _batch() instead of batch(). db._batch(input.map(putOperation), {}, function (err) { t.ifError(err, 'no batch error') db.upgrade(function (err) { t.ifError(err, 'no upgrade error') concatRaw(function (err, entries) { t.ifError(err, 'no concat error') entries.forEach(function (entry) { t.ok(entry.key instanceof ArrayBuffer) t.ok(entry.value instanceof Uint8Array) }) t.same(entries.map(bufferEntry), output.map(bufferEntry)) t.end() }) }) }) }) function concatRaw (callback) { const it = db.iterator() it._deserializeKey = it._deserializeValue = identity concat(it, callback) } function identity (data) { return data } function ab (data) { return Buffer.from(data).buffer } function bufferEntry (entry) { return { key: Buffer.from(entry.key), value: Buffer.from(entry.value) } } function putOperation (entry) { return { type: 'put', key: entry.key, value: entry.value } } }) } ================================================ FILE: util/clear.js ================================================ 'use strict' module.exports = function clear (db, location, keyRange, options, callback) { if (options.limit === 0) return db._nextTick(callback) const transaction = db.db.transaction([location], 'readwrite') const store = transaction.objectStore(location) let count = 0 transaction.oncomplete = function () { callback() } transaction.onabort = function () { callback(transaction.error || new Error('aborted by user')) } // A key cursor is faster (skips reading values) but not supported by IE const method = store.openKeyCursor ? 'openKeyCursor' : 'openCursor' const direction = options.reverse ? 'prev' : 'next' store[method](keyRange, direction).onsuccess = function (ev) { const cursor = ev.target.result if (cursor) { // Wait for a request to complete before continuing, saving CPU. store.delete(cursor.key).onsuccess = function () { if (options.limit <= 0 || ++count < options.limit) { cursor.continue() } } } } } ================================================ FILE: util/deserialize.js ================================================ 'use strict' const Buffer = require('buffer').Buffer const ta2str = (function () { if (global.TextDecoder) { const decoder = new TextDecoder('utf-8') return decoder.decode.bind(decoder) } else { return function ta2str (ta) { return ta2buf(ta).toString() } } })() const ab2str = (function () { if (global.TextDecoder) { const decoder = new TextDecoder('utf-8') return decoder.decode.bind(decoder) } else { return function ab2str (ab) { return Buffer.from(ab).toString() } } })() function ta2buf (ta) { const buf = Buffer.from(ta.buffer) if (ta.byteLength === ta.buffer.byteLength) { return buf } else { return buf.slice(ta.byteOffset, ta.byteOffset + ta.byteLength) } } module.exports = function (data, asBuffer) { if (data instanceof Uint8Array) { return asBuffer ? ta2buf(data) : ta2str(data) } else if (data instanceof ArrayBuffer) { return asBuffer ? Buffer.from(data) : ab2str(data) } else { return asBuffer ? Buffer.from(String(data)) : String(data) } } ================================================ FILE: util/key-range.js ================================================ /* global IDBKeyRange */ 'use strict' const ltgt = require('ltgt') const NONE = Symbol('none') module.exports = function createKeyRange (options) { const lower = ltgt.lowerBound(options, NONE) const upper = ltgt.upperBound(options, NONE) const lowerOpen = ltgt.lowerBoundExclusive(options, NONE) const upperOpen = ltgt.upperBoundExclusive(options, NONE) if (lower !== NONE && upper !== NONE) { return IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen) } else if (lower !== NONE) { return IDBKeyRange.lowerBound(lower, lowerOpen) } else if (upper !== NONE) { return IDBKeyRange.upperBound(upper, upperOpen) } else { return null } } ================================================ FILE: util/serialize.js ================================================ 'use strict' const Buffer = require('buffer').Buffer // Returns either a Uint8Array or Buffer (doesn't matter to // IndexedDB, because Buffer is a subclass of Uint8Array) const str2bin = (function () { if (global.TextEncoder) { const encoder = new TextEncoder('utf-8') return encoder.encode.bind(encoder) } else { return Buffer.from } })() module.exports = function (data, asBuffer) { if (asBuffer) { return Buffer.isBuffer(data) ? data : str2bin(String(data)) } else { return String(data) } } ================================================ FILE: util/support.js ================================================ 'use strict' const Buffer = require('buffer').Buffer exports.test = function (key) { return function test (impl) { try { impl.cmp(key, 0) return true } catch (err) { return false } } } // Detect binary key support (IndexedDB Second Edition) exports.bufferKeys = exports.test(Buffer.alloc(0))