braidfs 0.0.94 → 0.0.96

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 (2) hide show
  1. package/index.js +234 -224
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -378,7 +378,8 @@ async function sync_url(url) {
378
378
  if (!sync_url.cache) sync_url.cache = {}
379
379
  if (!sync_url.chain) sync_url.chain = Promise.resolve()
380
380
  if (!sync_url.cache[path]) {
381
- var freed = false,
381
+ var self = {url},
382
+ freed = false,
382
383
  aborts = new Set(),
383
384
  braid_text_get_options = null
384
385
  var wait_promise = Promise.resolve()
@@ -398,9 +399,8 @@ async function sync_url(url) {
398
399
  if (!unsync_url.cache) unsync_url.cache = {}
399
400
  unsync_url.cache[path] = async () => {
400
401
  freed = true
401
- for (let a of aborts) a.abort()
402
+ await self.disconnect?.()
402
403
  await wait_promise
403
- if (braid_text_get_options) await braid_text.forget(url, braid_text_get_options)
404
404
 
405
405
  delete braid_text.cache[url]
406
406
  for (let f of await braid_text.get_files_for_key(url)) {
@@ -416,8 +416,6 @@ async function sync_url(url) {
416
416
  async function init() {
417
417
  if (freed) return
418
418
 
419
- var self = {url}
420
-
421
419
  console.log(`sync_url: ${url}`)
422
420
 
423
421
  var resource = await braid_text.get_resource(url)
@@ -489,55 +487,6 @@ async function sync_url(url) {
489
487
  file_loop_pump()
490
488
  }
491
489
 
492
- async function my_fetch(params) {
493
- if (freed) return
494
- try {
495
- var a = new AbortController()
496
- aborts.add(a)
497
- return await braid_fetch(url, {
498
- signal: a.signal,
499
- headers: {
500
- "Merge-Type": "dt",
501
- "Content-Type": 'text/plain',
502
- ...(x => x && {Cookie: x})(config.cookies?.[new URL(url).hostname])
503
- },
504
- retry: { retryRes: r => r.status !== 401 && r.status !== 403 },
505
- ...params
506
- })
507
- } catch (e) {
508
- if (freed) return
509
- if (e?.name !== "AbortError") console.log(e)
510
- } finally {
511
- if (freed) return
512
- aborts.delete(a)
513
- }
514
- }
515
-
516
- async function send_out(stuff) {
517
- if (!is_external_link) return
518
- if (freed) return
519
-
520
- console.log(`send_out ${url} ${JSON.stringify(stuff, null, 4).slice(0, 1000)}`)
521
-
522
- var r = await my_fetch({ method: "PUT", ...stuff })
523
- if (freed) return
524
-
525
- // the server has acknowledged this version,
526
- // so add it to the fork point
527
- if (r.ok) self.update_fork_point(stuff.version[0], stuff.parents)
528
-
529
- // if we're not authorized,
530
- if (r.status == 401 || r.status == 403) {
531
- // and it's one of our versions (a local edit),
532
- if (self.peer === braid_text.decode_version(stuff.version[0])[0]) {
533
- // then revert it
534
- console.log(`access denied: reverting local edits`)
535
- unsync_url(url)
536
- sync_url(url)
537
- }
538
- }
539
- }
540
-
541
490
  await within_fiber(fullpath, async () => {
542
491
  if (freed) return
543
492
  var fullpath = await get_fullpath()
@@ -776,193 +725,254 @@ async function sync_url(url) {
776
725
  return frontier.sort()
777
726
  }
778
727
 
779
- async function find_fork_point() {
780
- if (freed) return
781
- console.log(`[find_fork_point] url: ${url}`)
728
+ var waitTime = 1
729
+ var last_connect_timer = null
782
730
 
783
- // see if remote has the fork point
784
- if (self.fork_point) {
785
- var r = await my_fetch({ method: "HEAD", version: self.fork_point })
786
- if (freed) return
787
- if (r.ok) {
788
- console.log(`[find_fork_point] it has our latest fork point, hooray!`)
789
- return self.fork_point
790
- }
731
+ if (is_external_link) connect()
732
+ async function connect() {
733
+ if (freed) return
734
+ if (last_connect_timer) return
735
+
736
+ var closed = false
737
+ var prev_disconnect = self.disconnect
738
+ self.disconnect = async () => {
739
+ if (closed) return
740
+ closed = true
741
+ for (var a of aborts) a.abort()
742
+ aborts.clear()
743
+ if (braid_text_get_options) await braid_text.forget(url, braid_text_get_options)
744
+ braid_text_get_options = null
791
745
  }
792
-
793
- // otherwise let's binary search for new fork point..
794
- var bytes = resource.doc.toBytes()
795
- var [_, events, __] = braid_text.dt_parse([...bytes])
796
- events = events.map(x => x.join('-'))
797
-
798
- var min = -1
799
- var max = events.length
800
- self.fork_point = []
801
- while (min + 1 < max) {
802
- var i = Math.floor((min + max)/2)
803
- var version = [events[i]]
804
-
805
- console.log(`min=${min}, max=${max}, i=${i}, version=${version}`)
806
-
807
- var st = Date.now()
808
- var r = await my_fetch({ method: "HEAD", version })
809
- if (freed) return
810
- console.log(`fetched in ${Date.now() - st}`)
811
-
812
- if (r.ok) {
813
- min = i
814
- self.fork_point = version
815
- } else max = i
746
+ self.reconnect = connect
747
+
748
+ await prev_disconnect?.()
749
+ if (freed || closed) return
750
+
751
+ async function retry(e) {
752
+ if (freed || closed) return
753
+ var p = self.disconnect()
754
+
755
+ console.log(`reconnecting in ${waitTime}s: ${url} after error: ${e}`)
756
+ last_connect_timer = setTimeout(async () => {
757
+ await p
758
+ last_connect_timer = null
759
+ connect()
760
+ }, waitTime * 1000)
761
+ waitTime = Math.min(waitTime + 1, 3)
816
762
  }
817
- console.log(`[find_fork_point] settled on: ${JSON.stringify(self.fork_point)}`)
818
- self.signal_file_needs_writing(true)
819
- return self.fork_point
820
- }
821
-
822
- var initial_connect_done
823
- var initial_connect_promise = new Promise(done => initial_connect_done = done)
824
763
 
825
- if (is_external_link) find_fork_point().then(async fork_point => {
826
- if (freed) return
827
- await send_new_stuff(fork_point)
828
- if (freed) return
829
- connect(fork_point)
830
- })
831
-
832
- function connect(fork_point) {
833
- if (freed) return
834
- let a = new AbortController()
835
- aborts.add(a)
836
- self.reconnect = () => {
837
- if (freed) return
838
- console.log(`reconnecting ${url}`)
764
+ var initial_connect_done
765
+ var initial_connect_promise = new Promise(done => initial_connect_done = done)
839
766
 
840
- aborts.delete(a)
841
- a.abort()
842
- connect(fork_point)
767
+ async function my_fetch(params) {
768
+ if (freed || closed) return
769
+ try {
770
+ var a = new AbortController()
771
+ aborts.add(a)
772
+ return await braid_fetch(url, {
773
+ signal: a.signal,
774
+ headers: {
775
+ "Merge-Type": "dt",
776
+ "Content-Type": 'text/plain',
777
+ ...(x => x && {Cookie: x})(config.cookies?.[new URL(url).hostname])
778
+ },
779
+ retry: { retryRes: r => r.status !== 401 && r.status !== 403 },
780
+ ...params
781
+ })
782
+ } catch (e) {
783
+ if (freed || closed) return
784
+ if (e?.name !== "AbortError") console.log(e)
785
+ } finally {
786
+ if (freed || closed) return
787
+ aborts.delete(a)
788
+ }
843
789
  }
844
-
845
- console.log(`connecting to ${url}`)
846
- braid_fetch(url, {
847
- signal: a.signal,
848
- headers: {
849
- "Merge-Type": "dt",
850
- Accept: 'text/plain',
851
- ...(x => x && {Cookie: x})(config.cookies?.[new URL(url).hostname]),
852
- },
853
- subscribe: true,
854
- retry: {
855
- retryRes: () => true,
856
- onRes: (res) => {
857
- if (res.status !== 209)
858
- return log_error(`Can't sync ${url} -- got bad response ${res.status} from server (expected 209)`)
859
-
860
- console.log(`connected to ${url}`)
861
- console.log(` editable = ${res.headers.get('editable')}`)
862
-
863
- self.file_read_only = res.headers.get('editable') === 'false'
864
- self.signal_file_needs_writing()
790
+
791
+ async function send_out(stuff) {
792
+ if (freed || closed) return
793
+
794
+ console.log(`send_out ${url} ${JSON.stringify(stuff, null, 4).slice(0, 1000)}`)
795
+
796
+ var r = await my_fetch({ method: "PUT", ...stuff })
797
+ if (freed || closed) return
798
+
799
+ // the server has acknowledged this version,
800
+ // so add it to the fork point
801
+ if (r.ok) self.update_fork_point(stuff.version[0], stuff.parents)
802
+
803
+ // if we're not authorized,
804
+ if (r.status == 401 || r.status == 403) {
805
+ // and it's one of our versions (a local edit),
806
+ if (self.peer === braid_text.decode_version(stuff.version[0])[0]) {
807
+ // then revert it
808
+ console.log(`access denied: reverting local edits`)
809
+ unsync_url(url)
810
+ sync_url(url)
865
811
  }
866
- },
867
- heartbeats: 120,
868
- parents: async () => {
869
- if (freed) return
870
- var x = fork_point || await find_fork_point()
871
- if (freed) return
872
- fork_point = null
873
- return x
874
- },
875
- peer: self.peer
876
- }).then(x => {
877
- if (freed) return
878
- if (x.status !== 209) throw new Error(`unexpected status: ${x.status}`)
879
- initial_connect_done()
880
- x.subscribe(async update => {
881
- if (freed) return
882
- console.log(`got external update about ${url}`)
812
+ }
813
+ }
883
814
 
884
- if (update.body) update.body = update.body_text
885
- if (update.patches) for (let p of update.patches) p.content = p.content_text
815
+ async function find_fork_point() {
816
+ if (freed || closed) return
817
+ console.log(`[find_fork_point] url: ${url}`)
886
818
 
887
- // console.log(`update: ${JSON.stringify(update, null, 4)}`)
888
- if (update.version.length === 0) return
889
- if (update.version.length !== 1) throw 'unexpected'
819
+ // see if remote has the fork point
820
+ if (self.fork_point) {
821
+ var r = await my_fetch({ method: "HEAD", version: self.fork_point })
822
+ if (freed || closed) return
823
+ if (r.ok) return console.log(`[find_fork_point] it has our latest fork point, hooray!`)
824
+ }
890
825
 
891
- await wait_on(braid_text.put(url, { ...update, peer: self.peer, merge_type: 'dt' }))
892
- if (freed) return
826
+ // otherwise let's binary search for new fork point..
827
+ var bytes = resource.doc.toBytes()
828
+ var [_, events, __] = braid_text.dt_parse([...bytes])
829
+ events = events.map(x => x.join('-'))
830
+
831
+ var min = -1
832
+ var max = events.length
833
+ self.fork_point = []
834
+ while (min + 1 < max) {
835
+ var i = Math.floor((min + max)/2)
836
+ var version = [events[i]]
837
+
838
+ console.log(`min=${min}, max=${max}, i=${i}, version=${version}`)
839
+
840
+ var st = Date.now()
841
+ var r = await my_fetch({ method: "HEAD", version })
842
+ if (freed || closed) return
843
+ console.log(`fetched in ${Date.now() - st}`)
844
+
845
+ if (r.ok) {
846
+ min = i
847
+ self.fork_point = version
848
+ } else max = i
849
+ }
850
+ console.log(`[find_fork_point] settled on: ${JSON.stringify(self.fork_point)}`)
851
+ self.signal_file_needs_writing(true)
852
+ }
893
853
 
894
- // the server is giving us this version,
895
- // so it must have it,
896
- // so let's add it to our fork point
897
- self.update_fork_point(update.version[0], update.parents)
854
+ await find_fork_point()
855
+ if (freed || closed) return
898
856
 
899
- self.signal_file_needs_writing()
900
- }, e => (e?.name !== "AbortError") && console.log(e))
901
- }).catch(e => (e?.name !== "AbortError") && console.log(e))
902
- }
857
+ await send_new_stuff()
858
+ if (freed || closed) return
903
859
 
904
- // send it stuff we have but it doesn't't
905
- async function send_new_stuff(fork_point) {
906
- if (freed) return
907
- var q = []
908
- var in_flight = new Map()
909
- var max_in_flight = 10
910
- var send_pump_lock = 0
911
-
912
- async function send_pump() {
913
- send_pump_lock++
914
- if (send_pump_lock > 1) return
915
- try {
916
- if (freed) return
917
- if (in_flight.size >= max_in_flight) return
918
- if (!q.length) {
919
- var frontier = self.fork_point
920
- for (var u of in_flight.values())
921
- frontier = extend_frontier(frontier, u.version[0], u.parents)
922
-
923
- var options = {
924
- parents: frontier,
925
- merge_type: 'dt',
926
- peer: self.peer,
927
- subscribe: u => u.version.length && q.push(u)
860
+ console.log(`connecting to ${url}`)
861
+ let a = new AbortController()
862
+ aborts.add(a)
863
+ try {
864
+ var res = await braid_fetch(url, {
865
+ signal: a.signal,
866
+ headers: {
867
+ "Merge-Type": "dt",
868
+ Accept: 'text/plain',
869
+ ...(x => x && {Cookie: x})(config.cookies?.[new URL(url).hostname]),
870
+ },
871
+ subscribe: true,
872
+ heartbeats: 120,
873
+ parents: self.fork_point,
874
+ peer: self.peer
875
+ })
876
+ } catch (e) { return retry(e) }
877
+ if (freed || closed) return
878
+
879
+ if (res.status < 200 || res.status >= 300) return retry(new Error(`unexpected status: ${res.status}`))
880
+
881
+ if (res.status !== 209)
882
+ return log_error(`Can't sync ${url} -- got bad response ${res.status} from server (expected 209)`)
883
+
884
+ console.log(`connected to ${url}`)
885
+ console.log(` editable = ${res.headers.get('editable')}`)
886
+
887
+ self.file_read_only = res.headers.get('editable') === 'false'
888
+ self.signal_file_needs_writing()
889
+
890
+ initial_connect_done()
891
+ res.subscribe(async update => {
892
+ if (freed || closed) return
893
+ console.log(`got external update about ${url}`)
894
+
895
+ if (update.body) update.body = update.body_text
896
+ if (update.patches) for (let p of update.patches) p.content = p.content_text
897
+
898
+ // console.log(`update: ${JSON.stringify(update, null, 4)}`)
899
+ if (update.version.length === 0) return
900
+ if (update.version.length !== 1) throw 'unexpected'
901
+
902
+ await wait_on(braid_text.put(url, { ...update, peer: self.peer, merge_type: 'dt' }))
903
+ if (freed || closed) return
904
+
905
+ // the server is giving us this version,
906
+ // so it must have it,
907
+ // so let's add it to our fork point
908
+ self.update_fork_point(update.version[0], update.parents)
909
+
910
+ self.signal_file_needs_writing()
911
+ }, retry)
912
+
913
+ // send it stuff we have but it doesn't't
914
+ async function send_new_stuff() {
915
+ if (freed || closed) return
916
+ var q = []
917
+ var in_flight = new Map()
918
+ var max_in_flight = 10
919
+ var send_pump_lock = 0
920
+
921
+ async function send_pump() {
922
+ send_pump_lock++
923
+ if (send_pump_lock > 1) return
924
+ try {
925
+ if (freed || closed) return
926
+ if (in_flight.size >= max_in_flight) return
927
+ if (!q.length) {
928
+ var frontier = self.fork_point
929
+ for (var u of in_flight.values())
930
+ frontier = extend_frontier(frontier, u.version[0], u.parents)
931
+
932
+ var options = {
933
+ parents: frontier,
934
+ merge_type: 'dt',
935
+ peer: self.peer,
936
+ subscribe: u => u.version.length && q.push(u)
937
+ }
938
+ await braid_text.get(url, options)
939
+ await braid_text.forget(url, options)
928
940
  }
929
- await braid_text.get(url, options)
930
- await braid_text.forget(url, options)
931
- }
932
- while (q.length && in_flight.size < max_in_flight) {
933
- let u = q.shift()
934
- in_flight.set(u.version[0], u);
935
- (async () => {
936
- await initial_connect_promise
937
- if (freed) return
938
- await send_out({...u, peer: self.peer})
939
- if (freed) return
940
- in_flight.delete(u.version[0])
941
- setTimeout(send_pump, 0)
942
- })()
941
+ while (q.length && in_flight.size < max_in_flight) {
942
+ let u = q.shift()
943
+ in_flight.set(u.version[0], u);
944
+ (async () => {
945
+ await initial_connect_promise
946
+ if (freed || closed) return
947
+ await send_out({...u, peer: self.peer})
948
+ if (freed || closed) return
949
+ in_flight.delete(u.version[0])
950
+ setTimeout(send_pump, 0)
951
+ })()
952
+ }
953
+ } finally {
954
+ var retry = send_pump_lock > 1
955
+ send_pump_lock = 0
956
+ if (retry) setTimeout(send_pump, 0)
943
957
  }
944
- } finally {
945
- var retry = send_pump_lock > 1
946
- send_pump_lock = 0
947
- if (retry) setTimeout(send_pump, 0)
948
958
  }
949
- }
950
959
 
951
- var initial_stuff = true
952
- await wait_on(braid_text.get(url, braid_text_get_options = {
953
- parents: fork_point,
954
- merge_type: 'dt',
955
- peer: self.peer,
956
- subscribe: async (u) => {
957
- if (freed) return
958
- if (u.version.length) {
959
- self.signal_file_needs_writing()
960
- if (initial_stuff || in_flight.size < max_in_flight) q.push(u)
961
- send_pump()
962
- }
963
- },
964
- }))
965
- initial_stuff = false
960
+ var initial_stuff = true
961
+ await wait_on(braid_text.get(url, braid_text_get_options = {
962
+ parents: self.fork_point,
963
+ merge_type: 'dt',
964
+ peer: self.peer,
965
+ subscribe: async (u) => {
966
+ if (freed || closed) return
967
+ if (u.version.length) {
968
+ self.signal_file_needs_writing()
969
+ if (initial_stuff || in_flight.size < max_in_flight) q.push(u)
970
+ send_pump()
971
+ }
972
+ },
973
+ }))
974
+ initial_stuff = false
975
+ }
966
976
  }
967
977
 
968
978
  // for config and errors file, listen for web changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braidfs",
3
- "version": "0.0.94",
3
+ "version": "0.0.96",
4
4
  "description": "braid technology synchronizing files and webpages",
5
5
  "author": "Braid Working Group",
6
6
  "repository": "braid-org/braidfs",