@bullet./paraql 0.1.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/CMakeLists.txt +103 -0
- package/LICENSE +201 -0
- package/NOTICE +13 -0
- package/README.md +213 -0
- package/binding.c +2646 -0
- package/binding.js +3 -0
- package/index.js +148 -0
- package/lib/codecs.js +138 -0
- package/lib/constants.js +10 -0
- package/lib/deferred.js +8 -0
- package/lib/encryption.js +39 -0
- package/lib/errors.js +35 -0
- package/lib/statement.d.ts +55 -0
- package/lib/statement.js +168 -0
- package/lib/vfs.js +558 -0
- package/package.json +98 -0
package/lib/vfs.js
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
const Autobee = require("autobee")
|
|
2
|
+
const { Buffer } = require("buffer")
|
|
3
|
+
const fs = require("fs/promises")
|
|
4
|
+
const path = require("path")
|
|
5
|
+
const ReadyResource = require("ready-resource")
|
|
6
|
+
const RocksDB = require("rocksdb-native")
|
|
7
|
+
|
|
8
|
+
const binding = require("../binding")
|
|
9
|
+
const { PageKey, Operation, deflate, inflate } = require("./codecs")
|
|
10
|
+
const { OPERATION, PAGE_SIZE } = require("./constants")
|
|
11
|
+
const Deferred = require("./deferred")
|
|
12
|
+
const Encryption = require("./encryption")
|
|
13
|
+
const errors = require("./errors")
|
|
14
|
+
|
|
15
|
+
module.exports = class ParaVFS extends ReadyResource {
|
|
16
|
+
constructor(db, store, key, options) {
|
|
17
|
+
const {
|
|
18
|
+
name = "paraql.db",
|
|
19
|
+
keyPair = null,
|
|
20
|
+
encrypted = false,
|
|
21
|
+
encryptionKey = null,
|
|
22
|
+
compressed = false,
|
|
23
|
+
compressionLevel = 6,
|
|
24
|
+
} = options
|
|
25
|
+
|
|
26
|
+
super()
|
|
27
|
+
|
|
28
|
+
this._handle = null
|
|
29
|
+
this._db = db
|
|
30
|
+
this._name = name
|
|
31
|
+
|
|
32
|
+
this._encryption = null
|
|
33
|
+
this._compressed = compressed
|
|
34
|
+
this._compressionLevel = compressionLevel
|
|
35
|
+
|
|
36
|
+
this.store = store.namespace(name)
|
|
37
|
+
this.bee = new Autobee(this.store, key, {
|
|
38
|
+
apply: this._apply.bind(this),
|
|
39
|
+
keyPair,
|
|
40
|
+
encrypted,
|
|
41
|
+
encryptionKey,
|
|
42
|
+
optimistic: false,
|
|
43
|
+
})
|
|
44
|
+
this.tmp = new RocksDB(path.resolve(this.store.storage.path, name))
|
|
45
|
+
|
|
46
|
+
this._view = null
|
|
47
|
+
this._files = new Map()
|
|
48
|
+
this._interactive = null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get name() {
|
|
52
|
+
return this._name
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get key() {
|
|
56
|
+
return this.bee.key
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get local() {
|
|
60
|
+
return this.bee.local.key
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get discoveryKey() {
|
|
64
|
+
return this.bee.discoveryKey
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get encryptionKey() {
|
|
68
|
+
return this.bee.encryptionKey
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get writable() {
|
|
72
|
+
return this.bee.writable
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
get encrypted() {
|
|
76
|
+
return !!this._encryption
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
get compressed() {
|
|
80
|
+
return !!this._compressed
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async _open() {
|
|
84
|
+
await this.bee.ready()
|
|
85
|
+
|
|
86
|
+
this._encryption =
|
|
87
|
+
this.bee.encryptionKey && new Encryption(this.key, this.bee.encryptionKey)
|
|
88
|
+
this._handle = binding.vfsInit(
|
|
89
|
+
this,
|
|
90
|
+
this._xDelete,
|
|
91
|
+
this._xAccess,
|
|
92
|
+
this._xRead,
|
|
93
|
+
this._xWrite,
|
|
94
|
+
this._xTruncate,
|
|
95
|
+
this._xSync,
|
|
96
|
+
this._xSize,
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async _close() {
|
|
101
|
+
if (this._handle) {
|
|
102
|
+
binding.vfsDestroy(this._handle)
|
|
103
|
+
|
|
104
|
+
this._handle = null
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (const batch of this._files.values()) {
|
|
108
|
+
batch.close()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
await this.bee.close()
|
|
112
|
+
await this.tmp.close()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async _apply(nodes, view, host) {
|
|
116
|
+
let stmt = null
|
|
117
|
+
let changes = 0
|
|
118
|
+
let lastInsertRowid = 0
|
|
119
|
+
let errors = 0
|
|
120
|
+
|
|
121
|
+
for (const node of nodes) {
|
|
122
|
+
const op = Operation.decode(this.compressed ? await inflate(node.value) : node.value)
|
|
123
|
+
|
|
124
|
+
switch (op.type) {
|
|
125
|
+
case OPERATION.WRITER_ADD: {
|
|
126
|
+
const batch = view.write()
|
|
127
|
+
host.addWriter(op.key)
|
|
128
|
+
await batch.flush()
|
|
129
|
+
break
|
|
130
|
+
}
|
|
131
|
+
case OPERATION.WRITER_DEL: {
|
|
132
|
+
const batch = view.write()
|
|
133
|
+
host.removeWriter(op.key)
|
|
134
|
+
await batch.flush()
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
case OPERATION.EXEC: {
|
|
138
|
+
this._write(view)
|
|
139
|
+
try {
|
|
140
|
+
await this._db._exec(op.sql)
|
|
141
|
+
this._interactive?.resolve()
|
|
142
|
+
} catch (err) {
|
|
143
|
+
this._interactive?.reject(err)
|
|
144
|
+
}
|
|
145
|
+
this._write(null)
|
|
146
|
+
break
|
|
147
|
+
}
|
|
148
|
+
case OPERATION.RUN: {
|
|
149
|
+
this._write(view)
|
|
150
|
+
try {
|
|
151
|
+
stmt = await this._db.prepare(op.sql)
|
|
152
|
+
const result = await stmt._run(op.named, op.positional)
|
|
153
|
+
this._interactive?.resolve(result)
|
|
154
|
+
} catch (err) {
|
|
155
|
+
this._interactive?.reject(err)
|
|
156
|
+
}
|
|
157
|
+
this._write(null)
|
|
158
|
+
break
|
|
159
|
+
}
|
|
160
|
+
case OPERATION.STMT: {
|
|
161
|
+
try {
|
|
162
|
+
stmt = await this._db.prepare(op.sql)
|
|
163
|
+
} catch (err) {
|
|
164
|
+
throw errors.INTERNAL(`Received invalid statement: ${op.sql}`)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
case OPERATION.BATCH: {
|
|
168
|
+
this._write(view)
|
|
169
|
+
try {
|
|
170
|
+
const result = await stmt._run(op.named, op.positional)
|
|
171
|
+
changes += result.changes
|
|
172
|
+
lastInsertRowid = result.lastInsertRowid
|
|
173
|
+
} catch (err) {
|
|
174
|
+
errors++
|
|
175
|
+
}
|
|
176
|
+
this._write(null)
|
|
177
|
+
break
|
|
178
|
+
}
|
|
179
|
+
case OPERATION.FLUSH: {
|
|
180
|
+
this._interactive?.resolve({ changes, lastInsertRowid, errors })
|
|
181
|
+
break
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
_write(view = null) {
|
|
188
|
+
if (this._files.size) {
|
|
189
|
+
throw errors.INTERNAL(`Unflushed batches: ${this._files.size}`)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
this._view = view
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async addWriter(key) {
|
|
196
|
+
await this.bee.append(
|
|
197
|
+
this.compressed
|
|
198
|
+
? await deflate(Operation.encode({ type: OPERATION.WRITER_ADD, key }), {
|
|
199
|
+
level: this._compressionLevel,
|
|
200
|
+
})
|
|
201
|
+
: Operation.encode({ type: OPERATION.WRITER_ADD, key }),
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async removeWriter(key) {
|
|
206
|
+
await this.bee.append(
|
|
207
|
+
this.compressed
|
|
208
|
+
? await deflate(Operation.encode({ type: OPERATION.WRITER_DEL, key }), {
|
|
209
|
+
level: this._compressionLevel,
|
|
210
|
+
})
|
|
211
|
+
: Operation.encode({ type: OPERATION.WRITER_DEL, key }),
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
replicate(isInitiator) {
|
|
216
|
+
return this.bee.replicate(isInitiator)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async compact() {
|
|
220
|
+
this.bee.bee.cache.empty()
|
|
221
|
+
await this.bee.bee.core.startMarking()
|
|
222
|
+
|
|
223
|
+
for await (const _ of this.bee.bee.createReadStream()) {
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
await this.bee.bee.core.sweep()
|
|
227
|
+
|
|
228
|
+
await this.bee.store.storage.db.compact()
|
|
229
|
+
|
|
230
|
+
await this.tmp.compact()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async info() {
|
|
234
|
+
const storage = this.store.storage.path
|
|
235
|
+
|
|
236
|
+
let database = 0
|
|
237
|
+
let temporary = 0
|
|
238
|
+
|
|
239
|
+
for (const entry of await fs.readdir(storage, { encoding: "utf-8", recursive: true })) {
|
|
240
|
+
const stat = await fs.stat(path.resolve(storage, entry))
|
|
241
|
+
|
|
242
|
+
if (path.dirname(entry) === this.name) {
|
|
243
|
+
temporary += stat.size
|
|
244
|
+
} else {
|
|
245
|
+
database += stat.size
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return { database, temporary, total: database + temporary }
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async exec(sql) {
|
|
253
|
+
this._interactive = new Deferred()
|
|
254
|
+
try {
|
|
255
|
+
const promise = this.bee.append(
|
|
256
|
+
this.compressed
|
|
257
|
+
? await deflate(Operation.encode({ type: OPERATION.EXEC, sql }), {
|
|
258
|
+
level: this._compressionLevel,
|
|
259
|
+
})
|
|
260
|
+
: Operation.encode({ type: OPERATION.EXEC, sql }),
|
|
261
|
+
)
|
|
262
|
+
await this._interactive.promise
|
|
263
|
+
.then(() => promise)
|
|
264
|
+
.catch(async (err) => {
|
|
265
|
+
await promise
|
|
266
|
+
throw err
|
|
267
|
+
})
|
|
268
|
+
} finally {
|
|
269
|
+
this._interactive = null
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async run(sql, named, positional) {
|
|
274
|
+
this._interactive = new Deferred()
|
|
275
|
+
try {
|
|
276
|
+
const promise = this.bee.append(
|
|
277
|
+
this.compressed
|
|
278
|
+
? await deflate(Operation.encode({ type: OPERATION.RUN, sql, named, positional }), {
|
|
279
|
+
level: this._compressionLevel,
|
|
280
|
+
})
|
|
281
|
+
: Operation.encode({ type: OPERATION.RUN, sql, named, positional }),
|
|
282
|
+
)
|
|
283
|
+
return await this._interactive.promise
|
|
284
|
+
.then(async (result) => {
|
|
285
|
+
await promise
|
|
286
|
+
return result
|
|
287
|
+
})
|
|
288
|
+
.catch(async (err) => {
|
|
289
|
+
await promise
|
|
290
|
+
throw err
|
|
291
|
+
})
|
|
292
|
+
} finally {
|
|
293
|
+
this._interactive = null
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async flush(sql, operations) {
|
|
298
|
+
this._interactive = new Deferred()
|
|
299
|
+
try {
|
|
300
|
+
const promise = this.bee.append(
|
|
301
|
+
this.compressed
|
|
302
|
+
? await Promise.all(
|
|
303
|
+
[
|
|
304
|
+
{ type: OPERATION.STMT, sql },
|
|
305
|
+
...operations.map((op) => ({ type: OPERATION.BATCH, ...op })),
|
|
306
|
+
{ type: OPERATION.FLUSH },
|
|
307
|
+
].map((op) => deflate(Operation.encode(op), { level: this._compressionLevel })),
|
|
308
|
+
)
|
|
309
|
+
: [
|
|
310
|
+
{ type: OPERATION.STMT, sql },
|
|
311
|
+
...operations.map((op) => ({ type: OPERATION.BATCH, ...op })),
|
|
312
|
+
{ type: OPERATION.FLUSH },
|
|
313
|
+
].map(Operation.encode),
|
|
314
|
+
)
|
|
315
|
+
return await this._interactive.promise
|
|
316
|
+
.then(async (result) => {
|
|
317
|
+
await promise
|
|
318
|
+
return result
|
|
319
|
+
})
|
|
320
|
+
.catch(async (err) => {
|
|
321
|
+
await promise
|
|
322
|
+
throw err
|
|
323
|
+
})
|
|
324
|
+
} finally {
|
|
325
|
+
this._interactive = null
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async _get(name, index) {
|
|
330
|
+
const tmp = name !== this.name
|
|
331
|
+
const key = PageKey.encode([name, index])
|
|
332
|
+
let value = null
|
|
333
|
+
|
|
334
|
+
if (tmp) {
|
|
335
|
+
value = await this.tmp.get(key)
|
|
336
|
+
|
|
337
|
+
if (value && this.encrypted) {
|
|
338
|
+
value = this._encryption.decrypt(value, name, index)
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
const entry = await (this._view ?? this.bee.view).get(key)
|
|
342
|
+
|
|
343
|
+
value = entry?.value ?? null
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (value && this.compressed) {
|
|
347
|
+
value = await inflate(value)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return value
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async _last(name) {
|
|
354
|
+
const tmp = name !== this.name
|
|
355
|
+
const range = PageKey.encodeRange({ gte: [name], lte: [name] })
|
|
356
|
+
const view = tmp ? this.tmp : (this._view ?? this.bee.view)
|
|
357
|
+
|
|
358
|
+
const entry = await view.peek({ ...range, reverse: true })
|
|
359
|
+
|
|
360
|
+
if (entry) {
|
|
361
|
+
const [_, index] = PageKey.decode(entry.key)
|
|
362
|
+
|
|
363
|
+
return index
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return -1
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async _tryPut(name, index, value) {
|
|
370
|
+
const tmp = name !== this.name
|
|
371
|
+
const key = PageKey.encode([name, index])
|
|
372
|
+
const view = tmp ? this.tmp : (this._view ?? this.bee.view)
|
|
373
|
+
const batch = this._files.get(name) ?? view.write()
|
|
374
|
+
|
|
375
|
+
this._files.set(name, batch)
|
|
376
|
+
|
|
377
|
+
if (this.compressed) {
|
|
378
|
+
value = await deflate(value, { level: this._compressionLevel })
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (tmp && this.encrypted) {
|
|
382
|
+
value = this._encryption.encrypt(value, name, index)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
batch.tryPut(key, value)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
async _tryDelete(name, index) {
|
|
389
|
+
const tmp = name !== this.name
|
|
390
|
+
const key = PageKey.encode([name, index])
|
|
391
|
+
const view = tmp ? this.tmp : (this._view ?? this.bee.view)
|
|
392
|
+
const batch = this._files.get(name) ?? view.write()
|
|
393
|
+
|
|
394
|
+
this._files.set(name, batch)
|
|
395
|
+
|
|
396
|
+
batch.tryDelete(key)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async _flush(name) {
|
|
400
|
+
const tmp = name !== this.name
|
|
401
|
+
const batch = this._files.get(name)
|
|
402
|
+
|
|
403
|
+
if (!batch) throw errors.INTERNAL(`No batch for ${name}`)
|
|
404
|
+
|
|
405
|
+
this._files.delete(name)
|
|
406
|
+
|
|
407
|
+
await batch.flush()
|
|
408
|
+
|
|
409
|
+
if (tmp) batch.destroy()
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async _xDelete(name, callback) {
|
|
413
|
+
try {
|
|
414
|
+
const index = await this._last(name)
|
|
415
|
+
|
|
416
|
+
if (index >= 0) {
|
|
417
|
+
for (let i = 0; i <= index; i++) {
|
|
418
|
+
await this._tryDelete(name, i)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
await this._flush(name)
|
|
423
|
+
|
|
424
|
+
callback(null)
|
|
425
|
+
} catch (err) {
|
|
426
|
+
callback(err)
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async _xAccess(name, callback) {
|
|
431
|
+
try {
|
|
432
|
+
const index = await this._last(name)
|
|
433
|
+
|
|
434
|
+
callback(null, index >= 0)
|
|
435
|
+
} catch (err) {
|
|
436
|
+
callback(err)
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async _xRead(name, buffer, offset, callback) {
|
|
441
|
+
try {
|
|
442
|
+
const data = Buffer.from(buffer)
|
|
443
|
+
const end = offset + data.byteLength
|
|
444
|
+
const startPage = Math.floor(offset / PAGE_SIZE)
|
|
445
|
+
const endPage = Math.floor((end - 1) / PAGE_SIZE)
|
|
446
|
+
|
|
447
|
+
for (let i = startPage; i <= endPage; i++) {
|
|
448
|
+
const pageStart = i * PAGE_SIZE
|
|
449
|
+
const pageEnd = pageStart + PAGE_SIZE
|
|
450
|
+
const readStart = Math.max(offset, pageStart)
|
|
451
|
+
const readEnd = Math.min(end, pageEnd)
|
|
452
|
+
|
|
453
|
+
if (readStart < readEnd) {
|
|
454
|
+
const page = (await this._get(name, i)) ?? Buffer.alloc(PAGE_SIZE)
|
|
455
|
+
const sliceStart = readStart - pageStart
|
|
456
|
+
const sliceLength = readEnd - readStart
|
|
457
|
+
const dataOffset = readStart - offset
|
|
458
|
+
|
|
459
|
+
page.copy(data, dataOffset, sliceStart, sliceStart + sliceLength)
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
callback(null)
|
|
464
|
+
} catch (err) {
|
|
465
|
+
callback(err)
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
async _xWrite(name, buffer, offset, callback) {
|
|
470
|
+
try {
|
|
471
|
+
const data = Buffer.from(buffer)
|
|
472
|
+
const start = Math.floor(offset / PAGE_SIZE)
|
|
473
|
+
const end = Math.floor((offset + data.byteLength - 1) / PAGE_SIZE)
|
|
474
|
+
|
|
475
|
+
for (let i = start; i <= end; i++) {
|
|
476
|
+
const pageStart = i * PAGE_SIZE
|
|
477
|
+
const pageEnd = pageStart + PAGE_SIZE
|
|
478
|
+
const writeStart = Math.max(offset, pageStart)
|
|
479
|
+
const writeEnd = Math.min(offset + data.byteLength, pageEnd)
|
|
480
|
+
const writeLength = writeEnd - writeStart
|
|
481
|
+
const pageOffset = writeStart - pageStart
|
|
482
|
+
const dataStart = writeStart - offset
|
|
483
|
+
|
|
484
|
+
if (writeLength === PAGE_SIZE) {
|
|
485
|
+
await this._tryPut(name, i, data.subarray(dataStart, dataStart + PAGE_SIZE))
|
|
486
|
+
} else {
|
|
487
|
+
const page = (await this._get(name, i)) ?? Buffer.alloc(PAGE_SIZE)
|
|
488
|
+
const patch = data.subarray(dataStart, dataStart + writeLength)
|
|
489
|
+
|
|
490
|
+
patch.copy(page, pageOffset)
|
|
491
|
+
await this._tryPut(name, i, page)
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
callback(null)
|
|
496
|
+
} catch (err) {
|
|
497
|
+
callback(err)
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
async _xTruncate(name, size, callback) {
|
|
502
|
+
try {
|
|
503
|
+
const index = await this._last(name)
|
|
504
|
+
const current = (index + 1) * PAGE_SIZE
|
|
505
|
+
|
|
506
|
+
if (size > current) {
|
|
507
|
+
const targetIndex = Math.floor((size - 1) / PAGE_SIZE)
|
|
508
|
+
|
|
509
|
+
for (let i = index + 1; i <= targetIndex; i++) {
|
|
510
|
+
await this._tryPut(name, i, Buffer.alloc(PAGE_SIZE))
|
|
511
|
+
}
|
|
512
|
+
} else if (size < current) {
|
|
513
|
+
const start = Math.floor(size / PAGE_SIZE)
|
|
514
|
+
const remainder = size % PAGE_SIZE
|
|
515
|
+
|
|
516
|
+
for (let i = lastIndex; i > start; i--) {
|
|
517
|
+
await this._tryDelete(name, i)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (remainder > 0) {
|
|
521
|
+
const page = await this._get(name, start)
|
|
522
|
+
const data = Buffer.alloc(PAGE_SIZE)
|
|
523
|
+
|
|
524
|
+
if (page) {
|
|
525
|
+
const length = Math.min(page.byteLength, remainder)
|
|
526
|
+
page.value.copy(data, 0, 0, length)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
await this._tryPut(name, start, data)
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
callback(null)
|
|
534
|
+
} catch (err) {
|
|
535
|
+
callback(err)
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async _xSync(name, callback) {
|
|
540
|
+
try {
|
|
541
|
+
await this._flush(name)
|
|
542
|
+
callback(null)
|
|
543
|
+
} catch (err) {
|
|
544
|
+
callback(err)
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
async _xSize(name, callback) {
|
|
549
|
+
try {
|
|
550
|
+
const index = await this._last(name)
|
|
551
|
+
const size = (index + 1) * PAGE_SIZE
|
|
552
|
+
|
|
553
|
+
callback(null, size)
|
|
554
|
+
} catch (err) {
|
|
555
|
+
callback(err)
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bullet./paraql",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Parallel multi-writer relational database with SQL syntax. Native support for encryption, compression, and vector search.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Tomas Ravinskas",
|
|
7
|
+
"email": "tomas@tomasrav.me",
|
|
8
|
+
"url": "https://tomasrav.me/"
|
|
9
|
+
},
|
|
10
|
+
"license": "Apache-2.0",
|
|
11
|
+
"addon": true,
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "npm run test:bare && npm run test:node",
|
|
14
|
+
"test:bare": "brittle-bare test/all.js",
|
|
15
|
+
"test:node": "brittle-node test/all.js",
|
|
16
|
+
"test:manual": "bare test/manual.js",
|
|
17
|
+
"bench": "brittle-bare test/bench.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"index.js",
|
|
21
|
+
"binding.c",
|
|
22
|
+
"binding.js",
|
|
23
|
+
"CMakeLists.txt",
|
|
24
|
+
"NOTICE",
|
|
25
|
+
"cmake",
|
|
26
|
+
"lib",
|
|
27
|
+
"prebuilds"
|
|
28
|
+
],
|
|
29
|
+
"exports": {
|
|
30
|
+
"./package": "./package.json",
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./index.d.ts",
|
|
33
|
+
"default": "./index.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"imports": {
|
|
37
|
+
"buffer": {
|
|
38
|
+
"bare": "bare-buffer",
|
|
39
|
+
"default": "buffer"
|
|
40
|
+
},
|
|
41
|
+
"fs": {
|
|
42
|
+
"bare": "bare-fs",
|
|
43
|
+
"default": "fs"
|
|
44
|
+
},
|
|
45
|
+
"fs/promises": {
|
|
46
|
+
"bare": "bare-fs/promises",
|
|
47
|
+
"default": "fs/promises"
|
|
48
|
+
},
|
|
49
|
+
"path": {
|
|
50
|
+
"bare": "bare-path",
|
|
51
|
+
"default": "path"
|
|
52
|
+
},
|
|
53
|
+
"zlib": {
|
|
54
|
+
"bare": "bare-zlib",
|
|
55
|
+
"default": "zlib"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"homepage": "https://github.com/getbullet-app/paraql",
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "https://github.com/getbullet-app/paraql.git"
|
|
62
|
+
},
|
|
63
|
+
"funding": [
|
|
64
|
+
{
|
|
65
|
+
"type": "github",
|
|
66
|
+
"url": "https://github.com/sponsors/OzymandiasTheGreat"
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"keywords": [
|
|
70
|
+
"sql",
|
|
71
|
+
"autobase",
|
|
72
|
+
"vector-search"
|
|
73
|
+
],
|
|
74
|
+
"dependencies": {
|
|
75
|
+
"autobee": "^1.0.10",
|
|
76
|
+
"bare-buffer": "^3.6.2",
|
|
77
|
+
"bare-fs": "^4.7.4",
|
|
78
|
+
"bare-path": "^3.1.1",
|
|
79
|
+
"bare-zlib": "^1.4.1",
|
|
80
|
+
"compact-encoding": "^3.3.0",
|
|
81
|
+
"index-encoder": "^3.5.0",
|
|
82
|
+
"ready-resource": "^1.2.0",
|
|
83
|
+
"require-addon": "^1.2.0",
|
|
84
|
+
"rocksdb-native": "^3.17.2",
|
|
85
|
+
"sodium-native": "^5.1.0"
|
|
86
|
+
},
|
|
87
|
+
"devDependencies": {
|
|
88
|
+
"bare-compat-napi": "^1.3.9",
|
|
89
|
+
"brittle": "^4.0.2",
|
|
90
|
+
"cmake-bare": "^1.8.0",
|
|
91
|
+
"cmake-fetch": "^1.5.2",
|
|
92
|
+
"cmake-napi": "^1.2.2",
|
|
93
|
+
"cmake-npm": "^1.1.2",
|
|
94
|
+
"corestore": "^7.11.0",
|
|
95
|
+
"prettier": "^3.9.4",
|
|
96
|
+
"test-tmp": "^1.4.0"
|
|
97
|
+
}
|
|
98
|
+
}
|