|
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 binary 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 binary. Supported binary types include [`Buffer`][buffer], `ArrayBuffer` or a view thereof (typed arrays). 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 binary 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