|
1 | 1 | # level-js
|
2 | 2 |
|
3 |
| -> An [`abstract-leveldown`][abstract-leveldown] compliant store on top of [IndexedDB][indexeddb], which is in turn implemented on top of [LevelDB][leveldb] which brings this whole shebang full circle. |
| 3 | +> An [`abstract-leveldown`][abstract-leveldown] compliant store on top of [IndexedDB][indexeddb]. |
4 | 4 |
|
5 | 5 | [![level badge][level-badge]][awesome]
|
6 | 6 | [](https://www.npmjs.com/package/level-js)
|
@@ -35,13 +35,11 @@ Here are the goals of `level-js`:
|
35 | 35 |
|
36 | 36 | - Store large amounts of data in modern browsers
|
37 | 37 | - Pass the full [`abstract-leveldown`][abstract-leveldown] test suite
|
38 |
| -- Support [`Buffer`][buffer] keys and values |
39 |
| -- Support all key types of IndexedDB Second Edition |
40 |
| -- Support all value types of the [structured clone algorithm][structured-clone-algorithm] except for `null` and `undefined` |
| 38 | +- Support string and [`Buffer`][buffer] keys and values |
41 | 39 | - Be as fast as possible
|
42 |
| -- Sync with [multilevel](https://github.com/juliangruber/multilevel) over ASCII or binary transports. |
| 40 | +- ~~Sync with [multilevel](https://github.com/juliangruber/multilevel) over ASCII or binary transports.~~ |
43 | 41 |
|
44 |
| -Being `abstract-leveldown` compliant means you can use many of the [Level modules][awesome] on top of this library. For some demos of it working, see [**@brycebaril**](https://github.com/brycebaril)'s presentation [Path of the NodeBases Jedi](http://brycebaril.github.io/nodebase_jedi/#/vanilla). |
| 42 | +Being `abstract-leveldown` compliant means you can use many of the [Level modules][awesome] on top of this library. |
45 | 43 |
|
46 | 44 | ## Example
|
47 | 45 |
|
@@ -80,120 +78,17 @@ const value = await db.get('hello')
|
80 | 78 |
|
81 | 79 | ## Type Support
|
82 | 80 |
|
83 |
| -Unlike [`leveldown`][leveldown], `level-js` does not stringify keys or values. This means that in addition to strings and Buffers you can store almost any JavaScript type without the need for [`encoding-down`][encoding-down]. |
| 81 | +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. |
84 | 82 |
|
85 |
| -### Values |
| 83 | +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 binary keys are not supported by the environment (like IE11) `level-js` falls back to `String(key)`. |
86 | 84 |
|
87 |
| -All value types of the [structured clone algorithm][structured-clone-algorithm] are supported except for `null` and `undefined`. Depending on the environment, this includes: |
| 85 | +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. |
88 | 86 |
|
89 |
| -- Number, including `NaN`, `Infinity` and `-Infinity` |
90 |
| -- String, Boolean, Date, RegExp, Array, Object |
91 |
| -- ArrayBuffer or a view thereof (typed arrays); |
92 |
| -- Map, Set, Blob, File, FileList, ImageData (limited support). |
93 |
| - |
94 |
| -In addition `level-js` stores [`Buffer`][buffer] values without transformation. This works in all target environments because `Buffer` is a subclass of `Uint8Array`, meaning such values can be passed to `IndexedDB` as-is. |
95 |
| - |
96 |
| -When getting or iterating binary values, regardless of whether they were stored as a `Buffer`, `ArrayBuffer` or a view thereof, values will return as a `Buffer`. This behavior can be disabled, in which case `ArrayBuffer` returns as `ArrayBuffer`, typed arrays return as typed arrays and `Buffer` returns as `Uint8Array`: |
| 87 | +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. |
97 | 88 |
|
98 | 89 | ```js
|
99 | 90 | db.get('key', { asBuffer: false })
|
100 |
| -db.iterator({ valueAsBuffer: false }) |
101 |
| -``` |
102 |
| - |
103 |
| -If the environment does not support a type, it will throw an error which `level-js` catches and passes to the callbacks of `put` or `batch`. For example, IE does not support typed array values. At the time of writing, Chrome is the only browser that supports all types listed above. |
104 |
| - |
105 |
| -### Keys |
106 |
| - |
107 |
| -All key types of IndexedDB Second Edition are supported. Depending on the environment, this includes: |
108 |
| - |
109 |
| -- Number, including `Infinity` and `-Infinity`, but not `NaN` |
110 |
| -- Date, except invalid (`NaN`) |
111 |
| -- String |
112 |
| -- ArrayBuffer or a view thereof (typed arrays); |
113 |
| -- Array, except cyclical, empty and sparse arrays. Elements must be valid types themselves. |
114 |
| - |
115 |
| -In addition you can use [`Buffer`][buffer] keys, giving `level-js` the same power as implementations like `leveldown` and `memdown`. When iterating binary keys, regardless of whether they were stored as `Buffer`, `ArrayBuffer` or a view thereof, keys will return as a `Buffer`. This behavior can be disabled, in which case binary keys will always return as `ArrayBuffer`: |
116 |
| - |
117 |
| -```js |
118 |
| -db.iterator({ keyAsBuffer: false }) |
119 |
| -``` |
120 |
| - |
121 |
| -Note that this behavior is slightly different from values due to the way that IndexedDB works. IndexedDB stores binary _values_ using the structured clone algorithm, which preserves views, but it stores binary _keys_ as an array of octets, so that it is able to compare and sort differently typed keys. |
122 |
| - |
123 |
| -If the environment does not support a type, it will throw an error which `level-js` catches and passes to the callbacks of `get`, `put`, `del`, `batch` or an iterator. Exceptions are: |
124 |
| - |
125 |
| -- `null` and `undefined`: rejected early by `abstract-leveldown` |
126 |
| -- Binary and array keys: if not supported by the environment, `level-js` falls back to `String(key)`. |
127 |
| - |
128 |
| -### Normalization |
129 |
| - |
130 |
| -If you desire normalization for keys and values (e.g. to stringify numbers), 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 or to smooth over type differences between browsers. For example, you could have [`leveldown`][leveldown] in a backend and `level-js` in the frontend. The `level` package does exactly that. |
131 |
| - |
132 |
| -Another reason you might want to use `encoding-down` is that the structured clone algorithm, while rich in types, can be slower than `JSON.stringify`. |
133 |
| - |
134 |
| -### Sort Order |
135 |
| - |
136 |
| -Unless `level-js` is wrapped with [`encoding-down`][encoding-down], IndexedDB will sort your keys in the following order: |
137 |
| - |
138 |
| -1. number (numeric) |
139 |
| -2. date (numeric, by epoch offset) |
140 |
| -3. binary (bitwise) |
141 |
| -4. string (lexicographic) |
142 |
| -5. array (componentwise). |
143 |
| - |
144 |
| -You can take advantage of this fact with `levelup` streams. For example, if your keys are dates, you can select everything greater than a specific date (let's be happy and ignore timezones for a moment): |
145 |
| - |
146 |
| -```js |
147 |
| -const db = levelup(leveljs('time-db')) |
148 |
| - |
149 |
| -db.createReadStream({ gt: new Date('2019-01-01') }) |
150 |
| - .pipe(..) |
151 |
| -``` |
152 |
| - |
153 |
| -Or if your keys are arrays, you can do things like: |
154 |
| - |
155 |
| -```js |
156 |
| -const db = levelup(leveljs('books-db')) |
157 |
| - |
158 |
| -await db.put(['Roald Dahl', 'Charlie and the Chocolate Factory'], {}) |
159 |
| -await db.put(['Roald Dahl', 'Fantastic Mr Fox'], {}) |
160 |
| - |
161 |
| -// Select all books by Roald Dahl |
162 |
| -db.createReadStream({ gt: ['Roald Dahl'], lt: ['Roald Dahl', '\xff'] }) |
163 |
| - .pipe(..) |
164 |
| -``` |
165 |
| - |
166 |
| -To achieve this on other `abstract-leveldown` implementations, wrap them with [`encoding-down`][encoding-down] and [`charwise`][charwise] (or similar). |
167 |
| - |
168 |
| -#### Known Browser Issues |
169 |
| - |
170 |
| -IE11 and Edge yield incorrect results for `{ gte: '' }` if the database contains any key types other than strings. |
171 |
| - |
172 |
| -### Buffer vs ArrayBuffer |
173 |
| - |
174 |
| -For interoperability it is recommended to use `Buffer` as your binary type. While we recognize that Node.js core modules are moving towards supporting `ArrayBuffer` and views thereof, `Buffer` remains the primary binary type in the Level ecosystem. |
175 |
| - |
176 |
| -That said: if you want to `put()` an `ArrayBuffer` you can! Just know that it will come back as a `Buffer` by default. If you want to `get()` or iterate stored `ArrayBuffer` data as an `ArrayBuffer`, you have a few options. Without `encoding-down`: |
177 |
| - |
178 |
| -```js |
179 |
| -const db = levelup(leveljs('mydb')) |
180 |
| - |
181 |
| -// Yields an ArrayBuffer, Buffer and ArrayBuffer |
182 |
| -const value1 = await db.get('key', { asBuffer: false }) |
183 |
| -const value2 = await db.get('key') |
184 |
| -const value3 = value2.buffer |
185 |
| -``` |
186 |
| - |
187 |
| -With `encoding-down` (or `level`) you can use the `id` encoding to selectively bypass encodings: |
188 |
| - |
189 |
| -```js |
190 |
| -const encode = require('encoding-down') |
191 |
| -const db = levelup(encode(leveljs('mydb'), { valueEncoding: 'binary' })) |
192 |
| - |
193 |
| -// Yields an ArrayBuffer, Buffer and ArrayBuffer |
194 |
| -const value1 = await db.get('key', { valueEncoding: 'id' }) |
195 |
| -const value2 = await db.get('key') |
196 |
| -const value3 = value2.buffer |
| 91 | +db.iterator({ keyAsBuffer: false, valueAsBuffer: false }) |
197 | 92 | ```
|
198 | 93 |
|
199 | 94 | ## Install
|
@@ -268,22 +163,16 @@ To sustain [`Level`](https://github.com/Level) and its activities, become a back
|
268 | 163 |
|
269 | 164 | [indexeddb]: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
|
270 | 165 |
|
271 |
| -[leveldb]: https://github.com/google/leveldb |
272 |
| - |
273 | 166 | [buffer]: https://nodejs.org/api/buffer.html
|
274 | 167 |
|
275 | 168 | [awesome]: https://github.com/Level/awesome
|
276 | 169 |
|
277 | 170 | [abstract-leveldown]: https://github.com/Level/abstract-leveldown
|
278 | 171 |
|
279 |
| -[charwise]: https://github.com/dominictarr/charwise |
280 |
| - |
281 | 172 | [levelup]: https://github.com/Level/levelup
|
282 | 173 |
|
283 | 174 | [leveldown]: https://github.com/Level/leveldown
|
284 | 175 |
|
285 | 176 | [level]: https://github.com/Level/level
|
286 | 177 |
|
287 | 178 | [encoding-down]: https://github.com/Level/encoding-down
|
288 |
| - |
289 |
| -[structured-clone-algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm |
0 commit comments