@reddb-io/client 1.26.4 → 1.26.5
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/package.json +1 -1
- package/src/redwire-core.js +64 -40
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reddb-io/client",
|
|
3
|
-
"version": "1.26.
|
|
3
|
+
"version": "1.26.5",
|
|
4
4
|
"description": "Thin remote-only RedDB driver. Pure JavaScript — no install scripts, no native binary. Speaks RedWire/gRPC/HTTP. Embedded URIs (memory://, file://, red:///path) are rejected — use @reddb-io/sdk for those.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/redwire-core.js
CHANGED
|
@@ -672,28 +672,30 @@ export function decodeFrame(buf) {
|
|
|
672
672
|
class FrameReader {
|
|
673
673
|
constructor(socket) {
|
|
674
674
|
this.chunks = []
|
|
675
|
+
this.chunkIndex = 0
|
|
676
|
+
this.chunkOffset = 0
|
|
675
677
|
this.totalLen = 0
|
|
678
|
+
this.frameLength = 0
|
|
676
679
|
this.waiters = []
|
|
677
680
|
this.error = null
|
|
678
681
|
this.eof = false
|
|
679
682
|
socket.on('data', (chunk) => {
|
|
683
|
+
if (this.error || this.eof) return
|
|
680
684
|
// chunk is Buffer (Node) or Uint8Array (Bun/Deno)
|
|
681
685
|
const u8 = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk)
|
|
686
|
+
if (u8.length === 0) return
|
|
682
687
|
this.chunks.push(u8)
|
|
683
688
|
this.totalLen += u8.length
|
|
684
689
|
this.#tryDeliver()
|
|
685
690
|
})
|
|
686
|
-
socket.on('error', (err) =>
|
|
687
|
-
this.error = err
|
|
688
|
-
this.#flushWaiters()
|
|
689
|
-
})
|
|
691
|
+
socket.on('error', (err) => this.#fail(err))
|
|
690
692
|
socket.on('end', () => {
|
|
691
693
|
this.eof = true
|
|
692
|
-
this.#
|
|
694
|
+
this.#tryDeliver()
|
|
693
695
|
})
|
|
694
696
|
socket.on('close', () => {
|
|
695
697
|
this.eof = true
|
|
696
|
-
this.#
|
|
698
|
+
this.#tryDeliver()
|
|
697
699
|
})
|
|
698
700
|
}
|
|
699
701
|
|
|
@@ -706,57 +708,79 @@ class FrameReader {
|
|
|
706
708
|
}
|
|
707
709
|
|
|
708
710
|
#tryDeliver() {
|
|
709
|
-
while (this.waiters.length > 0 && this.totalLen
|
|
710
|
-
const flat = this.#flatten()
|
|
711
|
+
while (this.waiters.length > 0 && this.totalLen >= FRAME_HEADER_SIZE) {
|
|
711
712
|
let frame
|
|
712
713
|
try {
|
|
713
|
-
|
|
714
|
+
if (this.frameLength === 0) this.frameLength = this.#readLength()
|
|
715
|
+
if (this.totalLen < this.frameLength) break
|
|
716
|
+
frame = decodeFrame(this.#take(this.frameLength))
|
|
717
|
+
this.frameLength = 0
|
|
714
718
|
} catch (err) {
|
|
715
|
-
|
|
716
|
-
w.reject(err)
|
|
717
|
-
return
|
|
718
|
-
}
|
|
719
|
-
if (frame == null) {
|
|
720
|
-
// Need more bytes — put the flattened buffer back as a
|
|
721
|
-
// single chunk so we don't keep flattening repeatedly.
|
|
722
|
-
this.chunks = [flat]
|
|
719
|
+
this.#fail(err)
|
|
723
720
|
return
|
|
724
721
|
}
|
|
725
|
-
this.chunks = [flat.subarray(frame.consumed)]
|
|
726
|
-
this.totalLen = this.chunks[0].length
|
|
727
722
|
const w = this.waiters.shift()
|
|
728
723
|
w.resolve(frame)
|
|
729
724
|
}
|
|
730
|
-
if (this.eof && this.waiters.length > 0
|
|
731
|
-
|
|
732
|
-
while (this.waiters.length > 0) {
|
|
733
|
-
this.waiters.shift().reject(err)
|
|
734
|
-
}
|
|
725
|
+
if (this.eof && this.waiters.length > 0) {
|
|
726
|
+
this.#fail(new RedDBError('CONNECTION_CLOSED', 'redwire: connection closed'))
|
|
735
727
|
}
|
|
736
728
|
}
|
|
737
729
|
|
|
738
|
-
#
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
730
|
+
#readLength() {
|
|
731
|
+
let length = 0
|
|
732
|
+
let shift = 0
|
|
733
|
+
for (let index = this.chunkIndex; shift < 32; index++) {
|
|
734
|
+
const chunk = this.chunks[index]
|
|
735
|
+
const start = index === this.chunkIndex ? this.chunkOffset : 0
|
|
736
|
+
for (let offset = start; offset < chunk.length && shift < 32; offset++, shift += 8) {
|
|
737
|
+
length += chunk[offset] * 2 ** shift
|
|
738
|
+
}
|
|
743
739
|
}
|
|
744
|
-
|
|
740
|
+
if (length < FRAME_HEADER_SIZE || length > MAX_FRAME_SIZE) {
|
|
741
|
+
throw new RedDBError('FRAME_INVALID_LENGTH', `length=${length}`)
|
|
742
|
+
}
|
|
743
|
+
return length
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
#fail(error) {
|
|
747
|
+
this.error ??= error
|
|
748
|
+
this.chunks = []
|
|
749
|
+
this.chunkIndex = 0
|
|
750
|
+
this.chunkOffset = 0
|
|
751
|
+
this.totalLen = 0
|
|
752
|
+
this.frameLength = 0
|
|
745
753
|
while (this.waiters.length > 0) {
|
|
746
|
-
this.waiters.shift().reject(
|
|
754
|
+
this.waiters.shift().reject(this.error)
|
|
747
755
|
}
|
|
748
756
|
}
|
|
749
757
|
|
|
750
|
-
#
|
|
751
|
-
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
758
|
+
#take(length) {
|
|
759
|
+
const first = this.chunks[this.chunkIndex]
|
|
760
|
+
const contiguous = first.length - this.chunkOffset >= length
|
|
761
|
+
// A plain Uint8Array keeps decodeFrame's payload slice owned, even for Buffer input.
|
|
762
|
+
const bytes = contiguous
|
|
763
|
+
? new Uint8Array(first.buffer, first.byteOffset + this.chunkOffset, length)
|
|
764
|
+
: new Uint8Array(length)
|
|
765
|
+
let written = 0
|
|
766
|
+
while (written < length) {
|
|
767
|
+
const chunk = this.chunks[this.chunkIndex]
|
|
768
|
+
const count = Math.min(length - written, chunk.length - this.chunkOffset)
|
|
769
|
+
if (!contiguous) bytes.set(chunk.subarray(this.chunkOffset, this.chunkOffset + count), written)
|
|
770
|
+
written += count
|
|
771
|
+
this.chunkOffset += count
|
|
772
|
+
if (this.chunkOffset === chunk.length) {
|
|
773
|
+
this.chunks[this.chunkIndex++] = null
|
|
774
|
+
this.chunkOffset = 0
|
|
775
|
+
}
|
|
757
776
|
}
|
|
758
|
-
this.
|
|
759
|
-
|
|
777
|
+
this.totalLen -= length
|
|
778
|
+
// Release consumed buffers immediately; compact references only after half are consumed.
|
|
779
|
+
if (this.chunkIndex * 2 >= this.chunks.length) {
|
|
780
|
+
this.chunks = this.chunks.slice(this.chunkIndex)
|
|
781
|
+
this.chunkIndex = 0
|
|
782
|
+
}
|
|
783
|
+
return bytes
|
|
760
784
|
}
|
|
761
785
|
}
|
|
762
786
|
|