braidfs 0.0.131 → 0.0.133
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 +135 -207
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var { diff_main } = require(`${__dirname}/diff.js`),
|
|
4
4
|
braid_text = require("braid-text"),
|
|
5
|
+
braid_blob = require("braid-blob"),
|
|
5
6
|
braid_fetch = require('braid-http').fetch
|
|
6
7
|
|
|
7
8
|
// Helper function to check if a file is binary based on its extension
|
|
@@ -27,6 +28,8 @@ var braidfs_config_dir = `${sync_base}/.braidfs`,
|
|
|
27
28
|
braidfs_config_file = `${braidfs_config_dir}/config`,
|
|
28
29
|
sync_base_meta = `${braidfs_config_dir}/proxy_base_meta`
|
|
29
30
|
braid_text.db_folder = `${braidfs_config_dir}/braid-text-db`
|
|
31
|
+
braid_blob.db_folder = { read: () => {}, write: () => {}, delete: () => {} }
|
|
32
|
+
braid_blob.meta_folder = `${braidfs_config_dir}/braid-blob-meta`
|
|
30
33
|
var trash = `${braidfs_config_dir}/trash`
|
|
31
34
|
var temp_folder = `${braidfs_config_dir}/temp`
|
|
32
35
|
|
|
@@ -417,6 +420,7 @@ async function sync_url(url) {
|
|
|
417
420
|
url = normalized_url
|
|
418
421
|
|
|
419
422
|
await braid_text.db_folder_init()
|
|
423
|
+
await braid_blob.init()
|
|
420
424
|
|
|
421
425
|
var is_external_link = url.match(/^https?:\/\//),
|
|
422
426
|
path = is_external_link ? url.replace(/^https?:\/\//, '') : url,
|
|
@@ -429,6 +433,8 @@ async function sync_url(url) {
|
|
|
429
433
|
if (!sync_url.cache) sync_url.cache = {}
|
|
430
434
|
if (!sync_url.chain) sync_url.chain = Promise.resolve()
|
|
431
435
|
if (!sync_url.cache[path]) {
|
|
436
|
+
// console.log(`sync_url: ${url}`)
|
|
437
|
+
|
|
432
438
|
var self = {url},
|
|
433
439
|
freed = false,
|
|
434
440
|
aborts = new Set()
|
|
@@ -449,7 +455,10 @@ async function sync_url(url) {
|
|
|
449
455
|
await self.disconnect?.()
|
|
450
456
|
await wait_promise
|
|
451
457
|
|
|
452
|
-
|
|
458
|
+
if (self.merge_type === 'dt')
|
|
459
|
+
await braid_text.delete(url)
|
|
460
|
+
else if (self.merge_type === 'aww')
|
|
461
|
+
await braid_blob.delete(url)
|
|
453
462
|
|
|
454
463
|
try {
|
|
455
464
|
console.log(`trying to delete: ${meta_path}`)
|
|
@@ -461,8 +470,17 @@ async function sync_url(url) {
|
|
|
461
470
|
await require('fs').promises.unlink(fp)
|
|
462
471
|
} catch (e) {}
|
|
463
472
|
}
|
|
464
|
-
|
|
473
|
+
|
|
474
|
+
sync_url.cache[path] = (async () => {
|
|
475
|
+
self.merge_type = await detect_merge_type()
|
|
476
|
+
if (self.merge_type === 'dt') {
|
|
477
|
+
return await (sync_url.chain = sync_url.chain.then(init))
|
|
478
|
+
} else if (self.merge_type === 'aww') {
|
|
479
|
+
return await (sync_url.chain = sync_url.chain.then(init_binary_sync))
|
|
480
|
+
} else throw new Error(`unknown merge-type: ${self.merge_type}`)
|
|
481
|
+
})()
|
|
465
482
|
}
|
|
483
|
+
return
|
|
466
484
|
|
|
467
485
|
async function detect_merge_type() {
|
|
468
486
|
// special case for .braidfs/config and .braidfs/error
|
|
@@ -500,19 +518,23 @@ async function sync_url(url) {
|
|
|
500
518
|
}
|
|
501
519
|
|
|
502
520
|
async function init_binary_sync() {
|
|
521
|
+
await ensure_path_stuff()
|
|
503
522
|
if (freed) return
|
|
504
523
|
|
|
505
524
|
console.log(`init_binary_sync: ${url}`)
|
|
506
525
|
|
|
507
526
|
async function save_meta() {
|
|
508
527
|
await require('fs').promises.writeFile(meta_path, JSON.stringify({
|
|
528
|
+
merge_type: self.merge_type,
|
|
509
529
|
peer: self.peer,
|
|
510
|
-
version: self.version,
|
|
511
530
|
file_mtimeNs_str: self.file_mtimeNs_str
|
|
512
531
|
}))
|
|
513
532
|
}
|
|
514
533
|
|
|
515
|
-
|
|
534
|
+
self.file_mtimeNs_str = null
|
|
535
|
+
self.file_read_only = null
|
|
536
|
+
|
|
537
|
+
await within_fiber(fullpath, async () => {
|
|
516
538
|
try {
|
|
517
539
|
Object.assign(self, JSON.parse(
|
|
518
540
|
await require('fs').promises.readFile(meta_path, 'utf8')))
|
|
@@ -520,227 +542,137 @@ async function sync_url(url) {
|
|
|
520
542
|
if (freed) return
|
|
521
543
|
|
|
522
544
|
if (!self.peer) self.peer = Math.random().toString(36).slice(2)
|
|
523
|
-
|
|
524
|
-
// create file if it doesn't exist
|
|
525
|
-
var fullpath = await get_fullpath()
|
|
526
|
-
if (freed) return
|
|
527
|
-
if (!(await file_exists(fullpath))) {
|
|
528
|
-
if (freed) return
|
|
529
|
-
await wait_on(require('fs').promises.writeFile(fullpath, ''))
|
|
530
|
-
if (freed) return
|
|
531
|
-
var stat = await require('fs').promises.stat(fullpath, { bigint: true })
|
|
532
|
-
if (freed) return
|
|
533
|
-
self.file_mtimeNs_str = '' + stat.mtimeNs
|
|
534
|
-
self.last_touch = Date.now()
|
|
535
|
-
}
|
|
536
|
-
if (freed) return
|
|
537
|
-
|
|
538
|
-
await save_meta()
|
|
539
545
|
})
|
|
540
546
|
if (freed) return
|
|
541
547
|
|
|
542
|
-
|
|
543
|
-
|
|
548
|
+
self.signal_file_needs_reading = async () => {
|
|
549
|
+
await within_fiber(fullpath, async () => {
|
|
550
|
+
try {
|
|
551
|
+
if (freed) return
|
|
544
552
|
|
|
545
|
-
|
|
553
|
+
var fullpath = await get_fullpath()
|
|
554
|
+
if (freed) return
|
|
546
555
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
if (freed) return
|
|
550
|
-
if (last_connect_timer) return
|
|
556
|
+
var stat = await require('fs').promises.stat(fullpath, { bigint: true })
|
|
557
|
+
if (freed) return
|
|
551
558
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
if (closed) return
|
|
556
|
-
closed = true
|
|
557
|
-
reconnect_rate_limiter.on_diss(url)
|
|
558
|
-
for (var a of aborts) a.abort()
|
|
559
|
-
aborts.clear()
|
|
560
|
-
}
|
|
561
|
-
self.reconnect = connect
|
|
562
|
-
|
|
563
|
-
await prev_disconnect?.()
|
|
564
|
-
if (freed || closed) return
|
|
565
|
-
|
|
566
|
-
await reconnect_rate_limiter.get_turn(url)
|
|
567
|
-
if (freed || closed) return
|
|
568
|
-
|
|
569
|
-
function retry(e) {
|
|
570
|
-
if (freed || closed) return
|
|
571
|
-
var p = self.disconnect()
|
|
572
|
-
|
|
573
|
-
var delay = waitTime * (config.retry_delay_ms ?? 1000)
|
|
574
|
-
console.log(`reconnecting in ${(delay / 1000).toFixed(2)}s: ${url} after error: ${e}`)
|
|
575
|
-
last_connect_timer = setTimeout(async () => {
|
|
576
|
-
await p
|
|
577
|
-
last_connect_timer = null
|
|
578
|
-
connect()
|
|
579
|
-
}, delay)
|
|
580
|
-
waitTime = Math.min(waitTime + 1, 3)
|
|
581
|
-
}
|
|
559
|
+
if (self.file_mtimeNs_str !== '' + stat.mtimeNs) {
|
|
560
|
+
var data = await require('fs').promises.readFile(fullpath, { encoding: 'utf8' })
|
|
561
|
+
if (freed) return
|
|
582
562
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
signal: a.signal,
|
|
592
|
-
method: "HEAD",
|
|
593
|
-
version: ['' + self.version]
|
|
594
|
-
})
|
|
595
|
-
if (r.ok) fork_point = ['' + self.version]
|
|
563
|
+
await braid_blob.put(url, data, { skip_write: true })
|
|
564
|
+
if (freed) return
|
|
565
|
+
|
|
566
|
+
self.file_mtimeNs_str = '' + stat.mtimeNs
|
|
567
|
+
await save_meta()
|
|
568
|
+
}
|
|
569
|
+
} catch (e) {
|
|
570
|
+
if (e.code !== 'ENOENT') throw e
|
|
596
571
|
}
|
|
572
|
+
})
|
|
573
|
+
}
|
|
574
|
+
await self.signal_file_needs_reading()
|
|
597
575
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
subscribe: true,
|
|
604
|
-
heartbeats: 120,
|
|
605
|
-
peer: self.peer,
|
|
606
|
-
parents: fork_point || []
|
|
607
|
-
})
|
|
608
|
-
if (freed || closed) return
|
|
609
|
-
|
|
610
|
-
if (res.status < 200 || res.status >= 300) return retry(new Error(`unexpected status: ${res.status}`))
|
|
611
|
-
|
|
612
|
-
if (res.status !== 209)
|
|
613
|
-
return log_error(`Can't sync ${url} -- got bad response ${res.status} from server (expected 209)`)
|
|
614
|
-
|
|
615
|
-
self.file_read_only = res.headers.get('editable') === 'false'
|
|
576
|
+
var db = {
|
|
577
|
+
read: async (_key) => {
|
|
578
|
+
return await within_fiber(fullpath, async () => {
|
|
579
|
+
var fullpath = await get_fullpath()
|
|
580
|
+
if (freed) return
|
|
616
581
|
|
|
617
|
-
|
|
582
|
+
try {
|
|
583
|
+
return await require('fs').promises.readFile(fullpath)
|
|
584
|
+
} catch (e) {
|
|
585
|
+
if (e.code === 'ENOENT') return null
|
|
586
|
+
throw e
|
|
587
|
+
}
|
|
588
|
+
})
|
|
589
|
+
},
|
|
590
|
+
write: async (_key, data) => {
|
|
591
|
+
return await within_fiber(fullpath, async () => {
|
|
618
592
|
var fullpath = await get_fullpath()
|
|
619
|
-
if (freed
|
|
593
|
+
if (freed) return
|
|
620
594
|
|
|
621
|
-
|
|
622
|
-
|
|
595
|
+
try {
|
|
596
|
+
var temp = `${temp_folder}/${Math.random().toString(36).slice(2)}`
|
|
597
|
+
await require('fs').promises.writeFile(temp, data)
|
|
598
|
+
if (freed) return
|
|
623
599
|
|
|
624
|
-
|
|
600
|
+
var stat = await require('fs').promises.stat(temp, { bigint: true })
|
|
601
|
+
if (freed) return
|
|
625
602
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
res.subscribe(async update => {
|
|
629
|
-
if (freed || closed) return
|
|
630
|
-
if (update.version.length === 0) return
|
|
631
|
-
if (update.version.length !== 1) throw 'unexpected'
|
|
632
|
-
var version = 1*update.version[0]
|
|
633
|
-
if (!update.body) return
|
|
634
|
-
|
|
635
|
-
if (self.version != null &&
|
|
636
|
-
version <= self.version) return
|
|
637
|
-
self.version = version
|
|
638
|
-
|
|
639
|
-
await within_fiber(url, async () => {
|
|
640
|
-
var fullpath = await get_fullpath()
|
|
641
|
-
if (freed || closed) return
|
|
642
|
-
|
|
643
|
-
await wait_on(set_read_only(fullpath, false))
|
|
644
|
-
if (freed || closed) return
|
|
645
|
-
await wait_on(require('fs').promises.writeFile(fullpath, update.body))
|
|
646
|
-
if (freed || closed) return
|
|
647
|
-
await wait_on(set_read_only(fullpath, self.file_read_only))
|
|
648
|
-
if (freed || closed) return
|
|
649
|
-
|
|
650
|
-
var stat = await require('fs').promises.stat(fullpath, { bigint: true })
|
|
651
|
-
if (freed || closed) return
|
|
652
|
-
self.file_mtimeNs_str = '' + stat.mtimeNs
|
|
653
|
-
self.last_touch = Date.now()
|
|
603
|
+
await require('fs').promises.rename(temp, fullpath)
|
|
604
|
+
if (freed) return
|
|
654
605
|
|
|
606
|
+
self.file_mtimeNs_str = '' + stat.mtimeNs
|
|
655
607
|
await save_meta()
|
|
656
|
-
})
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
var a = new AbortController()
|
|
665
|
-
aborts.add(a)
|
|
666
|
-
var r = await braid_fetch(url, {
|
|
667
|
-
method: 'PUT',
|
|
668
|
-
signal: a.signal,
|
|
669
|
-
version: ['' + self.version],
|
|
670
|
-
body,
|
|
671
|
-
headers: {
|
|
672
|
-
...(x => x && {Cookie: x})(config.cookies?.[new URL(url).hostname])
|
|
673
|
-
},
|
|
674
|
-
})
|
|
675
|
-
if (freed || closed) return
|
|
676
|
-
|
|
677
|
-
// if we're not authorized,
|
|
678
|
-
if (r.status == 401 || r.status == 403) {
|
|
679
|
-
// then revert it
|
|
680
|
-
console.log(`access denied: reverting local edits`)
|
|
681
|
-
unsync_url(url)
|
|
682
|
-
sync_url(url)
|
|
683
|
-
} else if (!r.ok) {
|
|
684
|
-
retry(new Error(`unexpected PUT status: ${r.status}`))
|
|
685
|
-
}
|
|
686
|
-
} catch (e) { retry(e) }
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// if what we have now is newer than what the server has,
|
|
690
|
-
// go ahead and send it
|
|
691
|
-
await within_fiber(url, async () => {
|
|
692
|
-
if (freed || closed) return
|
|
608
|
+
} catch (e) {
|
|
609
|
+
if (e.code === 'ENOENT') return null
|
|
610
|
+
throw e
|
|
611
|
+
}
|
|
612
|
+
})
|
|
613
|
+
},
|
|
614
|
+
delete: async (_key) => {
|
|
615
|
+
return await within_fiber(fullpath, async () => {
|
|
693
616
|
var fullpath = await get_fullpath()
|
|
694
|
-
if (freed
|
|
617
|
+
if (freed) return
|
|
618
|
+
|
|
695
619
|
try {
|
|
696
|
-
|
|
697
|
-
} catch (e) {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
var server_v = JSON.parse(`[${res.headers.get('current-version')}]`)
|
|
701
|
-
if (self.version != null &&
|
|
702
|
-
'' + stat.mtimeNs === self.file_mtimeNs_str && (
|
|
703
|
-
!server_v.length ||
|
|
704
|
-
1*server_v[0] < self.version
|
|
705
|
-
)) await send_file(fullpath)
|
|
620
|
+
await require('fs').promises.unlink(fullpath)
|
|
621
|
+
} catch (e) {
|
|
622
|
+
if (e.code !== 'ENOENT') throw e
|
|
623
|
+
}
|
|
706
624
|
})
|
|
625
|
+
}
|
|
626
|
+
}
|
|
707
627
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
var fullpath = await get_fullpath()
|
|
713
|
-
if (freed || closed) return
|
|
714
|
-
|
|
715
|
-
try {
|
|
716
|
-
var stat = await require('fs').promises.stat(fullpath, { bigint: true })
|
|
717
|
-
} catch (e) { return }
|
|
718
|
-
if (freed || closed) return
|
|
628
|
+
var ac
|
|
629
|
+
function start_sync() {
|
|
630
|
+
if (ac) ac.abort()
|
|
631
|
+
if (freed) return
|
|
719
632
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
Math.round(Number(stat.mtimeNs) / 1000000))
|
|
633
|
+
var closed = false
|
|
634
|
+
ac = new AbortController()
|
|
723
635
|
|
|
724
|
-
|
|
725
|
-
|
|
636
|
+
self.disconnect = async () => {
|
|
637
|
+
if (closed) return
|
|
638
|
+
closed = true
|
|
639
|
+
reconnect_rate_limiter.on_diss(url)
|
|
640
|
+
ac.abort()
|
|
641
|
+
}
|
|
726
642
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
643
|
+
braid_blob.sync(url, new URL(url), {
|
|
644
|
+
db,
|
|
645
|
+
signal: ac.signal,
|
|
646
|
+
peer: self.peer,
|
|
647
|
+
headers: {
|
|
648
|
+
'Content-Type': 'text/plain',
|
|
649
|
+
...(x => x && { Cookie: x })(config.cookies?.[new URL(url).hostname])
|
|
650
|
+
},
|
|
651
|
+
on_pre_connect: () => reconnect_rate_limiter.get_turn(url),
|
|
652
|
+
on_res: res => {
|
|
653
|
+
if (freed) return
|
|
654
|
+
reconnect_rate_limiter.on_conn(url)
|
|
655
|
+
self.file_read_only = res.headers.get('editable') === 'false'
|
|
656
|
+
console.log(`connected to ${url}${self.file_read_only ? ' (readonly)' : ''}`)
|
|
657
|
+
},
|
|
658
|
+
on_unauthorized: async () => {
|
|
659
|
+
console.log(`access denied: reverting local edits`)
|
|
660
|
+
unsync_url(url)
|
|
661
|
+
sync_url(url)
|
|
662
|
+
},
|
|
663
|
+
on_disconnect: () => reconnect_rate_limiter.on_diss(url)
|
|
664
|
+
})
|
|
734
665
|
}
|
|
735
666
|
|
|
667
|
+
self.reconnect = () => start_sync()
|
|
668
|
+
|
|
669
|
+
start_sync()
|
|
736
670
|
return self
|
|
737
671
|
}
|
|
738
|
-
|
|
739
|
-
async function init() {
|
|
740
|
-
if (freed) return
|
|
741
|
-
|
|
742
|
-
// console.log(`sync_url: ${url}`)
|
|
743
672
|
|
|
673
|
+
async function ensure_path_stuff() {
|
|
674
|
+
if (freed) return
|
|
675
|
+
|
|
744
676
|
// if we're accessing /blah/index, it will be normalized to /blah,
|
|
745
677
|
// but we still want to create a directory out of blah in this case
|
|
746
678
|
if (wasnt_normal && !(await is_dir(fullpath))) {
|
|
@@ -751,15 +683,11 @@ async function sync_url(url) {
|
|
|
751
683
|
|
|
752
684
|
await ensure_path(require("path").dirname(fullpath))
|
|
753
685
|
if (freed) return
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
// It notably does NOT handle `.braidfs/set_version/` and `.braidfs/get_version/` correctly!
|
|
760
|
-
// Search ` sync_url.cache[` to see how it's all handled.
|
|
761
|
-
return await init_binary_sync()
|
|
762
|
-
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
async function init() {
|
|
689
|
+
await ensure_path_stuff()
|
|
690
|
+
if (freed) return
|
|
763
691
|
|
|
764
692
|
self.peer = Math.random().toString(36).slice(2)
|
|
765
693
|
self.local_edit_counter = 0
|
|
@@ -903,6 +831,7 @@ async function sync_url(url) {
|
|
|
903
831
|
async function write_meta_file() {
|
|
904
832
|
if (freed) return
|
|
905
833
|
await wait_on(require('fs').promises.writeFile(meta_path, JSON.stringify({
|
|
834
|
+
merge_type: self.merge_type,
|
|
906
835
|
version: file_last_version,
|
|
907
836
|
digest: sha256(self.file_last_text),
|
|
908
837
|
peer: self.peer,
|
|
@@ -1123,7 +1052,6 @@ async function sync_url(url) {
|
|
|
1123
1052
|
start_sync()
|
|
1124
1053
|
return self
|
|
1125
1054
|
}
|
|
1126
|
-
return await sync_url.cache[url]
|
|
1127
1055
|
}
|
|
1128
1056
|
|
|
1129
1057
|
async function ensure_path(path) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "braidfs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.133",
|
|
4
4
|
"description": "braid technology synchronizing files and webpages",
|
|
5
5
|
"author": "Braid Working Group",
|
|
6
6
|
"repository": "braid-org/braidfs",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"braid-http": "~1.3.85",
|
|
10
10
|
"braid-text": "~0.2.97",
|
|
11
|
-
"braid-blob": "~0.0.
|
|
11
|
+
"braid-blob": "~0.0.42",
|
|
12
12
|
"chokidar": "^4.0.3"
|
|
13
13
|
},
|
|
14
14
|
"bin": {
|