hypercore 11.0.9 → 11.0.10
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/index.js +9 -9
- package/lib/core.js +4 -1
- package/lib/merkle-tree.js +1 -0
- package/lib/session-state.js +134 -98
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -344,18 +344,20 @@ class Hypercore extends EventEmitter {
|
|
|
344
344
|
const state = this.state
|
|
345
345
|
|
|
346
346
|
if (opts.atom) {
|
|
347
|
-
this.state = await parent.state.createSession(null,
|
|
347
|
+
this.state = await parent.state.createSession(null, false, opts.atom)
|
|
348
348
|
if (state) state.unref()
|
|
349
349
|
} else if (opts.name) {
|
|
350
350
|
// todo: need to make named sessions safe before ready
|
|
351
351
|
// atm we always copy the state in passCapabilities
|
|
352
|
-
this.state = await parent.state.createSession(opts.name,
|
|
352
|
+
this.state = await parent.state.createSession(opts.name, !!opts.overwrite, null)
|
|
353
353
|
if (state) state.unref() // ref'ed above in setup session
|
|
354
|
+
}
|
|
354
355
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
356
|
+
if (this.state && checkout !== -1 && checkout < this.state.length) {
|
|
357
|
+
await this.state.truncate(checkout, this.fork)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (this.state === null) {
|
|
359
361
|
this.state = this.core.state.ref()
|
|
360
362
|
}
|
|
361
363
|
|
|
@@ -520,9 +522,7 @@ class Hypercore extends EventEmitter {
|
|
|
520
522
|
get signedLength () {
|
|
521
523
|
if (this.opened === false) return 0
|
|
522
524
|
if (this.state === this.core.state) return this.core.state.length
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
return flushed === -1 ? this.state.length : flushed
|
|
525
|
+
return this.state.flushedLength()
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
/**
|
package/lib/core.js
CHANGED
|
@@ -240,6 +240,7 @@ module.exports = class Core {
|
|
|
240
240
|
|
|
241
241
|
const treeInfo = {
|
|
242
242
|
fork: header.tree.fork,
|
|
243
|
+
length: header.tree.length,
|
|
243
244
|
signature: header.tree.signature,
|
|
244
245
|
roots: header.tree.length ? await tree.getRoots(header.tree.length) : [],
|
|
245
246
|
prologue
|
|
@@ -417,7 +418,9 @@ module.exports = class Core {
|
|
|
417
418
|
|
|
418
419
|
if (this.preupdate !== null) await this.preupdate(batch, this.header.key)
|
|
419
420
|
|
|
420
|
-
await this.state._verifyBlock(batch, bitfield, value, this.header.manifest ? null : manifest)
|
|
421
|
+
if (!(await this.state._verifyBlock(batch, bitfield, value, this.header.manifest ? null : manifest))) {
|
|
422
|
+
return false
|
|
423
|
+
}
|
|
421
424
|
|
|
422
425
|
if (!batch.upgraded && bitfield) {
|
|
423
426
|
this.replicator.onhave(bitfield.start, bitfield.length, bitfield.drop)
|
package/lib/merkle-tree.js
CHANGED
package/lib/session-state.js
CHANGED
|
@@ -4,7 +4,7 @@ const assert = require('nanoassert')
|
|
|
4
4
|
const flat = require('flat-tree')
|
|
5
5
|
const quickbit = require('quickbit-universal')
|
|
6
6
|
|
|
7
|
-
const {
|
|
7
|
+
const { INVALID_OPERATION, INVALID_SIGNATURE } = require('hypercore-errors')
|
|
8
8
|
|
|
9
9
|
const Mutex = require('./mutex')
|
|
10
10
|
const Bitfield = require('./bitfield')
|
|
@@ -38,10 +38,6 @@ module.exports = class SessionState {
|
|
|
38
38
|
this.prologue = treeInfo.prologue || null
|
|
39
39
|
this.signature = treeInfo.signature || null
|
|
40
40
|
|
|
41
|
-
const deps = this.storage.dependencies
|
|
42
|
-
this.dependencyLength = deps.length ? deps[deps.length - 1].length : Infinity
|
|
43
|
-
|
|
44
|
-
if (treeInfo.length < this.dependencyLength) this.dependencyLength = treeInfo.length
|
|
45
41
|
if (treeInfo.roots.length) this.setRoots(treeInfo.roots)
|
|
46
42
|
|
|
47
43
|
this.snapshotCompatLength = this.isSnapshot() ? this.length : -1
|
|
@@ -61,7 +57,11 @@ module.exports = class SessionState {
|
|
|
61
57
|
}
|
|
62
58
|
|
|
63
59
|
isDefault () {
|
|
64
|
-
return this.core.state === this
|
|
60
|
+
return this.core.state === this || this.isAtomicDefault()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
isAtomicDefault () {
|
|
64
|
+
return !!this.storage.atom && !!this.parent && this.parent.isDefault()
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
createTreeBatch () {
|
|
@@ -85,7 +85,9 @@ module.exports = class SessionState {
|
|
|
85
85
|
|
|
86
86
|
flushedLength () {
|
|
87
87
|
if (this.isDefault() || this.isSnapshot()) return this.length
|
|
88
|
-
|
|
88
|
+
const deps = this.storage.dependencies
|
|
89
|
+
if (deps.length) return deps[deps.length - 1].length
|
|
90
|
+
return 0
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
unref () {
|
|
@@ -161,11 +163,8 @@ module.exports = class SessionState {
|
|
|
161
163
|
}
|
|
162
164
|
|
|
163
165
|
updateDependency (tx, length) {
|
|
164
|
-
const dependency = updateDependency(this, length)
|
|
165
|
-
if (dependency)
|
|
166
|
-
this.dependencyLength = dependency.length
|
|
167
|
-
tx.setDependency(dependency)
|
|
168
|
-
}
|
|
166
|
+
const dependency = updateDependency(this, length, false)
|
|
167
|
+
if (dependency) tx.setDependency(dependency)
|
|
169
168
|
|
|
170
169
|
return dependency
|
|
171
170
|
}
|
|
@@ -232,34 +231,19 @@ module.exports = class SessionState {
|
|
|
232
231
|
signature: this.signature
|
|
233
232
|
}
|
|
234
233
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
this.storage.updateDependencyLength(src.dependencyLength)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// handle migration
|
|
241
|
-
if (src.core !== this.core) {
|
|
242
|
-
if (this.lingers === null) this.lingers = []
|
|
243
|
-
this.lingers.push(this.storage)
|
|
234
|
+
const rx = src.storage.read()
|
|
235
|
+
const dependencyPromise = rx.getDependency()
|
|
244
236
|
|
|
245
|
-
|
|
246
|
-
this.storage = await src.core.state.storage.resumeSession(this.name)
|
|
247
|
-
this.tree = new MerkleTree(this.storage)
|
|
248
|
-
|
|
249
|
-
for (let i = this.core.sessionStates.length - 1; i >= 0; i--) {
|
|
250
|
-
const state = this.core.sessionStates[i]
|
|
251
|
-
if (state === this) continue
|
|
252
|
-
if (state.name === this.name) state._moveToCore(src.core)
|
|
253
|
-
}
|
|
237
|
+
rx.tryFlush()
|
|
254
238
|
|
|
255
|
-
|
|
239
|
+
const dependency = await dependencyPromise
|
|
240
|
+
if (dependency && dependency.length > this.flushedLength()) {
|
|
241
|
+
this.storage.updateDependencyLength(dependency.length, false, true)
|
|
256
242
|
}
|
|
257
243
|
|
|
258
|
-
const truncated =
|
|
259
|
-
? bitfield.truncated
|
|
260
|
-
: src.dependencyLength
|
|
244
|
+
const truncated = bitfield ? bitfield.truncated : -1
|
|
261
245
|
|
|
262
|
-
if (truncated < currLength) {
|
|
246
|
+
if (truncated !== -1 && truncated < currLength) {
|
|
263
247
|
this.ontruncate(tree, truncated, currLength, true)
|
|
264
248
|
if (!bitfield || bitfield.length === 0) return
|
|
265
249
|
}
|
|
@@ -296,6 +280,7 @@ module.exports = class SessionState {
|
|
|
296
280
|
await this.mutex.lock()
|
|
297
281
|
|
|
298
282
|
try {
|
|
283
|
+
if (!batch.commitable()) return false
|
|
299
284
|
const tx = this.createWriteBatch()
|
|
300
285
|
this.updating = true
|
|
301
286
|
|
|
@@ -307,9 +292,8 @@ module.exports = class SessionState {
|
|
|
307
292
|
|
|
308
293
|
if (manifest) this.core._setManifest(tx, manifest, null)
|
|
309
294
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
295
|
+
assert(batch.commitable(), 'Should still be commitable')
|
|
296
|
+
batch.commit(tx)
|
|
313
297
|
|
|
314
298
|
const head = {
|
|
315
299
|
fork: batch.fork,
|
|
@@ -335,6 +319,8 @@ module.exports = class SessionState {
|
|
|
335
319
|
this.updating = false
|
|
336
320
|
this.mutex.unlock()
|
|
337
321
|
}
|
|
322
|
+
|
|
323
|
+
return true
|
|
338
324
|
}
|
|
339
325
|
|
|
340
326
|
async truncate (length, fork, { signature, keyPair } = {}) {
|
|
@@ -367,7 +353,7 @@ module.exports = class SessionState {
|
|
|
367
353
|
this.roots = roots
|
|
368
354
|
this.signature = tree.signature
|
|
369
355
|
|
|
370
|
-
if (dependency) this.storage.updateDependencyLength(
|
|
356
|
+
if (dependency) this.storage.updateDependencyLength(dependency.length, true)
|
|
371
357
|
|
|
372
358
|
this.ontruncate(tree, tree.length, batch.treeLength, flushed)
|
|
373
359
|
} finally {
|
|
@@ -392,7 +378,7 @@ module.exports = class SessionState {
|
|
|
392
378
|
this.roots = batch.roots
|
|
393
379
|
this.signature = batch.signature
|
|
394
380
|
|
|
395
|
-
if (dependency) this.storage.updateDependencyLength(
|
|
381
|
+
if (dependency) this.storage.updateDependencyLength(dependency.length, true)
|
|
396
382
|
|
|
397
383
|
this.ontruncate(tree, batch.ancestors, batch.treeLength, flushed)
|
|
398
384
|
} finally {
|
|
@@ -415,9 +401,9 @@ module.exports = class SessionState {
|
|
|
415
401
|
if (tree) storage.setHead(tree)
|
|
416
402
|
|
|
417
403
|
const truncated = batch.length < this.flushedLength()
|
|
418
|
-
const dependency = truncated ? updateDependency(this, batch.length) : null
|
|
404
|
+
const dependency = truncated ? updateDependency(this, batch.length, true) : null
|
|
419
405
|
|
|
420
|
-
if (dependency)
|
|
406
|
+
if (dependency) storage.setDependency(dependency)
|
|
421
407
|
|
|
422
408
|
if (this.isDefault()) {
|
|
423
409
|
await storeBitfieldRange(this.storage, storage, batch.ancestors, batch.treeLength, false)
|
|
@@ -436,13 +422,11 @@ module.exports = class SessionState {
|
|
|
436
422
|
|
|
437
423
|
this.blocks.clear(tx, start, end)
|
|
438
424
|
|
|
439
|
-
const dependency = start < this.flushedLength() ? updateDependency(this, start) : null
|
|
440
|
-
|
|
441
|
-
if (dependency) this.dependencyLength = dependency.length
|
|
425
|
+
const dependency = start < this.flushedLength() ? updateDependency(this, start, true) : null
|
|
442
426
|
|
|
443
427
|
const flushed = await this.flush()
|
|
444
428
|
|
|
445
|
-
if (dependency) this.storage.updateDependencyLength(
|
|
429
|
+
if (dependency) this.storage.updateDependencyLength(dependency.length, true)
|
|
446
430
|
|
|
447
431
|
// todo: atomic event handle
|
|
448
432
|
if (this.isDefault() && flushed) {
|
|
@@ -578,6 +562,80 @@ module.exports = class SessionState {
|
|
|
578
562
|
p.length += b.length
|
|
579
563
|
}
|
|
580
564
|
|
|
565
|
+
async catchup (length) {
|
|
566
|
+
assert(!this.isDefault(), 'Cannot catchup signed state') // TODO: make this check better
|
|
567
|
+
|
|
568
|
+
await this.mutex.lock()
|
|
569
|
+
|
|
570
|
+
try {
|
|
571
|
+
if (length < this.length) throw INVALID_OPERATION('Can not catchup back in time')
|
|
572
|
+
if (length === this.length) return
|
|
573
|
+
|
|
574
|
+
const origLength = this.length
|
|
575
|
+
|
|
576
|
+
let sharedLength = 0
|
|
577
|
+
for (let i = this.storage.dependencies.length - 1; i >= 0; i--) {
|
|
578
|
+
const dep = this.storage.dependencies[i]
|
|
579
|
+
if (dep.dataPointer === this.core.state.storage.core.dataPointer) {
|
|
580
|
+
sharedLength = dep.length
|
|
581
|
+
break
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const tx = this.createWriteBatch()
|
|
586
|
+
const rx = this.core.state.storage.read()
|
|
587
|
+
const rootPromises = []
|
|
588
|
+
|
|
589
|
+
for (const root of flat.fullRoots(length * 2)) {
|
|
590
|
+
rootPromises.push(rx.getTreeNode(root))
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
rx.tryFlush()
|
|
594
|
+
|
|
595
|
+
const roots = await Promise.all(rootPromises)
|
|
596
|
+
const truncating = sharedLength < origLength
|
|
597
|
+
|
|
598
|
+
const fork = truncating ? this.fork + 1 : this.fork
|
|
599
|
+
|
|
600
|
+
// overwrite it atm, TODO: keep what we can connect to the tree
|
|
601
|
+
tx.deleteBlockRange(0, -1)
|
|
602
|
+
tx.deleteTreeNodeRange(0, -1)
|
|
603
|
+
tx.deleteBitfieldPageRange(0, -1)
|
|
604
|
+
|
|
605
|
+
const tree = {
|
|
606
|
+
fork,
|
|
607
|
+
length,
|
|
608
|
+
rootHash: crypto.tree(roots),
|
|
609
|
+
signature: null
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
tx.setHead(tree)
|
|
613
|
+
|
|
614
|
+
// prop a better way to do this
|
|
615
|
+
const dep = updateDependency(this, sharedLength, true)
|
|
616
|
+
dep.length = length
|
|
617
|
+
|
|
618
|
+
tx.setDependency(dep)
|
|
619
|
+
|
|
620
|
+
const flushed = await this.flush()
|
|
621
|
+
|
|
622
|
+
// and prop a better way to do this tooo
|
|
623
|
+
this.storage.updateDependencyLength(sharedLength, true)
|
|
624
|
+
this.storage.updateDependencyLength(length, false)
|
|
625
|
+
|
|
626
|
+
const bitfield = { start: sharedLength, length: tree.length - sharedLength, drop: false }
|
|
627
|
+
|
|
628
|
+
this.fork = tree.fork
|
|
629
|
+
this.roots = roots
|
|
630
|
+
this.length = tree.length
|
|
631
|
+
|
|
632
|
+
if (truncating) this.ontruncate(tree, sharedLength, origLength, flushed)
|
|
633
|
+
if (sharedLength < length) this.onappend(tree, bitfield, flushed)
|
|
634
|
+
} finally {
|
|
635
|
+
this.mutex.unlock()
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
581
639
|
async _overwrite (source, fork, length, treeLength, signature, isDependent, shallow) {
|
|
582
640
|
const blockPromises = []
|
|
583
641
|
const treePromises = []
|
|
@@ -675,9 +733,7 @@ module.exports = class SessionState {
|
|
|
675
733
|
|
|
676
734
|
if (upgraded) tx.setHead(tree)
|
|
677
735
|
|
|
678
|
-
const dependency = isDependent ? updateDependency(this, length) : null
|
|
679
|
-
|
|
680
|
-
if (dependency) this.dependencyLength = dependency.length
|
|
736
|
+
const dependency = isDependent ? updateDependency(this, length, true) : null
|
|
681
737
|
|
|
682
738
|
const flushed = await this.flush()
|
|
683
739
|
|
|
@@ -686,7 +742,7 @@ module.exports = class SessionState {
|
|
|
686
742
|
this.length = length
|
|
687
743
|
this.signature = signature
|
|
688
744
|
|
|
689
|
-
if (dependency) this.storage.updateDependencyLength(
|
|
745
|
+
if (dependency) this.storage.updateDependencyLength(dependency.length, true)
|
|
690
746
|
|
|
691
747
|
return { tree, flushed }
|
|
692
748
|
}
|
|
@@ -748,10 +804,7 @@ module.exports = class SessionState {
|
|
|
748
804
|
|
|
749
805
|
await state.flush(tx)
|
|
750
806
|
|
|
751
|
-
if (dependency)
|
|
752
|
-
state.storage.updateDependencyLength(dependency.length)
|
|
753
|
-
state.dependencyLength = dependency.length
|
|
754
|
-
}
|
|
807
|
+
if (dependency) state.storage.updateDependencyLength(dependency.length, false)
|
|
755
808
|
}
|
|
756
809
|
|
|
757
810
|
const bitfield = { start: treeLength, length: length - treeLength, drop: false }
|
|
@@ -799,10 +852,6 @@ module.exports = class SessionState {
|
|
|
799
852
|
this.core = core
|
|
800
853
|
this.index = this.core.sessionStates.push(this) - 1
|
|
801
854
|
|
|
802
|
-
// storage was updated
|
|
803
|
-
const deps = this.storage.dependencies
|
|
804
|
-
this.dependencyLength = deps[deps.length - 1].length
|
|
805
|
-
|
|
806
855
|
for (let i = this.sessions.length - 1; i >= 0; i--) this.sessions[i].transferSession(this.core)
|
|
807
856
|
}
|
|
808
857
|
|
|
@@ -812,9 +861,9 @@ module.exports = class SessionState {
|
|
|
812
861
|
await this.mutex.lock()
|
|
813
862
|
|
|
814
863
|
try {
|
|
815
|
-
if (state.storage && (await state.storage.resumeSession(this.name)) !== null) {
|
|
816
|
-
|
|
817
|
-
}
|
|
864
|
+
// if (state.storage && (await state.storage.resumeSession(this.name)) !== null) {
|
|
865
|
+
// throw STORAGE_CONFLICT('Batch has already been created')
|
|
866
|
+
// }
|
|
818
867
|
|
|
819
868
|
const treeLength = this.length
|
|
820
869
|
|
|
@@ -822,64 +871,60 @@ module.exports = class SessionState {
|
|
|
822
871
|
if (this.lingers === null) this.lingers = []
|
|
823
872
|
this.lingers.push(this.storage)
|
|
824
873
|
|
|
874
|
+
const resumed = await state.storage.resumeSession(this.name)
|
|
875
|
+
|
|
825
876
|
const truncation = length < this.length ? await truncateAndFlush(this, length) : null
|
|
826
|
-
const treeInfo = truncation ? truncation.tree : await state._getTreeHeadAt(this.length)
|
|
877
|
+
const treeInfo = truncation ? truncation.tree : resumed ? null : await state._getTreeHeadAt(this.length)
|
|
827
878
|
|
|
828
|
-
|
|
879
|
+
const fork = truncation ? this.fork + 1 : this.fork
|
|
829
880
|
|
|
830
881
|
// todo: validate treeInfo
|
|
831
882
|
|
|
832
|
-
if (
|
|
833
|
-
this.storage =
|
|
883
|
+
if (resumed) {
|
|
884
|
+
this.storage = resumed
|
|
834
885
|
} else {
|
|
835
|
-
|
|
836
|
-
this.storage = await
|
|
837
|
-
await s.close()
|
|
886
|
+
treeInfo.fork = fork
|
|
887
|
+
this.storage = await state.storage.createSession(this.name, treeInfo)
|
|
838
888
|
}
|
|
839
889
|
|
|
840
890
|
this.tree = new MerkleTree(this.storage)
|
|
841
891
|
|
|
842
892
|
this.prologue = state.prologue
|
|
843
|
-
this.fork =
|
|
893
|
+
this.fork = fork
|
|
844
894
|
this.length = length
|
|
845
895
|
this.roots = await this.tree.getRoots(length)
|
|
846
896
|
|
|
847
897
|
if (truncation) {
|
|
848
|
-
const { dependency, tree
|
|
898
|
+
const { dependency, tree } = truncation
|
|
849
899
|
|
|
850
|
-
if (dependency) this.storage.updateDependencyLength(
|
|
851
|
-
this.ontruncate(tree, tree.length, treeLength,
|
|
900
|
+
if (dependency) this.storage.updateDependencyLength(dependency.length, true)
|
|
901
|
+
this.ontruncate(tree, tree.length, treeLength, true)
|
|
852
902
|
}
|
|
853
903
|
}
|
|
854
904
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
if (state.name === this.name) state._moveToCore(core)
|
|
860
|
-
}
|
|
905
|
+
for (let i = this.core.sessionStates.length - 1; i >= 0; i--) {
|
|
906
|
+
const state = this.core.sessionStates[i]
|
|
907
|
+
if (state === this) continue
|
|
908
|
+
if (state.name === this.name) state._moveToCore(core.core)
|
|
861
909
|
}
|
|
862
910
|
|
|
863
|
-
this._moveToCore(core)
|
|
911
|
+
this._moveToCore(core.core)
|
|
864
912
|
} finally {
|
|
865
913
|
this.mutex.unlock()
|
|
866
914
|
}
|
|
867
915
|
}
|
|
868
916
|
|
|
869
|
-
async createSession (name,
|
|
917
|
+
async createSession (name, overwrite, atom) {
|
|
870
918
|
let storage = null
|
|
871
919
|
let treeInfo = null
|
|
872
920
|
|
|
873
921
|
if (!atom && !overwrite && this.storage) {
|
|
874
922
|
storage = await this.storage.resumeSession(name)
|
|
875
923
|
|
|
876
|
-
if (storage !== null)
|
|
877
|
-
treeInfo = (await getCoreHead(storage)) || getDefaultTree()
|
|
878
|
-
if (length !== -1 && treeInfo.length !== length) throw STORAGE_CONFLICT('Different batch stored here')
|
|
879
|
-
}
|
|
924
|
+
if (storage !== null) treeInfo = (await getCoreHead(storage)) || getDefaultTree()
|
|
880
925
|
}
|
|
881
926
|
|
|
882
|
-
|
|
927
|
+
const length = treeInfo ? treeInfo.length : this.length
|
|
883
928
|
|
|
884
929
|
if (storage === null) {
|
|
885
930
|
treeInfo = await this._getTreeHeadAt(length)
|
|
@@ -980,25 +1025,16 @@ async function truncateAndFlush (s, length) {
|
|
|
980
1025
|
}
|
|
981
1026
|
}
|
|
982
1027
|
|
|
983
|
-
function updateDependency (state, length) {
|
|
984
|
-
const
|
|
985
|
-
if (
|
|
1028
|
+
function updateDependency (state, length, truncated) {
|
|
1029
|
+
const i = state.storage.findDependencyIndex(length, truncated)
|
|
1030
|
+
if (i === -1) return null // skip default state and overlays of default
|
|
986
1031
|
|
|
987
1032
|
return {
|
|
988
|
-
dataPointer:
|
|
1033
|
+
dataPointer: state.storage.dependencies[i].dataPointer,
|
|
989
1034
|
length
|
|
990
1035
|
}
|
|
991
1036
|
}
|
|
992
1037
|
|
|
993
|
-
function findDependency (storage, length) {
|
|
994
|
-
for (let i = storage.dependencies.length - 1; i >= 0; i--) {
|
|
995
|
-
const dep = storage.dependencies[i]
|
|
996
|
-
if (dep.length < length) return dep
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
return null
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
1038
|
function getDefaultTree () {
|
|
1003
1039
|
return {
|
|
1004
1040
|
fork: 0,
|