hypercore 9.12.0 → 10.0.0-alpha.11

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.
Files changed (86) hide show
  1. package/.github/workflows/test-node.yml +3 -4
  2. package/README.md +131 -404
  3. package/__snapshots__/test/storage.js.snapshot.cjs +15 -0
  4. package/examples/announce.js +19 -0
  5. package/examples/basic.js +10 -0
  6. package/examples/http.js +123 -0
  7. package/examples/lookup.js +20 -0
  8. package/index.js +365 -1600
  9. package/lib/bitfield.js +113 -285
  10. package/lib/block-encryption.js +68 -0
  11. package/lib/block-store.js +58 -0
  12. package/lib/core.js +468 -0
  13. package/lib/extensions.js +76 -0
  14. package/lib/merkle-tree.js +1110 -0
  15. package/lib/messages.js +571 -0
  16. package/lib/mutex.js +39 -0
  17. package/lib/oplog.js +224 -0
  18. package/lib/protocol.js +525 -0
  19. package/lib/random-iterator.js +46 -0
  20. package/lib/remote-bitfield.js +24 -0
  21. package/lib/replicator.js +857 -0
  22. package/lib/streams.js +39 -0
  23. package/package.json +44 -45
  24. package/test/basic.js +59 -471
  25. package/test/bitfield.js +48 -133
  26. package/test/core.js +290 -0
  27. package/test/encodings.js +18 -0
  28. package/test/encryption.js +123 -0
  29. package/test/extension.js +71 -0
  30. package/test/helpers/index.js +23 -0
  31. package/test/merkle-tree.js +518 -0
  32. package/test/mutex.js +137 -0
  33. package/test/oplog.js +399 -0
  34. package/test/preload.js +72 -0
  35. package/test/replicate.js +227 -824
  36. package/test/sessions.js +173 -0
  37. package/test/storage.js +31 -0
  38. package/test/streams.js +39 -146
  39. package/test/user-data.js +47 -0
  40. package/bench/all.sh +0 -65
  41. package/bench/copy-64kb-blocks.js +0 -51
  42. package/bench/helpers/read-throttled.js +0 -27
  43. package/bench/helpers/read.js +0 -47
  44. package/bench/helpers/write.js +0 -29
  45. package/bench/read-16kb-blocks-proof-throttled.js +0 -1
  46. package/bench/read-16kb-blocks-proof.js +0 -1
  47. package/bench/read-16kb-blocks-throttled.js +0 -1
  48. package/bench/read-16kb-blocks.js +0 -1
  49. package/bench/read-512b-blocks.js +0 -1
  50. package/bench/read-64kb-blocks-linear-batch.js +0 -18
  51. package/bench/read-64kb-blocks-linear.js +0 -18
  52. package/bench/read-64kb-blocks-proof.js +0 -1
  53. package/bench/read-64kb-blocks.js +0 -1
  54. package/bench/replicate-16kb-blocks.js +0 -19
  55. package/bench/replicate-64kb-blocks.js +0 -19
  56. package/bench/write-16kb-blocks.js +0 -1
  57. package/bench/write-512b-blocks.js +0 -1
  58. package/bench/write-64kb-blocks-static.js +0 -1
  59. package/bench/write-64kb-blocks.js +0 -1
  60. package/example.js +0 -23
  61. package/lib/cache.js +0 -26
  62. package/lib/crypto.js +0 -5
  63. package/lib/replicate.js +0 -829
  64. package/lib/safe-buffer-equals.js +0 -6
  65. package/lib/storage.js +0 -421
  66. package/lib/tree-index.js +0 -183
  67. package/test/ack.js +0 -306
  68. package/test/audit.js +0 -36
  69. package/test/cache.js +0 -93
  70. package/test/compat.js +0 -209
  71. package/test/copy.js +0 -377
  72. package/test/default-storage.js +0 -51
  73. package/test/extensions.js +0 -137
  74. package/test/get.js +0 -64
  75. package/test/head.js +0 -65
  76. package/test/helpers/create-tracking-ram.js +0 -27
  77. package/test/helpers/create.js +0 -6
  78. package/test/helpers/replicate.js +0 -4
  79. package/test/seek.js +0 -234
  80. package/test/selections.js +0 -95
  81. package/test/set-uploading-downloading.js +0 -91
  82. package/test/stats.js +0 -77
  83. package/test/timeouts.js +0 -22
  84. package/test/tree-index.js +0 -841
  85. package/test/update.js +0 -156
  86. package/test/value-encoding.js +0 -52
