xitdb 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -111
- package/dist/core-buffered-file.d.ts +21 -23
- package/dist/core-file.d.ts +7 -9
- package/dist/core-memory.d.ts +14 -14
- package/dist/core.d.ts +14 -14
- package/dist/database.d.ts +35 -36
- package/dist/hasher.d.ts +2 -1
- package/dist/index.js +728 -791
- package/dist/read-array-list.d.ts +5 -5
- package/dist/read-counted-hash-map.d.ts +1 -1
- package/dist/read-counted-hash-set.d.ts +1 -1
- package/dist/read-cursor.d.ts +18 -18
- package/dist/read-hash-map.d.ts +18 -18
- package/dist/read-hash-set.d.ts +9 -9
- package/dist/read-linked-array-list.d.ts +5 -5
- package/dist/write-array-list.d.ts +9 -10
- package/dist/write-counted-hash-map.d.ts +2 -3
- package/dist/write-counted-hash-set.d.ts +2 -3
- package/dist/write-cursor.d.ts +10 -10
- package/dist/write-hash-map.d.ts +18 -19
- package/dist/write-hash-set.d.ts +12 -13
- package/dist/write-linked-array-list.d.ts +12 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* Reads never block writes, and a database can be read from multiple threads/processes without locks.
|
|
18
18
|
* No query engine of any kind. You just write data structures (primarily an `ArrayList` and `HashMap`) that can be nested arbitrarily.
|
|
19
19
|
* No dependencies besides the JavaScript standard library.
|
|
20
|
+
* Fully synchronous API — no async/await needed.
|
|
20
21
|
* Available [on npm](https://www.npmjs.com/package/xitdb).
|
|
21
22
|
|
|
22
23
|
This database was originally made for the [xit version control system](https://github.com/xit-vcs/xit), but I bet it has a lot of potential for other projects. The combination of being immutable and having an API similar to in-memory data structures is pretty powerful. Consider using it [instead of SQLite](https://gist.github.com/xeubie/03a0724484e1111ef4c05d72a935c42c) for your TypeScript projects: it's simpler, it's pure TypeScript, and it creates no impedance mismatch with your program the way SQL databases do.
|
|
@@ -36,13 +37,13 @@ In this example, we create a new database, write some data in a transaction, and
|
|
|
36
37
|
|
|
37
38
|
```typescript
|
|
38
39
|
// init the db
|
|
39
|
-
using core =
|
|
40
|
+
using core = new CoreBufferedFile('main.db');
|
|
40
41
|
const hasher = new Hasher('SHA-1');
|
|
41
|
-
const db =
|
|
42
|
+
const db = new Database(core, hasher);
|
|
42
43
|
|
|
43
44
|
// to get the benefits of immutability, the top-level data structure
|
|
44
45
|
// must be an ArrayList, so each transaction is stored as an item in it
|
|
45
|
-
const history =
|
|
46
|
+
const history = new WriteArrayList(db.rootCursor());
|
|
46
47
|
|
|
47
48
|
// this is how a transaction is executed. we call history.appendContext,
|
|
48
49
|
// providing it with the most recent copy of the db and a context
|
|
@@ -61,52 +62,52 @@ const history = await WriteArrayList.create(db.rootCursor());
|
|
|
61
62
|
// {"name": "Alice", "age": 25},
|
|
62
63
|
// {"name": "Bob", "age": 42}
|
|
63
64
|
// ]}
|
|
64
|
-
|
|
65
|
-
const moment =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const fruitsCursor =
|
|
71
|
-
const fruits =
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const peopleCursor =
|
|
77
|
-
const people =
|
|
78
|
-
|
|
79
|
-
const aliceCursor =
|
|
80
|
-
const alice =
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const bobCursor =
|
|
85
|
-
const bob =
|
|
86
|
-
|
|
87
|
-
|
|
65
|
+
history.appendContext(history.getSlot(-1), (cursor) => {
|
|
66
|
+
const moment = new WriteHashMap(cursor);
|
|
67
|
+
|
|
68
|
+
moment.put('foo', new Bytes('foo'));
|
|
69
|
+
moment.put('bar', new Bytes('bar'));
|
|
70
|
+
|
|
71
|
+
const fruitsCursor = moment.putCursor('fruits');
|
|
72
|
+
const fruits = new WriteArrayList(fruitsCursor);
|
|
73
|
+
fruits.append(new Bytes('apple'));
|
|
74
|
+
fruits.append(new Bytes('pear'));
|
|
75
|
+
fruits.append(new Bytes('grape'));
|
|
76
|
+
|
|
77
|
+
const peopleCursor = moment.putCursor('people');
|
|
78
|
+
const people = new WriteArrayList(peopleCursor);
|
|
79
|
+
|
|
80
|
+
const aliceCursor = people.appendCursor();
|
|
81
|
+
const alice = new WriteHashMap(aliceCursor);
|
|
82
|
+
alice.put('name', new Bytes('Alice'));
|
|
83
|
+
alice.put('age', new Uint(25));
|
|
84
|
+
|
|
85
|
+
const bobCursor = people.appendCursor();
|
|
86
|
+
const bob = new WriteHashMap(bobCursor);
|
|
87
|
+
bob.put('name', new Bytes('Bob'));
|
|
88
|
+
bob.put('age', new Uint(42));
|
|
88
89
|
});
|
|
89
90
|
|
|
90
91
|
// get the most recent copy of the database, like a moment
|
|
91
92
|
// in time. the -1 index will return the last index in the list.
|
|
92
|
-
const momentCursor =
|
|
93
|
+
const momentCursor = history.getCursor(-1);
|
|
93
94
|
const moment = new ReadHashMap(momentCursor!);
|
|
94
95
|
|
|
95
96
|
// we can read the value of "foo" from the map by getting
|
|
96
97
|
// the cursor to "foo" and then calling readBytes on it
|
|
97
|
-
const fooCursor =
|
|
98
|
-
const fooValue =
|
|
98
|
+
const fooCursor = moment.getCursor('foo');
|
|
99
|
+
const fooValue = fooCursor!.readBytes(MAX_READ_BYTES);
|
|
99
100
|
expect(new TextDecoder().decode(fooValue)).toBe('foo');
|
|
100
101
|
|
|
101
102
|
// to get the "fruits" list, we get the cursor to it and
|
|
102
103
|
// then pass it to the ReadArrayList constructor
|
|
103
|
-
const fruitsCursor =
|
|
104
|
+
const fruitsCursor = moment.getCursor('fruits');
|
|
104
105
|
const fruits = new ReadArrayList(fruitsCursor!);
|
|
105
|
-
expect(
|
|
106
|
+
expect(fruits.count()).toBe(3);
|
|
106
107
|
|
|
107
108
|
// now we can get the first item from the fruits list and read it
|
|
108
|
-
const appleCursor =
|
|
109
|
-
const appleValue =
|
|
109
|
+
const appleCursor = fruits.getCursor(0);
|
|
110
|
+
const appleValue = appleCursor!.readBytes(MAX_READ_BYTES);
|
|
110
111
|
expect(new TextDecoder().decode(appleValue)).toBe('apple');
|
|
111
112
|
```
|
|
112
113
|
|
|
@@ -148,14 +149,14 @@ In xitdb, you can optionally store a format tag with a byte array. A format tag
|
|
|
148
149
|
```typescript
|
|
149
150
|
const randomBytes = new Uint8Array(32);
|
|
150
151
|
crypto.getRandomValues(randomBytes);
|
|
151
|
-
|
|
152
|
+
moment.put('random-number', new Bytes(randomBytes, new TextEncoder().encode('bi')));
|
|
152
153
|
```
|
|
153
154
|
|
|
154
155
|
Then, you can read it like this:
|
|
155
156
|
|
|
156
157
|
```typescript
|
|
157
|
-
const randomNumberCursor =
|
|
158
|
-
const randomNumber =
|
|
158
|
+
const randomNumberCursor = moment.getCursor('random-number');
|
|
159
|
+
const randomNumber = randomNumberCursor!.readBytesObject(MAX_READ_BYTES);
|
|
159
160
|
expect(new TextDecoder().decode(randomNumber.formatTag!)).toBe('bi');
|
|
160
161
|
const randomBigInt = randomNumber.value;
|
|
161
162
|
```
|
|
@@ -167,76 +168,76 @@ There are many types you may want to store this way. Maybe an ISO-8601 date like
|
|
|
167
168
|
A powerful feature of immutable data is fast cloning. Any data structure can be instantly cloned and changed without affecting the original. Starting with the example code above, we can make a new transaction that creates a "food" list based on the existing "fruits" list:
|
|
168
169
|
|
|
169
170
|
```typescript
|
|
170
|
-
|
|
171
|
-
const moment =
|
|
171
|
+
history.appendContext(history.getSlot(-1), (cursor) => {
|
|
172
|
+
const moment = new WriteHashMap(cursor);
|
|
172
173
|
|
|
173
|
-
const fruitsCursor =
|
|
174
|
+
const fruitsCursor = moment.getCursor('fruits');
|
|
174
175
|
const fruits = new ReadArrayList(fruitsCursor!);
|
|
175
176
|
|
|
176
177
|
// create a new key called "food" whose initial value is
|
|
177
178
|
// based on the "fruits" list
|
|
178
|
-
const foodCursor =
|
|
179
|
-
|
|
179
|
+
const foodCursor = moment.putCursor('food');
|
|
180
|
+
foodCursor.write(fruits.slot());
|
|
180
181
|
|
|
181
|
-
const food =
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
const food = new WriteArrayList(foodCursor);
|
|
183
|
+
food.append(new Bytes('eggs'));
|
|
184
|
+
food.append(new Bytes('rice'));
|
|
185
|
+
food.append(new Bytes('fish'));
|
|
185
186
|
});
|
|
186
187
|
|
|
187
|
-
const momentCursor =
|
|
188
|
+
const momentCursor = history.getCursor(-1);
|
|
188
189
|
const moment = new ReadHashMap(momentCursor!);
|
|
189
190
|
|
|
190
191
|
// the food list includes the fruits
|
|
191
|
-
const foodCursor =
|
|
192
|
+
const foodCursor = moment.getCursor('food');
|
|
192
193
|
const food = new ReadArrayList(foodCursor!);
|
|
193
|
-
expect(
|
|
194
|
+
expect(food.count()).toBe(6);
|
|
194
195
|
|
|
195
196
|
// ...but the fruits list hasn't been changed
|
|
196
|
-
const fruitsCursor =
|
|
197
|
+
const fruitsCursor = moment.getCursor('fruits');
|
|
197
198
|
const fruits = new ReadArrayList(fruitsCursor!);
|
|
198
|
-
expect(
|
|
199
|
+
expect(fruits.count()).toBe(3);
|
|
199
200
|
```
|
|
200
201
|
|
|
201
202
|
Before we continue, let's save the latest history index, so we can revert back to this moment of the database later:
|
|
202
203
|
|
|
203
204
|
```typescript
|
|
204
|
-
const historyIndex =
|
|
205
|
+
const historyIndex = history.count() - 1;
|
|
205
206
|
```
|
|
206
207
|
|
|
207
208
|
There's one catch you'll run into when cloning. If we try cloning a data structure that was created in the same transaction, it doesn't seem to work:
|
|
208
209
|
|
|
209
210
|
```typescript
|
|
210
|
-
|
|
211
|
-
const moment =
|
|
211
|
+
history.appendContext(history.getSlot(-1), (cursor) => {
|
|
212
|
+
const moment = new WriteHashMap(cursor);
|
|
212
213
|
|
|
213
|
-
const bigCitiesCursor =
|
|
214
|
-
const bigCities =
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
const bigCitiesCursor = moment.putCursor('big-cities');
|
|
215
|
+
const bigCities = new WriteArrayList(bigCitiesCursor);
|
|
216
|
+
bigCities.append(new Bytes('New York, NY'));
|
|
217
|
+
bigCities.append(new Bytes('Los Angeles, CA'));
|
|
217
218
|
|
|
218
219
|
// create a new key called "cities" whose initial value is
|
|
219
220
|
// based on the "big-cities" list
|
|
220
|
-
const citiesCursor =
|
|
221
|
-
|
|
221
|
+
const citiesCursor = moment.putCursor('cities');
|
|
222
|
+
citiesCursor.write(bigCities.slot());
|
|
222
223
|
|
|
223
|
-
const cities =
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
const cities = new WriteArrayList(citiesCursor);
|
|
225
|
+
cities.append(new Bytes('Charleston, SC'));
|
|
226
|
+
cities.append(new Bytes('Louisville, KY'));
|
|
226
227
|
});
|
|
227
228
|
|
|
228
|
-
const momentCursor =
|
|
229
|
+
const momentCursor = history.getCursor(-1);
|
|
229
230
|
const moment = new ReadHashMap(momentCursor!);
|
|
230
231
|
|
|
231
232
|
// the cities list contains all four
|
|
232
|
-
const citiesCursor =
|
|
233
|
+
const citiesCursor = moment.getCursor('cities');
|
|
233
234
|
const cities = new ReadArrayList(citiesCursor!);
|
|
234
|
-
expect(
|
|
235
|
+
expect(cities.count()).toBe(4);
|
|
235
236
|
|
|
236
237
|
// ..but so does big-cities! we did not intend to mutate this
|
|
237
|
-
const bigCitiesCursor =
|
|
238
|
+
const bigCitiesCursor = moment.getCursor('big-cities');
|
|
238
239
|
const bigCities = new ReadArrayList(bigCitiesCursor!);
|
|
239
|
-
expect(
|
|
240
|
+
expect(bigCities.count()).toBe(4);
|
|
240
241
|
```
|
|
241
242
|
|
|
242
243
|
The reason that `big-cities` was mutated is because all data in a given transaction is temporarily mutable. This is a very important optimization, but in this case, it's not what we want.
|
|
@@ -244,45 +245,45 @@ The reason that `big-cities` was mutated is because all data in a given transact
|
|
|
244
245
|
To show how to fix this, let's first undo the transaction we just made. Here we use the `historyIndex` we saved before to revert back to the older database moment:
|
|
245
246
|
|
|
246
247
|
```typescript
|
|
247
|
-
|
|
248
|
+
history.append(history.getSlot(historyIndex)!);
|
|
248
249
|
```
|
|
249
250
|
|
|
250
251
|
This time, after making the "big cities" list, we call `freeze`, which tells xitdb to consider all data made so far in the transaction to be immutable. After that, we can clone it into the "cities" list and it will work the way we wanted:
|
|
251
252
|
|
|
252
253
|
```typescript
|
|
253
|
-
|
|
254
|
-
const moment =
|
|
254
|
+
history.appendContext(history.getSlot(-1), (cursor) => {
|
|
255
|
+
const moment = new WriteHashMap(cursor);
|
|
255
256
|
|
|
256
|
-
const bigCitiesCursor =
|
|
257
|
-
const bigCities =
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
const bigCitiesCursor = moment.putCursor('big-cities');
|
|
258
|
+
const bigCities = new WriteArrayList(bigCitiesCursor);
|
|
259
|
+
bigCities.append(new Bytes('New York, NY'));
|
|
260
|
+
bigCities.append(new Bytes('Los Angeles, CA'));
|
|
260
261
|
|
|
261
262
|
// freeze here, so big-cities won't be mutated
|
|
262
263
|
cursor.db.freeze();
|
|
263
264
|
|
|
264
265
|
// create a new key called "cities" whose initial value is
|
|
265
266
|
// based on the "big-cities" list
|
|
266
|
-
const citiesCursor =
|
|
267
|
-
|
|
267
|
+
const citiesCursor = moment.putCursor('cities');
|
|
268
|
+
citiesCursor.write(bigCities.slot());
|
|
268
269
|
|
|
269
|
-
const cities =
|
|
270
|
-
|
|
271
|
-
|
|
270
|
+
const cities = new WriteArrayList(citiesCursor);
|
|
271
|
+
cities.append(new Bytes('Charleston, SC'));
|
|
272
|
+
cities.append(new Bytes('Louisville, KY'));
|
|
272
273
|
});
|
|
273
274
|
|
|
274
|
-
const momentCursor =
|
|
275
|
+
const momentCursor = history.getCursor(-1);
|
|
275
276
|
const moment = new ReadHashMap(momentCursor!);
|
|
276
277
|
|
|
277
278
|
// the cities list contains all four
|
|
278
|
-
const citiesCursor =
|
|
279
|
+
const citiesCursor = moment.getCursor('cities');
|
|
279
280
|
const cities = new ReadArrayList(citiesCursor!);
|
|
280
|
-
expect(
|
|
281
|
+
expect(cities.count()).toBe(4);
|
|
281
282
|
|
|
282
283
|
// and big-cities only contains the original two
|
|
283
|
-
const bigCitiesCursor =
|
|
284
|
+
const bigCitiesCursor = moment.getCursor('big-cities');
|
|
284
285
|
const bigCities = new ReadArrayList(bigCitiesCursor!);
|
|
285
|
-
expect(
|
|
286
|
+
expect(bigCities.count()).toBe(2);
|
|
286
287
|
```
|
|
287
288
|
|
|
288
289
|
## Large Byte Arrays
|
|
@@ -290,12 +291,12 @@ expect(await bigCities.count()).toBe(2);
|
|
|
290
291
|
When reading and writing large byte arrays, you probably don't want to have all of their contents in memory at once. To incrementally write to a byte array, just get a writer from a cursor:
|
|
291
292
|
|
|
292
293
|
```typescript
|
|
293
|
-
const longTextCursor =
|
|
294
|
-
const cursorWriter =
|
|
294
|
+
const longTextCursor = moment.putCursor('long-text');
|
|
295
|
+
const cursorWriter = longTextCursor.writer();
|
|
295
296
|
for (let i = 0; i < 50; i++) {
|
|
296
|
-
|
|
297
|
+
cursorWriter.write(new TextEncoder().encode('hello, world\n'));
|
|
297
298
|
}
|
|
298
|
-
|
|
299
|
+
cursorWriter.finish(); // remember to call this!
|
|
299
300
|
```
|
|
300
301
|
|
|
301
302
|
If you need to set a format tag for the byte array, put it in the `formatTag` field of the writer before you call `finish`.
|
|
@@ -303,11 +304,11 @@ If you need to set a format tag for the byte array, put it in the `formatTag` fi
|
|
|
303
304
|
To read a byte array incrementally, get a reader from a cursor:
|
|
304
305
|
|
|
305
306
|
```typescript
|
|
306
|
-
const longTextCursor =
|
|
307
|
-
const cursorReader =
|
|
307
|
+
const longTextCursor = moment.getCursor('long-text');
|
|
308
|
+
const cursorReader = longTextCursor!.reader();
|
|
308
309
|
let lineCount = 0, line: number[] = [];
|
|
309
310
|
const buf = new Uint8Array(1024);
|
|
310
|
-
for (let n; (n =
|
|
311
|
+
for (let n; (n = cursorReader.read(buf)) > 0; ) {
|
|
311
312
|
for (let i = 0; i < n; i++) {
|
|
312
313
|
if (buf[i] === 0x0A) { lineCount++; line = []; }
|
|
313
314
|
else line.push(buf[i]);
|
|
@@ -322,24 +323,24 @@ expect(lineCount).toBe(50);
|
|
|
322
323
|
All data structures support iteration. Here's an example of iterating over an `ArrayList` and printing all of the keys and values of each `HashMap` contained in it:
|
|
323
324
|
|
|
324
325
|
```typescript
|
|
325
|
-
const peopleCursor =
|
|
326
|
+
const peopleCursor = moment.getCursor('people');
|
|
326
327
|
const people = new ReadArrayList(peopleCursor!);
|
|
327
328
|
|
|
328
|
-
const peopleIter =
|
|
329
|
-
while (
|
|
330
|
-
const personCursor =
|
|
329
|
+
const peopleIter = people.iterator();
|
|
330
|
+
while (peopleIter.hasNext()) {
|
|
331
|
+
const personCursor = peopleIter.next();
|
|
331
332
|
const person = new ReadHashMap(personCursor!);
|
|
332
|
-
const personIter =
|
|
333
|
-
while (
|
|
334
|
-
const kvPairCursor =
|
|
335
|
-
const kvPair =
|
|
333
|
+
const personIter = person.iterator();
|
|
334
|
+
while (personIter.hasNext()) {
|
|
335
|
+
const kvPairCursor = personIter.next();
|
|
336
|
+
const kvPair = kvPairCursor!.readKeyValuePair();
|
|
336
337
|
|
|
337
|
-
const key = new TextDecoder().decode(
|
|
338
|
+
const key = new TextDecoder().decode(kvPair.keyCursor.readBytes(MAX_READ_BYTES));
|
|
338
339
|
|
|
339
340
|
switch (kvPair.valueCursor.slot().tag) {
|
|
340
341
|
case Tag.SHORT_BYTES:
|
|
341
342
|
case Tag.BYTES:
|
|
342
|
-
console.log(`${key}: ${new TextDecoder().decode(
|
|
343
|
+
console.log(`${key}: ${new TextDecoder().decode(kvPair.valueCursor.readBytes(MAX_READ_BYTES))}`);
|
|
343
344
|
break;
|
|
344
345
|
case Tag.UINT:
|
|
345
346
|
console.log(`${key}: ${kvPair.valueCursor.readUint()}`);
|
|
@@ -366,16 +367,16 @@ The hashing data structures will create the hash for you when you call methods l
|
|
|
366
367
|
When initializing a database, you tell xitdb how to hash with the `Hasher`. If you're using SHA-1, it will look like this:
|
|
367
368
|
|
|
368
369
|
```typescript
|
|
369
|
-
using core =
|
|
370
|
+
using core = new CoreBufferedFile('main.db');
|
|
370
371
|
const hasher = new Hasher('SHA-1');
|
|
371
|
-
const db =
|
|
372
|
+
const db = new Database(core, hasher);
|
|
372
373
|
```
|
|
373
374
|
|
|
374
375
|
The size of the hash in bytes will be stored in the database's header. If you try opening it later with a hashing algorithm that has the wrong hash size, it will throw an exception. If you are unsure what hash size the database uses, this creates a chicken-and-egg problem. You can read the header before initializing the database like this:
|
|
375
376
|
|
|
376
377
|
```typescript
|
|
377
|
-
|
|
378
|
-
const header =
|
|
378
|
+
core.seek(0);
|
|
379
|
+
const header = Header.read(core);
|
|
379
380
|
expect(header.hashSize).toBe(20);
|
|
380
381
|
```
|
|
381
382
|
|
|
@@ -388,8 +389,8 @@ const hasher = new Hasher('SHA-1', Hasher.stringToId('sha1'));
|
|
|
388
389
|
The hash id is only written to the database header when it is first initialized. When you open it later, the hash id in the `Hasher` is ignored. You can read the hash id of an existing database like this:
|
|
389
390
|
|
|
390
391
|
```typescript
|
|
391
|
-
|
|
392
|
-
const header =
|
|
392
|
+
core.seek(0);
|
|
393
|
+
const header = Header.read(core);
|
|
393
394
|
expect(Hasher.idToString(header.hashId)).toBe("sha1");
|
|
394
395
|
```
|
|
395
396
|
|
|
@@ -425,12 +426,12 @@ switch (hashIdStr) {
|
|
|
425
426
|
Normally, an immutable database grows forever, because old data is never deleted. To reclaim disk space and clear the history, xitdb supports compaction. This involves completely rebuilding the database file to only contain the data accessible from the latest copy (i.e., "moment") of the database.
|
|
426
427
|
|
|
427
428
|
```typescript
|
|
428
|
-
using compactCore =
|
|
429
|
-
const compactDb =
|
|
429
|
+
using compactCore = new CoreBufferedFile('compact.db');
|
|
430
|
+
const compactDb = db.compact(compactCore);
|
|
430
431
|
|
|
431
432
|
// read from the new compacted db
|
|
432
433
|
const history = new ReadArrayList(compactDb.rootCursor());
|
|
433
|
-
expect(
|
|
434
|
+
expect(history.count()).toBe(1);
|
|
434
435
|
```
|
|
435
436
|
|
|
436
437
|
This compacted database will be in a separate file. If you want to delete the original database and replace it with this one, you'll need to do that yourself. It is not possible to compact a database in-place (using the same file as the target database); doing so would fail and would render your original database unreadable.
|
|
@@ -2,16 +2,15 @@ import type { Core, DataReader, DataWriter } from './core';
|
|
|
2
2
|
import { CoreFile } from './core-file';
|
|
3
3
|
export declare class CoreBufferedFile implements Core {
|
|
4
4
|
file: RandomAccessBufferedFile;
|
|
5
|
-
constructor(
|
|
6
|
-
static create(filePath: string, bufferSize?: number): Promise<CoreBufferedFile>;
|
|
5
|
+
constructor(filePath: string, bufferSize?: number);
|
|
7
6
|
reader(): DataReader;
|
|
8
7
|
writer(): DataWriter;
|
|
9
|
-
length():
|
|
10
|
-
seek(pos: number):
|
|
8
|
+
length(): number;
|
|
9
|
+
seek(pos: number): void;
|
|
11
10
|
position(): number;
|
|
12
|
-
setLength(len: number):
|
|
13
|
-
flush():
|
|
14
|
-
sync():
|
|
11
|
+
setLength(len: number): void;
|
|
12
|
+
flush(): void;
|
|
13
|
+
sync(): void;
|
|
15
14
|
[Symbol.dispose](): void;
|
|
16
15
|
}
|
|
17
16
|
declare class RandomAccessBufferedFile implements DataReader, DataWriter {
|
|
@@ -20,22 +19,21 @@ declare class RandomAccessBufferedFile implements DataReader, DataWriter {
|
|
|
20
19
|
private bufferSize;
|
|
21
20
|
private filePos;
|
|
22
21
|
private memoryPos;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
length(): Promise<number>;
|
|
22
|
+
constructor(filePath: string, bufferSize?: number);
|
|
23
|
+
seek(pos: number): void;
|
|
24
|
+
length(): number;
|
|
27
25
|
position(): number;
|
|
28
|
-
setLength(len: number):
|
|
29
|
-
flush():
|
|
30
|
-
sync():
|
|
31
|
-
write(buffer: Uint8Array):
|
|
32
|
-
writeByte(v: number):
|
|
33
|
-
writeShort(v: number):
|
|
34
|
-
writeLong(v: number):
|
|
35
|
-
readFully(buffer: Uint8Array):
|
|
36
|
-
readByte():
|
|
37
|
-
readShort():
|
|
38
|
-
readInt():
|
|
39
|
-
readLong():
|
|
26
|
+
setLength(len: number): void;
|
|
27
|
+
flush(): void;
|
|
28
|
+
sync(): void;
|
|
29
|
+
write(buffer: Uint8Array): void;
|
|
30
|
+
writeByte(v: number): void;
|
|
31
|
+
writeShort(v: number): void;
|
|
32
|
+
writeLong(v: number): void;
|
|
33
|
+
readFully(buffer: Uint8Array): void;
|
|
34
|
+
readByte(): number;
|
|
35
|
+
readShort(): number;
|
|
36
|
+
readInt(): number;
|
|
37
|
+
readLong(): number;
|
|
40
38
|
}
|
|
41
39
|
export {};
|
package/dist/core-file.d.ts
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import type { Core, DataReader, DataWriter } from './core';
|
|
2
|
-
import type { FileHandle } from 'fs/promises';
|
|
3
2
|
export declare class CoreFile implements Core {
|
|
4
3
|
filePath: string;
|
|
5
4
|
private _position;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
static create(filePath: string): Promise<CoreFile>;
|
|
5
|
+
fd: number;
|
|
6
|
+
constructor(filePath: string);
|
|
9
7
|
reader(): DataReader;
|
|
10
8
|
writer(): DataWriter;
|
|
11
|
-
length():
|
|
12
|
-
seek(pos: number):
|
|
9
|
+
length(): number;
|
|
10
|
+
seek(pos: number): void;
|
|
13
11
|
position(): number;
|
|
14
|
-
setLength(len: number):
|
|
15
|
-
flush():
|
|
16
|
-
sync():
|
|
12
|
+
setLength(len: number): void;
|
|
13
|
+
flush(): void;
|
|
14
|
+
sync(): void;
|
|
17
15
|
[Symbol.dispose](): void;
|
|
18
16
|
}
|
package/dist/core-memory.d.ts
CHANGED
|
@@ -4,12 +4,12 @@ export declare class CoreMemory implements Core {
|
|
|
4
4
|
constructor();
|
|
5
5
|
reader(): DataReader;
|
|
6
6
|
writer(): DataWriter;
|
|
7
|
-
length():
|
|
8
|
-
seek(pos: number):
|
|
7
|
+
length(): number;
|
|
8
|
+
seek(pos: number): void;
|
|
9
9
|
position(): number;
|
|
10
|
-
setLength(len: number):
|
|
11
|
-
flush():
|
|
12
|
-
sync():
|
|
10
|
+
setLength(len: number): void;
|
|
11
|
+
flush(): void;
|
|
12
|
+
sync(): void;
|
|
13
13
|
}
|
|
14
14
|
declare class RandomAccessMemory implements DataReader, DataWriter {
|
|
15
15
|
private buffer;
|
|
@@ -23,14 +23,14 @@ declare class RandomAccessMemory implements DataReader, DataWriter {
|
|
|
23
23
|
setLength(len: number): void;
|
|
24
24
|
reset(): void;
|
|
25
25
|
toByteArray(): Uint8Array;
|
|
26
|
-
write(data: Uint8Array):
|
|
27
|
-
writeByte(v: number):
|
|
28
|
-
writeShort(v: number):
|
|
29
|
-
writeLong(v: number):
|
|
30
|
-
readFully(b: Uint8Array):
|
|
31
|
-
readByte():
|
|
32
|
-
readShort():
|
|
33
|
-
readInt():
|
|
34
|
-
readLong():
|
|
26
|
+
write(data: Uint8Array): void;
|
|
27
|
+
writeByte(v: number): void;
|
|
28
|
+
writeShort(v: number): void;
|
|
29
|
+
writeLong(v: number): void;
|
|
30
|
+
readFully(b: Uint8Array): void;
|
|
31
|
+
readByte(): number;
|
|
32
|
+
readShort(): number;
|
|
33
|
+
readInt(): number;
|
|
34
|
+
readLong(): number;
|
|
35
35
|
}
|
|
36
36
|
export {};
|
package/dist/core.d.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
export interface DataReader {
|
|
2
|
-
readFully(buffer: Uint8Array):
|
|
3
|
-
readByte():
|
|
4
|
-
readShort():
|
|
5
|
-
readInt():
|
|
6
|
-
readLong():
|
|
2
|
+
readFully(buffer: Uint8Array): void;
|
|
3
|
+
readByte(): number;
|
|
4
|
+
readShort(): number;
|
|
5
|
+
readInt(): number;
|
|
6
|
+
readLong(): number;
|
|
7
7
|
}
|
|
8
8
|
export interface DataWriter {
|
|
9
|
-
write(buffer: Uint8Array):
|
|
10
|
-
writeByte(v: number):
|
|
11
|
-
writeShort(v: number):
|
|
12
|
-
writeLong(v: number):
|
|
9
|
+
write(buffer: Uint8Array): void;
|
|
10
|
+
writeByte(v: number): void;
|
|
11
|
+
writeShort(v: number): void;
|
|
12
|
+
writeLong(v: number): void;
|
|
13
13
|
}
|
|
14
14
|
export interface Core {
|
|
15
15
|
reader(): DataReader;
|
|
16
16
|
writer(): DataWriter;
|
|
17
|
-
length():
|
|
18
|
-
seek(pos: number):
|
|
17
|
+
length(): number;
|
|
18
|
+
seek(pos: number): void;
|
|
19
19
|
position(): number;
|
|
20
|
-
setLength(len: number):
|
|
21
|
-
flush():
|
|
22
|
-
sync():
|
|
20
|
+
setLength(len: number): void;
|
|
21
|
+
flush(): void;
|
|
22
|
+
sync(): void;
|
|
23
23
|
}
|