package/lib/oplog.js ADDED
@@ -0,0 +1,224 @@
1
+ const cenc = require('compact-encoding')
2
+ const b4a = require('b4a')
3
+ const crc32 = require('crc32-universal')
4
+
5
+ module.exports = class Oplog {
6
+ constructor (storage, { pageSize = 4096, headerEncoding = cenc.raw, entryEncoding = cenc.raw } = {}) {
7
+ this.storage = storage
8
+ this.headerEncoding = headerEncoding
9
+ this.entryEncoding = entryEncoding
10
+ this.flushed = false
11
+ this.byteLength = 0
12
+ this.length = 0
13
+
14
+ this._headers = [1, 0]
15
+ this._pageSize = pageSize
16
+ this._entryOffset = pageSize * 2
17
+ }
18
+
19
+ _addHeader (state, len, headerBit, partialBit) {
20
+ // add the uint header (frame length and flush info)
21
+ state.start = state.start - len - 4
22
+ cenc.uint32.encode(state, (len << 2) | headerBit | partialBit)
23
+
24
+ // crc32 the length + header-bit + content and prefix it
25
+ state.start -= 8
26
+ cenc.uint32.encode(state, crc32(state.buffer.subarray(state.start + 4, state.start + 8 + len)))
27
+ state.start += len + 4
28
+ }
29
+
30
+ _decodeEntry (state, enc) {
31
+ if (state.end - state.start < 8) return null
32
+ const cksum = cenc.uint32.decode(state)
33
+ const l = cenc.uint32.decode(state)
34
+ const length = l >>> 2
35
+ const headerBit = l & 1
36
+ const partialBit = l & 2
37
+
38
+ if (state.end - state.start < length) return null
39
+
40
+ const end = state.start + length
41
+
42
+ if (crc32(state.buffer.subarray(state.start - 4, end)) !== cksum) {
43
+ return null
44
+ }
45
+
46
+ const result = { header: headerBit, partial: partialBit !== 0, byteLength: length + 8, message: null }
47
+
48
+ try {
49
+ result.message = enc.decode({ start: state.start, end, buffer: state.buffer })
50
+ } catch {
51
+ return null
52
+ }
53
+
54
+ state.start = end
55
+
56
+ return result
57
+ }
58
+
59
+ async open () {
60
+ const buffer = await this._readAll() // TODO: stream the oplog in on load maybe?
61
+ const state = { start: 0, end: buffer.byteLength, buffer }
62
+ const result = { header: null, entries: [] }
63
+
64
+ this.byteLength = 0
65
+ this.length = 0
66
+
67
+ const h1 = this._decodeEntry(state, this.headerEncoding)
68
+ state.start = this._pageSize
69
+
70
+ const h2 = this._decodeEntry(state, this.headerEncoding)
71
+ state.start = this._entryOffset
72
+
73
+ if (!h1 && !h2) {
74
+ // reset state...
75
+ this.flushed = false
76
+ this._headers[0] = 1
77
+ this._headers[1] = 0
78
+
79
+ if (buffer.byteLength >= this._entryOffset) {
80
+ throw new Error('Oplog file appears corrupt or out of date')
81
+ }
82
+ return result
83
+ }
84
+
85
+ this.flushed = true
86
+
87
+ if (h1 && !h2) {
88
+ this._headers[0] = h1.header
89
+ this._headers[1] = h1.header
90
+ } else if (!h1 && h2) {
91
+ this._headers[0] = (h2.header + 1) & 1
92
+ this._headers[1] = h2.header
93
+ } else {
94
+ this._headers[0] = h1.header
95
+ this._headers[1] = h2.header
96
+ }
97
+
98
+ const header = (this._headers[0] + this._headers[1]) & 1
99
+ const decoded = []
100
+
101
+ result.header = header ? h2.message : h1.message
102
+
103
+ while (true) {
104
+ const entry = this._decodeEntry(state, this.entryEncoding)
105
+ if (!entry) break
106
+ if (entry.header !== header) break
107
+
108
+ decoded.push(entry)
109
+ }
110
+
111
+ while (decoded.length > 0 && decoded[decoded.length - 1].partial) decoded.pop()
112
+
113
+ for (const e of decoded) {
114
+ result.entries.push(e.message)
115
+ this.byteLength += e.byteLength
116
+ this.length++
117
+ }
118
+
119
+ const size = this.byteLength + this._entryOffset
120
+
121
+ if (size === buffer.byteLength) return result
122
+
123
+ await new Promise((resolve, reject) => {
124
+ this.storage.del(size, Infinity, err => {
125
+ if (err) return reject(err)
126
+ resolve()
127
+ })
128
+ })
129
+
130
+ return result
131
+ }
132
+
133
+ _readAll () {
134
+ return new Promise((resolve, reject) => {
135
+ this.storage.open(err => {
136
+ if (err && err.code !== 'ENOENT') return reject(err)
137
+ if (err) return resolve(b4a.alloc(0))
138
+ this.storage.stat((err, stat) => {
139
+ if (err && err.code !== 'ENOENT') return reject(err)
140
+ this.storage.read(0, stat.size, (err, buf) => {
141
+ if (err) return reject(err)
142
+ resolve(buf)
143
+ })
144
+ })
145
+ })
146
+ })
147
+ }
148
+
149
+ flush (header) {
150
+ const state = { start: 8, end: 8, buffer: null }
151
+ const i = this._headers[0] === this._headers[1] ? 1 : 0
152
+ const bit = (this._headers[i] + 1) & 1
153
+
154
+ this.headerEncoding.preencode(state, header)
155
+ state.buffer = b4a.allocUnsafe(state.end)
156
+ this.headerEncoding.encode(state, header)
157
+ this._addHeader(state, state.end - 8, bit, 0)
158
+
159
+ return this._writeHeaderAndTruncate(i, bit, state.buffer)
160
+ }
161
+
162
+ _writeHeaderAndTruncate (i, bit, buf) {
163
+ return new Promise((resolve, reject) => {
164
+ this.storage.write(i === 0 ? 0 : this._pageSize, buf, err => {
165
+ if (err) return reject(err)
166
+
167
+ this.storage.del(this._entryOffset, Infinity, err => {
168
+ if (err) return reject(err)
169
+
170
+ this._headers[i] = bit
171
+ this.byteLength = 0
172
+ this.length = 0
173
+ this.flushed = true
174
+
175
+ resolve()
176
+ })
177
+ })
178
+ })
179
+ }
180
+
181
+ append (batch, atomic = true) {
182
+ if (!Array.isArray(batch)) batch = [batch]
183
+
184
+ const state = { start: 0, end: batch.length * 8, buffer: null }
185
+ const bit = (this._headers[0] + this._headers[1]) & 1
186
+
187
+ for (let i = 0; i < batch.length; i++) {
188
+ this.entryEncoding.preencode(state, batch[i])
189
+ }
190
+
191
+ state.buffer = b4a.allocUnsafe(state.end)
192
+
193
+ for (let i = 0; i < batch.length; i++) {
194
+ const start = state.start += 8 // space for header
195
+ const partial = (atomic && i < batch.length - 1) ? 2 : 0
196
+ this.entryEncoding.encode(state, batch[i])
197
+ this._addHeader(state, state.start - start, bit, partial)
198
+ }
199
+
200
+ return this._append(state.buffer, batch.length)
201
+ }
202
+
203
+ close () {
204
+ return new Promise((resolve, reject) => {
205
+ this.storage.close(err => {
206
+ if (err) return reject(err)
207
+ resolve()
208
+ })
209
+ })
210
+ }
211
+
212
+ _append (buf, count) {
213
+ return new Promise((resolve, reject) => {
214
+ this.storage.write(this._entryOffset + this.byteLength, buf, err => {
215
+ if (err) return reject(err)
216
+
217
+ this.byteLength += buf.byteLength
218
+ this.length += count
219
+
220
+ resolve()
221
+ })
222
+ })
223
+ }
224
+ }