basalt-sync 0.2.0 → 0.2.1

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/README.md CHANGED
@@ -25,10 +25,19 @@ bun install
25
25
  bun run test # everything, including against a real server
26
26
  bun run typecheck
27
27
  bun run format # prettier, and CI fails on anything it would change
28
+ bun run stress # the hostile suite: kills, collisions, awkward names
28
29
  bun run bench # throughput and bandwidth, reported not asserted
29
30
  bun run bench:sync # a whole vault, timed and checked
30
31
  ```
31
32
 
33
+ `src/stress/*.stress.ts` is excluded from `bun run test` and has its own config.
34
+ It is not a slower copy of the unit tests: it kills the server while it is
35
+ committing, lets two devices edit the same twenty notes without either seeing
36
+ the other, and writes the filenames people actually have, emoji and right to
37
+ left and combining marks included. Every defect this project has found in
38
+ itself was silent and only appeared when the system ran, which is why this
39
+ exists and why CI runs it as its own job.
40
+
32
41
  The benchmarks run under bun and the shipped CLI runs under node, which is not a
33
42
  detail: two of the largest wins in `docs/benchmark.md` were each invisible under
34
43
  one of the two engines. Anything measuring the chunker should be run under both.
package/dist/basalt.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- var ar=Object.create;var Bt=Object.defineProperty;var lr=Object.getOwnPropertyDescriptor;var hr=Object.getOwnPropertyNames;var cr=Object.getPrototypeOf,ur=Object.prototype.hasOwnProperty;var fr=(t,e)=>()=>{try{return e||t((e={exports:{}}).exports,e),e.exports}catch(n){throw e=0,n}};var dr=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of hr(e))!ur.call(t,i)&&i!==n&&Bt(t,i,{get:()=>e[i],enumerable:!(r=lr(e,i))||r.enumerable});return t};var gr=(t,e,n)=>(n=t!=null?ar(cr(t)):{},dr(e||!t||!t.__esModule?Bt(n,"default",{value:t,enumerable:!0}):n,t));var vn=fr((ns,we)=>{var d=function(){this.Diff_Timeout=1,this.Diff_EditCost=4,this.Match_Threshold=.5,this.Match_Distance=1e3,this.Patch_DeleteThreshold=.5,this.Patch_Margin=4,this.Match_MaxBits=32},T=-1,_=1,k=0;d.Diff=function(t,e){return[t,e]};d.prototype.diff_main=function(t,e,n,r){typeof r>"u"&&(this.Diff_Timeout<=0?r=Number.MAX_VALUE:r=new Date().getTime()+this.Diff_Timeout*1e3);var i=r;if(t==null||e==null)throw new Error("Null input. (diff_main)");if(t==e)return t?[new d.Diff(k,t)]:[];typeof n>"u"&&(n=!0);var s=n,o=this.diff_commonPrefix(t,e),a=t.substring(0,o);t=t.substring(o),e=e.substring(o),o=this.diff_commonSuffix(t,e);var l=t.substring(t.length-o);t=t.substring(0,t.length-o),e=e.substring(0,e.length-o);var h=this.diff_compute_(t,e,s,i);return a&&h.unshift(new d.Diff(k,a)),l&&h.push(new d.Diff(k,l)),this.diff_cleanupMerge(h),h};d.prototype.diff_compute_=function(t,e,n,r){var i;if(!t)return[new d.Diff(_,e)];if(!e)return[new d.Diff(T,t)];var s=t.length>e.length?t:e,o=t.length>e.length?e:t,a=s.indexOf(o);if(a!=-1)return i=[new d.Diff(_,s.substring(0,a)),new d.Diff(k,o),new d.Diff(_,s.substring(a+o.length))],t.length>e.length&&(i[0][0]=i[2][0]=T),i;if(o.length==1)return[new d.Diff(T,t),new d.Diff(_,e)];var l=this.diff_halfMatch_(t,e);if(l){var h=l[0],c=l[1],u=l[2],f=l[3],g=l[4],m=this.diff_main(h,u,n,r),p=this.diff_main(c,f,n,r);return m.concat([new d.Diff(k,g)],p)}return n&&t.length>100&&e.length>100?this.diff_lineMode_(t,e,r):this.diff_bisect_(t,e,r)};d.prototype.diff_lineMode_=function(t,e,n){var r=this.diff_linesToChars_(t,e);t=r.chars1,e=r.chars2;var i=r.lineArray,s=this.diff_main(t,e,!1,n);this.diff_charsToLines_(s,i),this.diff_cleanupSemantic(s),s.push(new d.Diff(k,""));for(var o=0,a=0,l=0,h="",c="";o<s.length;){switch(s[o][0]){case _:l++,c+=s[o][1];break;case T:a++,h+=s[o][1];break;case k:if(a>=1&&l>=1){s.splice(o-a-l,a+l),o=o-a-l;for(var u=this.diff_main(h,c,!1,n),f=u.length-1;f>=0;f--)s.splice(o,0,u[f]);o=o+u.length}l=0,a=0,h="",c="";break}o++}return s.pop(),s};d.prototype.diff_bisect_=function(t,e,n){for(var r=t.length,i=e.length,s=Math.ceil((r+i)/2),o=s,a=2*s,l=new Array(a),h=new Array(a),c=0;c<a;c++)l[c]=-1,h[c]=-1;l[o+1]=0,h[o+1]=0;for(var u=r-i,f=u%2!=0,g=0,m=0,p=0,x=0,w=0;w<s&&!(new Date().getTime()>n);w++){for(var A=-w+g;A<=w-m;A+=2){var b=o+A,S;A==-w||A!=w&&l[b-1]<l[b+1]?S=l[b+1]:S=l[b-1]+1;for(var M=S-A;S<r&&M<i&&t.charAt(S)==e.charAt(M);)S++,M++;if(l[b]=S,S>r)m+=2;else if(M>i)g+=2;else if(f){var $=o+u-A;if($>=0&&$<a&&h[$]!=-1){var E=r-h[$];if(S>=E)return this.diff_bisectSplit_(t,e,S,M,n)}}}for(var v=-w+p;v<=w-x;v+=2){var $=o+v,E;v==-w||v!=w&&h[$-1]<h[$+1]?E=h[$+1]:E=h[$-1]+1;for(var y=E-v;E<r&&y<i&&t.charAt(r-E-1)==e.charAt(i-y-1);)E++,y++;if(h[$]=E,E>r)x+=2;else if(y>i)p+=2;else if(!f){var b=o+u-v;if(b>=0&&b<a&&l[b]!=-1){var S=l[b],M=o+S-b;if(E=r-E,S>=E)return this.diff_bisectSplit_(t,e,S,M,n)}}}}return[new d.Diff(T,t),new d.Diff(_,e)]};d.prototype.diff_bisectSplit_=function(t,e,n,r,i){var s=t.substring(0,n),o=e.substring(0,r),a=t.substring(n),l=e.substring(r),h=this.diff_main(s,o,!1,i),c=this.diff_main(a,l,!1,i);return h.concat(c)};d.prototype.diff_linesToChars_=function(t,e){var n=[],r={};n[0]="";function i(l){for(var h="",c=0,u=-1,f=n.length;u<l.length-1;){u=l.indexOf(`
2
+ var lr=Object.create;var Wt=Object.defineProperty;var hr=Object.getOwnPropertyDescriptor;var cr=Object.getOwnPropertyNames;var ur=Object.getPrototypeOf,fr=Object.prototype.hasOwnProperty;var dr=(t,e)=>()=>{try{return e||t((e={exports:{}}).exports,e),e.exports}catch(n){throw e=0,n}};var gr=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of cr(e))!fr.call(t,i)&&i!==n&&Wt(t,i,{get:()=>e[i],enumerable:!(r=hr(e,i))||r.enumerable});return t};var mr=(t,e,n)=>(n=t!=null?lr(ur(t)):{},gr(e||!t||!t.__esModule?Wt(n,"default",{value:t,enumerable:!0}):n,t));var vn=dr((rs,we)=>{var d=function(){this.Diff_Timeout=1,this.Diff_EditCost=4,this.Match_Threshold=.5,this.Match_Distance=1e3,this.Patch_DeleteThreshold=.5,this.Patch_Margin=4,this.Match_MaxBits=32},T=-1,_=1,k=0;d.Diff=function(t,e){return[t,e]};d.prototype.diff_main=function(t,e,n,r){typeof r>"u"&&(this.Diff_Timeout<=0?r=Number.MAX_VALUE:r=new Date().getTime()+this.Diff_Timeout*1e3);var i=r;if(t==null||e==null)throw new Error("Null input. (diff_main)");if(t==e)return t?[new d.Diff(k,t)]:[];typeof n>"u"&&(n=!0);var s=n,o=this.diff_commonPrefix(t,e),a=t.substring(0,o);t=t.substring(o),e=e.substring(o),o=this.diff_commonSuffix(t,e);var l=t.substring(t.length-o);t=t.substring(0,t.length-o),e=e.substring(0,e.length-o);var h=this.diff_compute_(t,e,s,i);return a&&h.unshift(new d.Diff(k,a)),l&&h.push(new d.Diff(k,l)),this.diff_cleanupMerge(h),h};d.prototype.diff_compute_=function(t,e,n,r){var i;if(!t)return[new d.Diff(_,e)];if(!e)return[new d.Diff(T,t)];var s=t.length>e.length?t:e,o=t.length>e.length?e:t,a=s.indexOf(o);if(a!=-1)return i=[new d.Diff(_,s.substring(0,a)),new d.Diff(k,o),new d.Diff(_,s.substring(a+o.length))],t.length>e.length&&(i[0][0]=i[2][0]=T),i;if(o.length==1)return[new d.Diff(T,t),new d.Diff(_,e)];var l=this.diff_halfMatch_(t,e);if(l){var h=l[0],c=l[1],u=l[2],f=l[3],g=l[4],m=this.diff_main(h,u,n,r),p=this.diff_main(c,f,n,r);return m.concat([new d.Diff(k,g)],p)}return n&&t.length>100&&e.length>100?this.diff_lineMode_(t,e,r):this.diff_bisect_(t,e,r)};d.prototype.diff_lineMode_=function(t,e,n){var r=this.diff_linesToChars_(t,e);t=r.chars1,e=r.chars2;var i=r.lineArray,s=this.diff_main(t,e,!1,n);this.diff_charsToLines_(s,i),this.diff_cleanupSemantic(s),s.push(new d.Diff(k,""));for(var o=0,a=0,l=0,h="",c="";o<s.length;){switch(s[o][0]){case _:l++,c+=s[o][1];break;case T:a++,h+=s[o][1];break;case k:if(a>=1&&l>=1){s.splice(o-a-l,a+l),o=o-a-l;for(var u=this.diff_main(h,c,!1,n),f=u.length-1;f>=0;f--)s.splice(o,0,u[f]);o=o+u.length}l=0,a=0,h="",c="";break}o++}return s.pop(),s};d.prototype.diff_bisect_=function(t,e,n){for(var r=t.length,i=e.length,s=Math.ceil((r+i)/2),o=s,a=2*s,l=new Array(a),h=new Array(a),c=0;c<a;c++)l[c]=-1,h[c]=-1;l[o+1]=0,h[o+1]=0;for(var u=r-i,f=u%2!=0,g=0,m=0,p=0,x=0,w=0;w<s&&!(new Date().getTime()>n);w++){for(var A=-w+g;A<=w-m;A+=2){var b=o+A,S;A==-w||A!=w&&l[b-1]<l[b+1]?S=l[b+1]:S=l[b-1]+1;for(var M=S-A;S<r&&M<i&&t.charAt(S)==e.charAt(M);)S++,M++;if(l[b]=S,S>r)m+=2;else if(M>i)g+=2;else if(f){var $=o+u-A;if($>=0&&$<a&&h[$]!=-1){var E=r-h[$];if(S>=E)return this.diff_bisectSplit_(t,e,S,M,n)}}}for(var v=-w+p;v<=w-x;v+=2){var $=o+v,E;v==-w||v!=w&&h[$-1]<h[$+1]?E=h[$+1]:E=h[$-1]+1;for(var y=E-v;E<r&&y<i&&t.charAt(r-E-1)==e.charAt(i-y-1);)E++,y++;if(h[$]=E,E>r)x+=2;else if(y>i)p+=2;else if(!f){var b=o+u-v;if(b>=0&&b<a&&l[b]!=-1){var S=l[b],M=o+S-b;if(E=r-E,S>=E)return this.diff_bisectSplit_(t,e,S,M,n)}}}}return[new d.Diff(T,t),new d.Diff(_,e)]};d.prototype.diff_bisectSplit_=function(t,e,n,r,i){var s=t.substring(0,n),o=e.substring(0,r),a=t.substring(n),l=e.substring(r),h=this.diff_main(s,o,!1,i),c=this.diff_main(a,l,!1,i);return h.concat(c)};d.prototype.diff_linesToChars_=function(t,e){var n=[],r={};n[0]="";function i(l){for(var h="",c=0,u=-1,f=n.length;u<l.length-1;){u=l.indexOf(`
3
3
  `,c),u==-1&&(u=l.length-1);var g=l.substring(c,u+1);(r.hasOwnProperty?r.hasOwnProperty(g):r[g]!==void 0)?h+=String.fromCharCode(r[g]):(f==s&&(g=l.substring(c),u=l.length),h+=String.fromCharCode(f),r[g]=f,n[f++]=g),c=u+1}return h}var s=4e4,o=i(t);s=65535;var a=i(e);return{chars1:o,chars2:a,lineArray:n}};d.prototype.diff_charsToLines_=function(t,e){for(var n=0;n<t.length;n++){for(var r=t[n][1],i=[],s=0;s<r.length;s++)i[s]=e[r.charCodeAt(s)];t[n][1]=i.join("")}};d.prototype.diff_commonPrefix=function(t,e){if(!t||!e||t.charAt(0)!=e.charAt(0))return 0;for(var n=0,r=Math.min(t.length,e.length),i=r,s=0;n<i;)t.substring(s,i)==e.substring(s,i)?(n=i,s=n):r=i,i=Math.floor((r-n)/2+n);return i};d.prototype.diff_commonSuffix=function(t,e){if(!t||!e||t.charAt(t.length-1)!=e.charAt(e.length-1))return 0;for(var n=0,r=Math.min(t.length,e.length),i=r,s=0;n<i;)t.substring(t.length-i,t.length-s)==e.substring(e.length-i,e.length-s)?(n=i,s=n):r=i,i=Math.floor((r-n)/2+n);return i};d.prototype.diff_commonOverlap_=function(t,e){var n=t.length,r=e.length;if(n==0||r==0)return 0;n>r?t=t.substring(n-r):n<r&&(e=e.substring(0,n));var i=Math.min(n,r);if(t==e)return i;for(var s=0,o=1;;){var a=t.substring(i-o),l=e.indexOf(a);if(l==-1)return s;o+=l,(l==0||t.substring(i-o)==e.substring(0,o))&&(s=o,o++)}};d.prototype.diff_halfMatch_=function(t,e){if(this.Diff_Timeout<=0)return null;var n=t.length>e.length?t:e,r=t.length>e.length?e:t;if(n.length<4||r.length*2<n.length)return null;var i=this;function s(m,p,x){for(var w=m.substring(x,x+Math.floor(m.length/4)),A=-1,b="",S,M,$,E;(A=p.indexOf(w,A+1))!=-1;){var v=i.diff_commonPrefix(m.substring(x),p.substring(A)),y=i.diff_commonSuffix(m.substring(0,x),p.substring(0,A));b.length<y+v&&(b=p.substring(A-y,A)+p.substring(A,A+v),S=m.substring(0,x-y),M=m.substring(x+v),$=p.substring(0,A-y),E=p.substring(A+v))}return b.length*2>=m.length?[S,M,$,E,b]:null}var o=s(n,r,Math.ceil(n.length/4)),a=s(n,r,Math.ceil(n.length/2)),l;if(!o&&!a)return null;a?o?l=o[4].length>a[4].length?o:a:l=a:l=o;var h,c,u,f;t.length>e.length?(h=l[0],c=l[1],u=l[2],f=l[3]):(u=l[0],f=l[1],h=l[2],c=l[3]);var g=l[4];return[h,c,u,f,g]};d.prototype.diff_cleanupSemantic=function(t){for(var e=!1,n=[],r=0,i=null,s=0,o=0,a=0,l=0,h=0;s<t.length;)t[s][0]==k?(n[r++]=s,o=l,a=h,l=0,h=0,i=t[s][1]):(t[s][0]==_?l+=t[s][1].length:h+=t[s][1].length,i&&i.length<=Math.max(o,a)&&i.length<=Math.max(l,h)&&(t.splice(n[r-1],0,new d.Diff(T,i)),t[n[r-1]+1][0]=_,r--,r--,s=r>0?n[r-1]:-1,o=0,a=0,l=0,h=0,i=null,e=!0)),s++;for(e&&this.diff_cleanupMerge(t),this.diff_cleanupSemanticLossless(t),s=1;s<t.length;){if(t[s-1][0]==T&&t[s][0]==_){var c=t[s-1][1],u=t[s][1],f=this.diff_commonOverlap_(c,u),g=this.diff_commonOverlap_(u,c);f>=g?(f>=c.length/2||f>=u.length/2)&&(t.splice(s,0,new d.Diff(k,u.substring(0,f))),t[s-1][1]=c.substring(0,c.length-f),t[s+1][1]=u.substring(f),s++):(g>=c.length/2||g>=u.length/2)&&(t.splice(s,0,new d.Diff(k,c.substring(0,g))),t[s-1][0]=_,t[s-1][1]=u.substring(0,u.length-g),t[s+1][0]=T,t[s+1][1]=c.substring(g),s++),s++}s++}};d.prototype.diff_cleanupSemanticLossless=function(t){function e(g,m){if(!g||!m)return 6;var p=g.charAt(g.length-1),x=m.charAt(0),w=p.match(d.nonAlphaNumericRegex_),A=x.match(d.nonAlphaNumericRegex_),b=w&&p.match(d.whitespaceRegex_),S=A&&x.match(d.whitespaceRegex_),M=b&&p.match(d.linebreakRegex_),$=S&&x.match(d.linebreakRegex_),E=M&&g.match(d.blanklineEndRegex_),v=$&&m.match(d.blanklineStartRegex_);return E||v?5:M||$?4:w&&!b&&S?3:b||S?2:w||A?1:0}for(var n=1;n<t.length-1;){if(t[n-1][0]==k&&t[n+1][0]==k){var r=t[n-1][1],i=t[n][1],s=t[n+1][1],o=this.diff_commonSuffix(r,i);if(o){var a=i.substring(i.length-o);r=r.substring(0,r.length-o),i=a+i.substring(0,i.length-o),s=a+s}for(var l=r,h=i,c=s,u=e(r,i)+e(i,s);i.charAt(0)===s.charAt(0);){r+=i.charAt(0),i=i.substring(1)+s.charAt(0),s=s.substring(1);var f=e(r,i)+e(i,s);f>=u&&(u=f,l=r,h=i,c=s)}t[n-1][1]!=l&&(l?t[n-1][1]=l:(t.splice(n-1,1),n--),t[n][1]=h,c?t[n+1][1]=c:(t.splice(n+1,1),n--))}n++}};d.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;d.whitespaceRegex_=/\s/;d.linebreakRegex_=/[\r\n]/;d.blanklineEndRegex_=/\n\r?\n$/;d.blanklineStartRegex_=/^\r?\n\r?\n/;d.prototype.diff_cleanupEfficiency=function(t){for(var e=!1,n=[],r=0,i=null,s=0,o=!1,a=!1,l=!1,h=!1;s<t.length;)t[s][0]==k?(t[s][1].length<this.Diff_EditCost&&(l||h)?(n[r++]=s,o=l,a=h,i=t[s][1]):(r=0,i=null),l=h=!1):(t[s][0]==T?h=!0:l=!0,i&&(o&&a&&l&&h||i.length<this.Diff_EditCost/2&&o+a+l+h==3)&&(t.splice(n[r-1],0,new d.Diff(T,i)),t[n[r-1]+1][0]=_,r--,i=null,o&&a?(l=h=!0,r=0):(r--,s=r>0?n[r-1]:-1,l=h=!1),e=!0)),s++;e&&this.diff_cleanupMerge(t)};d.prototype.diff_cleanupMerge=function(t){t.push(new d.Diff(k,""));for(var e=0,n=0,r=0,i="",s="",o;e<t.length;)switch(t[e][0]){case _:r++,s+=t[e][1],e++;break;case T:n++,i+=t[e][1],e++;break;case k:n+r>1?(n!==0&&r!==0&&(o=this.diff_commonPrefix(s,i),o!==0&&(e-n-r>0&&t[e-n-r-1][0]==k?t[e-n-r-1][1]+=s.substring(0,o):(t.splice(0,0,new d.Diff(k,s.substring(0,o))),e++),s=s.substring(o),i=i.substring(o)),o=this.diff_commonSuffix(s,i),o!==0&&(t[e][1]=s.substring(s.length-o)+t[e][1],s=s.substring(0,s.length-o),i=i.substring(0,i.length-o))),e-=n+r,t.splice(e,n+r),i.length&&(t.splice(e,0,new d.Diff(T,i)),e++),s.length&&(t.splice(e,0,new d.Diff(_,s)),e++),e++):e!==0&&t[e-1][0]==k?(t[e-1][1]+=t[e][1],t.splice(e,1)):e++,r=0,n=0,i="",s="";break}t[t.length-1][1]===""&&t.pop();var a=!1;for(e=1;e<t.length-1;)t[e-1][0]==k&&t[e+1][0]==k&&(t[e][1].substring(t[e][1].length-t[e-1][1].length)==t[e-1][1]?(t[e][1]=t[e-1][1]+t[e][1].substring(0,t[e][1].length-t[e-1][1].length),t[e+1][1]=t[e-1][1]+t[e+1][1],t.splice(e-1,1),a=!0):t[e][1].substring(0,t[e+1][1].length)==t[e+1][1]&&(t[e-1][1]+=t[e+1][1],t[e][1]=t[e][1].substring(t[e+1][1].length)+t[e+1][1],t.splice(e+1,1),a=!0)),e++;a&&this.diff_cleanupMerge(t)};d.prototype.diff_xIndex=function(t,e){var n=0,r=0,i=0,s=0,o;for(o=0;o<t.length&&(t[o][0]!==_&&(n+=t[o][1].length),t[o][0]!==T&&(r+=t[o][1].length),!(n>e));o++)i=n,s=r;return t.length!=o&&t[o][0]===T?s:s+(e-i)};d.prototype.diff_prettyHtml=function(t){for(var e=[],n=/&/g,r=/</g,i=/>/g,s=/\n/g,o=0;o<t.length;o++){var a=t[o][0],l=t[o][1],h=l.replace(n,"&amp;").replace(r,"&lt;").replace(i,"&gt;").replace(s,"&para;<br>");switch(a){case _:e[o]='<ins style="background:#e6ffe6;">'+h+"</ins>";break;case T:e[o]='<del style="background:#ffe6e6;">'+h+"</del>";break;case k:e[o]="<span>"+h+"</span>";break}}return e.join("")};d.prototype.diff_text1=function(t){for(var e=[],n=0;n<t.length;n++)t[n][0]!==_&&(e[n]=t[n][1]);return e.join("")};d.prototype.diff_text2=function(t){for(var e=[],n=0;n<t.length;n++)t[n][0]!==T&&(e[n]=t[n][1]);return e.join("")};d.prototype.diff_levenshtein=function(t){for(var e=0,n=0,r=0,i=0;i<t.length;i++){var s=t[i][0],o=t[i][1];switch(s){case _:n+=o.length;break;case T:r+=o.length;break;case k:e+=Math.max(n,r),n=0,r=0;break}}return e+=Math.max(n,r),e};d.prototype.diff_toDelta=function(t){for(var e=[],n=0;n<t.length;n++)switch(t[n][0]){case _:e[n]="+"+encodeURI(t[n][1]);break;case T:e[n]="-"+t[n][1].length;break;case k:e[n]="="+t[n][1].length;break}return e.join(" ").replace(/%20/g," ")};d.prototype.diff_fromDelta=function(t,e){for(var n=[],r=0,i=0,s=e.split(/\t/g),o=0;o<s.length;o++){var a=s[o].substring(1);switch(s[o].charAt(0)){case"+":try{n[r++]=new d.Diff(_,decodeURI(a))}catch{throw new Error("Illegal escape in diff_fromDelta: "+a)}break;case"-":case"=":var l=parseInt(a,10);if(isNaN(l)||l<0)throw new Error("Invalid number in diff_fromDelta: "+a);var h=t.substring(i,i+=l);s[o].charAt(0)=="="?n[r++]=new d.Diff(k,h):n[r++]=new d.Diff(T,h);break;default:if(s[o])throw new Error("Invalid diff operation in diff_fromDelta: "+s[o])}}if(i!=t.length)throw new Error("Delta length ("+i+") does not equal source text length ("+t.length+").");return n};d.prototype.match_main=function(t,e,n){if(t==null||e==null||n==null)throw new Error("Null input. (match_main)");return n=Math.max(0,Math.min(n,t.length)),t==e?0:t.length?t.substring(n,n+e.length)==e?n:this.match_bitap_(t,e,n):-1};d.prototype.match_bitap_=function(t,e,n){if(e.length>this.Match_MaxBits)throw new Error("Pattern too long for this browser.");var r=this.match_alphabet_(e),i=this;function s(S,M){var $=S/e.length,E=Math.abs(n-M);return i.Match_Distance?$+E/i.Match_Distance:E?1:$}var o=this.Match_Threshold,a=t.indexOf(e,n);a!=-1&&(o=Math.min(s(0,a),o),a=t.lastIndexOf(e,n+e.length),a!=-1&&(o=Math.min(s(0,a),o)));var l=1<<e.length-1;a=-1;for(var h,c,u=e.length+t.length,f,g=0;g<e.length;g++){for(h=0,c=u;h<c;)s(g,n+c)<=o?h=c:u=c,c=Math.floor((u-h)/2+h);u=c;var m=Math.max(1,n-c+1),p=Math.min(n+c,t.length)+e.length,x=Array(p+2);x[p+1]=(1<<g)-1;for(var w=p;w>=m;w--){var A=r[t.charAt(w-1)];if(g===0?x[w]=(x[w+1]<<1|1)&A:x[w]=(x[w+1]<<1|1)&A|((f[w+1]|f[w])<<1|1)|f[w+1],x[w]&l){var b=s(g,w-1);if(b<=o)if(o=b,a=w-1,a>n)m=Math.max(1,2*n-a);else break}}if(s(g+1,n)>o)break;f=x}return a};d.prototype.match_alphabet_=function(t){for(var e={},n=0;n<t.length;n++)e[t.charAt(n)]=0;for(var n=0;n<t.length;n++)e[t.charAt(n)]|=1<<t.length-n-1;return e};d.prototype.patch_addContext_=function(t,e){if(e.length!=0){if(t.start2===null)throw Error("patch not initialized");for(var n=e.substring(t.start2,t.start2+t.length1),r=0;e.indexOf(n)!=e.lastIndexOf(n)&&n.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)r+=this.Patch_Margin,n=e.substring(t.start2-r,t.start2+t.length1+r);r+=this.Patch_Margin;var i=e.substring(t.start2-r,t.start2);i&&t.diffs.unshift(new d.Diff(k,i));var s=e.substring(t.start2+t.length1,t.start2+t.length1+r);s&&t.diffs.push(new d.Diff(k,s)),t.start1-=i.length,t.start2-=i.length,t.length1+=i.length+s.length,t.length2+=i.length+s.length}};d.prototype.patch_make=function(t,e,n){var r,i;if(typeof t=="string"&&typeof e=="string"&&typeof n>"u")r=t,i=this.diff_main(r,e,!0),i.length>2&&(this.diff_cleanupSemantic(i),this.diff_cleanupEfficiency(i));else if(t&&typeof t=="object"&&typeof e>"u"&&typeof n>"u")i=t,r=this.diff_text1(i);else if(typeof t=="string"&&e&&typeof e=="object"&&typeof n>"u")r=t,i=e;else if(typeof t=="string"&&typeof e=="string"&&n&&typeof n=="object")r=t,i=n;else throw new Error("Unknown call format to patch_make.");if(i.length===0)return[];for(var s=[],o=new d.patch_obj,a=0,l=0,h=0,c=r,u=r,f=0;f<i.length;f++){var g=i[f][0],m=i[f][1];switch(!a&&g!==k&&(o.start1=l,o.start2=h),g){case _:o.diffs[a++]=i[f],o.length2+=m.length,u=u.substring(0,h)+m+u.substring(h);break;case T:o.length1+=m.length,o.diffs[a++]=i[f],u=u.substring(0,h)+u.substring(h+m.length);break;case k:m.length<=2*this.Patch_Margin&&a&&i.length!=f+1?(o.diffs[a++]=i[f],o.length1+=m.length,o.length2+=m.length):m.length>=2*this.Patch_Margin&&a&&(this.patch_addContext_(o,c),s.push(o),o=new d.patch_obj,a=0,c=u,l=h);break}g!==_&&(l+=m.length),g!==T&&(h+=m.length)}return a&&(this.patch_addContext_(o,c),s.push(o)),s};d.prototype.patch_deepCopy=function(t){for(var e=[],n=0;n<t.length;n++){var r=t[n],i=new d.patch_obj;i.diffs=[];for(var s=0;s<r.diffs.length;s++)i.diffs[s]=new d.Diff(r.diffs[s][0],r.diffs[s][1]);i.start1=r.start1,i.start2=r.start2,i.length1=r.length1,i.length2=r.length2,e[n]=i}return e};d.prototype.patch_apply=function(t,e){if(t.length==0)return[e,[]];t=this.patch_deepCopy(t);var n=this.patch_addPadding(t);e=n+e+n,this.patch_splitMax(t);for(var r=0,i=[],s=0;s<t.length;s++){var o=t[s].start2+r,a=this.diff_text1(t[s].diffs),l,h=-1;if(a.length>this.Match_MaxBits?(l=this.match_main(e,a.substring(0,this.Match_MaxBits),o),l!=-1&&(h=this.match_main(e,a.substring(a.length-this.Match_MaxBits),o+a.length-this.Match_MaxBits),(h==-1||l>=h)&&(l=-1))):l=this.match_main(e,a,o),l==-1)i[s]=!1,r-=t[s].length2-t[s].length1;else{i[s]=!0,r=l-o;var c;if(h==-1?c=e.substring(l,l+a.length):c=e.substring(l,h+this.Match_MaxBits),a==c)e=e.substring(0,l)+this.diff_text2(t[s].diffs)+e.substring(l+a.length);else{var u=this.diff_main(a,c,!1);if(a.length>this.Match_MaxBits&&this.diff_levenshtein(u)/a.length>this.Patch_DeleteThreshold)i[s]=!1;else{this.diff_cleanupSemanticLossless(u);for(var f=0,g,m=0;m<t[s].diffs.length;m++){var p=t[s].diffs[m];p[0]!==k&&(g=this.diff_xIndex(u,f)),p[0]===_?e=e.substring(0,l+g)+p[1]+e.substring(l+g):p[0]===T&&(e=e.substring(0,l+g)+e.substring(l+this.diff_xIndex(u,f+p[1].length))),p[0]!==T&&(f+=p[1].length)}}}}}return e=e.substring(n.length,e.length-n.length),[e,i]};d.prototype.patch_addPadding=function(t){for(var e=this.Patch_Margin,n="",r=1;r<=e;r++)n+=String.fromCharCode(r);for(var r=0;r<t.length;r++)t[r].start1+=e,t[r].start2+=e;var i=t[0],s=i.diffs;if(s.length==0||s[0][0]!=k)s.unshift(new d.Diff(k,n)),i.start1-=e,i.start2-=e,i.length1+=e,i.length2+=e;else if(e>s[0][1].length){var o=e-s[0][1].length;s[0][1]=n.substring(s[0][1].length)+s[0][1],i.start1-=o,i.start2-=o,i.length1+=o,i.length2+=o}if(i=t[t.length-1],s=i.diffs,s.length==0||s[s.length-1][0]!=k)s.push(new d.Diff(k,n)),i.length1+=e,i.length2+=e;else if(e>s[s.length-1][1].length){var o=e-s[s.length-1][1].length;s[s.length-1][1]+=n.substring(0,o),i.length1+=o,i.length2+=o}return n};d.prototype.patch_splitMax=function(t){for(var e=this.Match_MaxBits,n=0;n<t.length;n++)if(!(t[n].length1<=e)){var r=t[n];t.splice(n--,1);for(var i=r.start1,s=r.start2,o="";r.diffs.length!==0;){var a=new d.patch_obj,l=!0;for(a.start1=i-o.length,a.start2=s-o.length,o!==""&&(a.length1=a.length2=o.length,a.diffs.push(new d.Diff(k,o)));r.diffs.length!==0&&a.length1<e-this.Patch_Margin;){var h=r.diffs[0][0],c=r.diffs[0][1];h===_?(a.length2+=c.length,s+=c.length,a.diffs.push(r.diffs.shift()),l=!1):h===T&&a.diffs.length==1&&a.diffs[0][0]==k&&c.length>2*e?(a.length1+=c.length,i+=c.length,l=!1,a.diffs.push(new d.Diff(h,c)),r.diffs.shift()):(c=c.substring(0,e-a.length1-this.Patch_Margin),a.length1+=c.length,i+=c.length,h===k?(a.length2+=c.length,s+=c.length):l=!1,a.diffs.push(new d.Diff(h,c)),c==r.diffs[0][1]?r.diffs.shift():r.diffs[0][1]=r.diffs[0][1].substring(c.length))}o=this.diff_text2(a.diffs),o=o.substring(o.length-this.Patch_Margin);var u=this.diff_text1(r.diffs).substring(0,this.Patch_Margin);u!==""&&(a.length1+=u.length,a.length2+=u.length,a.diffs.length!==0&&a.diffs[a.diffs.length-1][0]===k?a.diffs[a.diffs.length-1][1]+=u:a.diffs.push(new d.Diff(k,u))),l||t.splice(++n,0,a)}}};d.prototype.patch_toText=function(t){for(var e=[],n=0;n<t.length;n++)e[n]=t[n];return e.join("")};d.prototype.patch_fromText=function(t){var e=[];if(!t)return e;for(var n=t.split(`
4
4
  `),r=0,i=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;r<n.length;){var s=n[r].match(i);if(!s)throw new Error("Invalid patch string: "+n[r]);var o=new d.patch_obj;for(e.push(o),o.start1=parseInt(s[1],10),s[2]===""?(o.start1--,o.length1=1):s[2]=="0"?o.length1=0:(o.start1--,o.length1=parseInt(s[2],10)),o.start2=parseInt(s[3],10),s[4]===""?(o.start2--,o.length2=1):s[4]=="0"?o.length2=0:(o.start2--,o.length2=parseInt(s[4],10)),r++;r<n.length;){var a=n[r].charAt(0);try{var l=decodeURI(n[r].substring(1))}catch{throw new Error("Illegal escape in patch_fromText: "+l)}if(a=="-")o.diffs.push(new d.Diff(T,l));else if(a=="+")o.diffs.push(new d.Diff(_,l));else if(a==" ")o.diffs.push(new d.Diff(k,l));else{if(a=="@")break;if(a!=="")throw new Error('Invalid patch mode "'+a+'" in: '+l)}r++}}return e};d.patch_obj=function(){this.diffs=[],this.start1=null,this.start2=null,this.length1=0,this.length2=0};d.patch_obj.prototype.toString=function(){var t,e;this.length1===0?t=this.start1+",0":this.length1==1?t=this.start1+1:t=this.start1+1+","+this.length1,this.length2===0?e=this.start2+",0":this.length2==1?e=this.start2+1:e=this.start2+1+","+this.length2;for(var n=["@@ -"+t+" +"+e+` @@
5
5
  `],r,i=0;i<this.diffs.length;i++){switch(this.diffs[i][0]){case _:r="+";break;case T:r="-";break;case k:r=" ";break}n[i+1]=r+encodeURI(this.diffs[i][1])+`
6
- `}return n.join("").replace(/%20/g," ")};we.exports=d;we.exports.diff_match_patch=d;we.exports.DIFF_DELETE=T;we.exports.DIFF_INSERT=_;we.exports.DIFF_EQUAL=k});import{hostname as zi}from"node:os";import{resolve as Di}from"node:path";import{createRequire as mr}from"module";var pr=mr("/"),ye,yr,vr;try{ye=pr("worker_threads"),yr=ye.Worker,vr=ye.isMarkedAsUntransferable}catch{}var N=Uint8Array,K=Uint16Array,ft=Int32Array,Oe=new N([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),Ne=new N([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),lt=new N([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),Jt=function(t,e){for(var n=new K(31),r=0;r<31;++r)n[r]=e+=1<<t[r-1];for(var i=new ft(n[30]),r=1;r<30;++r)for(var s=n[r];s<n[r+1];++s)i[s]=s-n[r]<<5|r;return{b:n,r:i}},ye=Jt(Oe,2),Kt=ye.b,ht=ye.r;Kt[28]=258,ht[258]=28;var Ht=Jt(Ne,0),wr=Ht.b,Lt=Ht.r,ct=new K(32768);for(C=0;C<32768;++C)te=(C&43690)>>1|(C&21845)<<1,te=(te&52428)>>2|(te&13107)<<2,te=(te&61680)>>4|(te&3855)<<4,ct[C]=((te&65280)>>8|(te&255)<<8)>>1;var te,C,Q=(function(t,e,n){for(var r=t.length,i=0,s=new K(e);i<r;++i)t[i]&&++s[t[i]-1];var o=new K(e);for(i=1;i<e;++i)o[i]=o[i-1]+s[i-1]<<1;var a;if(n){a=new K(1<<e);var l=15-e;for(i=0;i<r;++i)if(t[i])for(var h=i<<4|t[i],c=e-t[i],u=o[t[i]-1]++<<c,f=u|(1<<c)-1;u<=f;++u)a[ct[u]>>l]=h}else for(a=new K(r),i=0;i<r;++i)t[i]&&(a[i]=ct[o[t[i]-1]++]>>15-t[i]);return a}),oe=new N(288);for(C=0;C<144;++C)oe[C]=8;var C;for(C=144;C<256;++C)oe[C]=9;var C;for(C=256;C<280;++C)oe[C]=7;var C;for(C=280;C<288;++C)oe[C]=8;var C,Me=new N(32);for(C=0;C<32;++C)Me[C]=5;var C,br=Q(oe,9,0),kr=Q(oe,9,1),Sr=Q(Me,5,0),Er=Q(Me,5,1),st=function(t){for(var e=t[0],n=1;n<t.length;++n)t[n]>e&&(e=t[n]);return e},Z=function(t,e,n){var r=e/8|0;return(t[r]|t[r+1]<<8)>>(e&7)&n},ot=function(t,e){var n=e/8|0;return(t[n]|t[n+1]<<8|t[n+2]<<16)>>(e&7)},dt=function(t){return(t+7)/8|0},jt=function(t,e,n){return(e==null||e<0)&&(e=0),(n==null||n>t.length)&&(n=t.length),new N(t.subarray(e,n))};var Ar=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],re=function(t,e,n){var r=new Error(e||Ar[t]);if(r.code=t,Error.captureStackTrace&&Error.captureStackTrace(r,re),!n)throw r;return r},xr=function(t,e,n,r){var i=t.length,s=r?r.length:0;if(!i||e.f&&!e.l)return n||new N(0);var o=!n,a=o||e.i!=2,l=e.i;o&&(n=new N(i*3));var h=function(Ae){var xe=n.length;if(Ae>xe){var pe=new N(Math.max(xe*2,Ae));pe.set(n),n=pe}},c=e.f||0,u=e.p||0,f=e.b||0,g=e.l,m=e.d,p=e.m,x=e.n,w=i*8;do{if(!g){c=Z(t,u,1);var A=Z(t,u+1,3);if(u+=3,A)if(A==1)g=kr,m=Er,p=9,x=5;else if(A==2){var $=Z(t,u,31)+257,E=Z(t,u+10,15)+4,v=$+Z(t,u+5,31)+1;u+=14;for(var y=new N(v),D=new N(19),z=0;z<E;++z)D[lt[z]]=Z(t,u+z*3,7);u+=E*3;for(var O=st(D),se=(1<<O)-1,W=Q(D,O,1),z=0;z<v;){var B=W[Z(t,u,se)];u+=B&15;var b=B>>4;if(b<16)y[z++]=b;else{var U=0,R=0;for(b==16?(R=3+Z(t,u,3),u+=2,U=y[z-1]):b==17?(R=3+Z(t,u,7),u+=3):b==18&&(R=11+Z(t,u,127),u+=7);R--;)y[z++]=U}}var L=y.subarray(0,$),F=y.subarray($);p=st(L),x=st(F),g=Q(L,p,1),m=Q(F,x,1)}else re(1);else{var b=dt(u)+4,S=t[b-4]|t[b-3]<<8,M=b+S;if(M>i){l&&re(0);break}a&&h(f+S),n.set(t.subarray(b,M),f),e.b=f+=S,e.p=u=M*8,e.f=c;continue}if(u>w){l&&re(0);break}}a&&h(f+131072);for(var Ee=(1<<p)-1,G=(1<<x)-1,ee=u;;ee=u){var U=g[ot(t,u)&Ee],V=U>>4;if(u+=U&15,u>w){l&&re(0);break}if(U||re(2),V<256)n[f++]=V;else if(V==256){ee=u,g=null;break}else{var J=V-254;if(V>264){var z=V-257,I=Oe[z];J=Z(t,u,(1<<I)-1)+Kt[z],u+=I}var Y=m[ot(t,u)&G],ge=Y>>4;Y||re(3),u+=Y&15;var F=wr[ge];if(ge>3){var I=Ne[ge];F+=ot(t,u)&(1<<I)-1,u+=I}if(u>w){l&&re(0);break}a&&h(f+131072);var me=f+J;if(f<F){var Ue=s-F,Fe=Math.min(F,me);for(Ue+f<0&&re(3);f<Fe;++f)n[f]=r[Ue+f]}for(;f<me;++f)n[f]=n[f-F]}}e.l=g,e.p=ee,e.b=f,e.f=c,g&&(c=1,e.m=p,e.d=m,e.n=x)}while(!c);return f!=n.length&&o?jt(n,0,f):n.subarray(0,f)},ne=function(t,e,n){n<<=e&7;var r=e/8|0;t[r]|=n,t[r+1]|=n>>8},Pe=function(t,e,n){n<<=e&7;var r=e/8|0;t[r]|=n,t[r+1]|=n>>8,t[r+2]|=n>>16},at=function(t,e){for(var n=[],r=0;r<t.length;++r)t[r]&&n.push({s:r,f:t[r]});var i=n.length,s=n.slice();if(!i)return{t:qt,l:0};if(i==1){var o=new N(n[0].s+1);return o[n[0].s]=1,{t:o,l:1}}n.sort(function(M,$){return M.f-$.f}),n.push({s:-1,f:25001});var a=n[0],l=n[1],h=0,c=1,u=2;for(n[0]={s:-1,f:a.f+l.f,l:a,r:l};c!=i-1;)a=n[n[h].f<n[u].f?h++:u++],l=n[h!=c&&n[h].f<n[u].f?h++:u++],n[c++]={s:-1,f:a.f+l.f,l:a,r:l};for(var f=s[0].s,r=1;r<i;++r)s[r].s>f&&(f=s[r].s);var g=new K(f+1),m=ut(n[c-1],g,0);if(m>e){var r=0,p=0,x=m-e,w=1<<x;for(s.sort(function($,E){return g[E.s]-g[$.s]||$.f-E.f});r<i;++r){var A=s[r].s;if(g[A]>e)p+=w-(1<<m-g[A]),g[A]=e;else break}for(p>>=x;p>0;){var b=s[r].s;g[b]<e?p-=1<<e-g[b]++-1:++r}for(;r>=0&&p;--r){var S=s[r].s;g[S]==e&&(--g[S],++p)}m=e}return{t:new N(g),l:m}},ut=function(t,e,n){return t.s==-1?Math.max(ut(t.l,e,n+1),ut(t.r,e,n+1)):e[t.s]=n},Wt=function(t){for(var e=t.length;e&&!t[--e];);for(var n=new K(++e),r=0,i=t[0],s=1,o=function(l){n[r++]=l},a=1;a<=e;++a)if(t[a]==i&&a!=e)++s;else{if(!i&&s>2){for(;s>138;s-=138)o(32754);s>2&&(o(s>10?s-11<<5|28690:s-3<<5|12305),s=0)}else if(s>3){for(o(i),--s;s>6;s-=6)o(8304);s>2&&(o(s-3<<5|8208),s=0)}for(;s--;)o(i);s=1,i=t[a]}return{c:n.subarray(0,r),n:e}},$e=function(t,e){for(var n=0,r=0;r<e.length;++r)n+=t[r]*e[r];return n},Gt=function(t,e,n){var r=n.length,i=dt(e+2);t[i]=r&255,t[i+1]=r>>8,t[i+2]=t[i]^255,t[i+3]=t[i+1]^255;for(var s=0;s<r;++s)t[i+s+4]=n[s];return(i+4+r)*8},Vt=function(t,e,n,r,i,s,o,a,l,h,c){ne(e,c++,n),++i[256];for(var u=at(i,15),f=u.t,g=u.l,m=at(s,15),p=m.t,x=m.l,w=Wt(f),A=w.c,b=w.n,S=Wt(p),M=S.c,$=S.n,E=new K(19),v=0;v<A.length;++v)++E[A[v]&31];for(var v=0;v<M.length;++v)++E[M[v]&31];for(var y=at(E,7),D=y.t,z=y.l,O=19;O>4&&!D[lt[O-1]];--O);var se=h+5<<3,W=$e(i,oe)+$e(s,Me)+o,B=$e(i,f)+$e(s,p)+o+14+3*O+$e(E,D)+2*E[16]+3*E[17]+7*E[18];if(l>=0&&se<=W&&se<=B)return Gt(e,c,t.subarray(l,l+h));var U,R,L,F;if(ne(e,c,1+(B<W)),c+=2,B<W){U=Q(f,g,0),R=f,L=Q(p,x,0),F=p;var Ee=Q(D,z,0);ne(e,c,b-257),ne(e,c+5,$-1),ne(e,c+10,O-4),c+=14;for(var v=0;v<O;++v)ne(e,c+3*v,D[lt[v]]);c+=3*O;for(var G=[A,M],ee=0;ee<2;++ee)for(var V=G[ee],v=0;v<V.length;++v){var J=V[v]&31;ne(e,c,Ee[J]),c+=D[J],J>15&&(ne(e,c,V[v]>>5&127),c+=V[v]>>12)}}else U=br,R=oe,L=Sr,F=Me;for(var v=0;v<a;++v){var I=r[v];if(I>255){var J=I>>18&31;Pe(e,c,U[J+257]),c+=R[J+257],J>7&&(ne(e,c,I>>23&31),c+=Oe[J]);var Y=I&31;Pe(e,c,L[Y]),c+=F[Y],Y>3&&(Pe(e,c,I>>5&8191),c+=Ne[Y])}else Pe(e,c,U[I]),c+=R[I]}return Pe(e,c,U[256]),c+R[256]},Pr=new ft([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),qt=new N(0),$r=function(t,e,n,r,i,s){var o=s.z||t.length,a=new N(r+o+5*(1+Math.ceil(o/7e3))+i),l=a.subarray(r,a.length-i),h=s.l,c=(s.r||0)&7;if(e){c&&(l[0]=s.r>>3);for(var u=Pr[e-1],f=u>>13,g=u&8191,m=(1<<n)-1,p=s.p||new K(32768),x=s.h||new K(m+1),w=Math.ceil(n/3),A=2*w,b=function(it){return(t[it]^t[it+1]<<w^t[it+2]<<A)&m},S=new ft(25e3),M=new K(288),$=new K(32),E=0,v=0,y=s.i||0,D=0,z=s.w||0,O=0;y+2<o;++y){var se=b(y),W=y&32767,B=x[se];if(p[W]=B,x[se]=W,z<=y){var U=o-y;if((E>7e3||D>24576)&&(U>423||!h)){c=Vt(t,l,0,S,M,$,v,D,O,y-O,c),D=E=v=0,O=y;for(var R=0;R<286;++R)M[R]=0;for(var R=0;R<30;++R)$[R]=0}var L=2,F=0,Ee=g,G=W-B&32767;if(U>2&&se==b(y-G))for(var ee=Math.min(f,U)-1,V=Math.min(32767,y),J=Math.min(258,U);G<=V&&--Ee&&W!=B;){if(t[y+L]==t[y+L-G]){for(var I=0;I<J&&t[y+I]==t[y+I-G];++I);if(I>L){if(L=I,F=G,I>ee)break;for(var Y=Math.min(G,I-2),ge=0,R=0;R<Y;++R){var me=y-G+R&32767,Ue=p[me],Fe=me-Ue&32767;Fe>ge&&(ge=Fe,B=me)}}}W=B,B=p[W],G+=W-B&32767}if(F){S[D++]=268435456|ht[L]<<18|Lt[F];var Ae=ht[L]&31,xe=Lt[F]&31;v+=Oe[Ae]+Ne[xe],++M[257+Ae],++$[xe],z=y+L,++E}else S[D++]=t[y],++M[t[y]]}}for(y=Math.max(y,z);y<o;++y)S[D++]=t[y],++M[t[y]];c=Vt(t,l,h,S,M,$,v,D,O,y-O,c),h||(s.r=c&7|l[c/8|0]<<3,c-=7,s.h=x,s.p=p,s.i=y,s.w=z)}else{for(var y=s.w||0;y<o+h;y+=65535){var pe=y+65535;pe>=o&&(l[c/8|0]=h,pe=o),c=Gt(l,c+1,t.subarray(y,pe))}s.i=o}return jt(a,0,r+dt(c)+i)};var Mr=function(t,e,n,r,i){if(!i&&(i={l:1},e.dictionary)){var s=e.dictionary.subarray(-32768),o=new N(s.length+t.length);o.set(s),o.set(t,s.length),t=o,i.w=s.length}return $r(t,e.level==null?6:e.level,e.mem==null?i.l?Math.ceil(Math.max(8,Math.min(13,Math.log(t.length)))*1.5):20:12+e.mem,n,r,i)};function gt(t,e){return Mr(t,e||{},0,0)}function Zt(t,e){return xr(t,{i:2},e&&e.out,e&&e.dictionary)}var Cr=typeof TextDecoder<"u"&&new TextDecoder,Tr=0;try{Cr.decode(qt,{stream:!0}),Tr=1}catch{}var Yt="basalt/hkdf-aes-gcm/1",he=12,Be=128,Qt=he+Be/8+1,X=20,Le=new TextEncoder,_r=new TextDecoder,Ce={auth:"basalt/auth/1",path:"basalt/path/1",content:"basalt/content/1",nonce:"basalt/nonce/1",meta:"basalt/meta/1"};function ce(){let t=globalThis.crypto;if(!t?.subtle)throw new Error("WebCrypto is unavailable, so this vault cannot be opened");return t.subtle}function en(){let t=globalThis.crypto;if(!t?.getRandomValues)throw new Error("no secure random source is available, so a vault cannot be created here");return t.getRandomValues(new Uint8Array(X))}async function tn(t){if(t.length<16)throw new Error(`root secret is ${t.length} bytes, need at least 16`);let e=ce(),n=await e.importKey("raw",ie(t),"HKDF",!1,["deriveKey","deriveBits"]),r=h=>({name:"HKDF",hash:"SHA-256",salt:new Uint8Array(0),info:Le.encode(h)}),[i,s,o,a,l]=await Promise.all([e.deriveBits(r(Ce.auth),n,256),e.deriveKey(r(Ce.path),n,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"]),e.deriveKey(r(Ce.content),n,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"]),e.deriveKey(r(Ce.nonce),n,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),e.deriveKey(r(Ce.meta),n,{name:"HMAC",hash:"SHA-256"},!1,["sign"])]);return{auth:new Uint8Array(i),path:s,content:o,nonce:a,meta:l}}async function nn(t,e,n){let r=ce(),i=await Rr(e,n),s=await r.encrypt({name:"AES-GCM",iv:ie(i),tagLength:Be},t,ie(n)),o=new Uint8Array(he+s.byteLength);return o.set(i,0),o.set(new Uint8Array(s),he),o}async function rn(t,e){if(e.length<he+Be/8)throw new Error(`sealed value is ${e.length} bytes, too short to contain a nonce and a tag`);let n=ce(),r=e.subarray(0,he),i=e.subarray(he);try{let s=await n.decrypt({name:"AES-GCM",iv:ie(r),tagLength:Be},t,ie(i));return new Uint8Array(s)}catch(s){throw new Error("sealed value failed authentication, so it is not what was stored",{cause:s})}}async function Rr(t,e){let n=await ce().sign("HMAC",t,ie(e));return new Uint8Array(n,0,he)}async function We(t,e){return Te(await nn(t.path,t.nonce,Le.encode(e)))}async function Ve(t,e){let n=await rn(t.path,Je(e));return _r.decode(n)}var sn=0,on=1;async function Ir(t,e){let n=zr(e)?gt(e,{level:6}):void 0,r=n!==void 0&&n.length<e.length,i=r?n:e,s=new Uint8Array(1+i.length);return s[0]=r?on:sn,s.set(i,1),nn(t.content,t.nonce,s)}function zr(t){if(t.length===0)return!1;if(t.length<=Xt*2)return!0;let e=t.subarray(0,Xt);return gt(e,{level:6}).length<e.length}var Xt=4096;async function ue(t,e){return Promise.all([...e].map(async n=>{let r=await Ir(t,n);return{name:await mt(r),bytes:r}}))}async function an(t,e){let n=await rn(t.content,e);if(n.length===0)throw new Error("sealed chunk carries no marker byte");let r=n[0],i=n.subarray(1);if(r===sn)return i;if(r===on)try{return Zt(i)}catch(s){throw new Error("sealed chunk claims to be deflated and is not",{cause:s})}throw new Error(`sealed chunk has an unknown marker byte ${r}`)}async function mt(t){let e=await ce().digest("SHA-256",ie(t));return pt(new Uint8Array(e))}function ln(t){return Te(t.auth)}function ie(t){return t.byteOffset===0&&t.byteLength===t.buffer.byteLength?t.buffer:t.slice().buffer}function pt(t){let e="";for(let n of t)e+=n.toString(16).padStart(2,"0");return e}var ve="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";function Te(t){let e="";for(let n=0;n<t.length;n+=3){let r=t[n],i=n+1<t.length?t[n+1]:void 0,s=n+2<t.length?t[n+2]:void 0;if(e+=ve[r>>2],e+=ve[(r&3)<<4|(i??0)>>4],i===void 0||(e+=ve[(i&15)<<2|(s??0)>>6],s===void 0))break;e+=ve[s&63]}return e}var Dr=(()=>{let t=new Int16Array(128).fill(-1);for(let e=0;e<ve.length;e++)t[ve.charCodeAt(e)]=e;return t})();function Je(t){let e=t.length,n=new Uint8Array(Math.floor(e*3/4)),r=0,i=0,s=0;for(let o=0;o<e;o++){let a=t.charCodeAt(o),l=a<128?Dr[a]:-1;if(l<0)throw new Error(`invalid base64url character ${JSON.stringify(t[o])} at position ${o}`);i=i<<6|l,s+=6,s>=8&&(s-=8,n[r++]=i>>s&255)}return n.subarray(0,r)}function Ur(t){let e=[t.path,String(t.size),String(t.ctime),String(t.mtime),t.folder?"1":"0",t.deleted?"1":"0",t.prev??"",t.parent,String(t.chunks.length),...t.chunks];return Le.encode(e.map(n=>`${n.length}:${n}`).join(""))}async function yt(t,e){let n=await ce().sign("HMAC",t.meta,ie(Ur(e)));return pt(new Uint8Array(n))}async function hn(t,e,n){let r=await yt(t,e);if(r.length!==n.length)return!1;let i=0;for(let s=0;s<r.length;s++)i|=r.charCodeAt(s)^n.charCodeAt(s);return i===0}async function cn(t){if(t==="")return"";let e=await ce().digest("SHA-256",ie(Le.encode(t)));return pt(new Uint8Array(e))}var ae=48,fe=31,Fr=1;function un(t,e){let n=t>>>0;return Math.floor(n/e)*e===n-Fr}var fn={min:512,avg:1024,max:4096},Or=64,Nr=fn.avg,Br=64*1024;function Lr(t){let e=Math.sqrt(Or*Math.max(t,1)),n=Math.min(Br,Math.max(Nr,Math.round(e/512)*512));return{min:Math.max(fn.min,n/2),avg:n,max:n*4}}var vt={min:128*1024,avg:256*1024,max:1024*1024},Wr=4*1024*1024;function dn(t,e,n=vt.max){(!Number.isFinite(n)||n<=0)&&(n=vt.max);let r=e&&t<Wr?Lr(t):vt,i=Math.min(r.max,n-Qt),s=Math.max(i,ae*4);return{min:Math.min(r.min,s),avg:Math.min(r.avg,s),max:s}}function gn(t,e,n){let r=n-1;for(;r>e&&(t[r]&192)===128;)r--;if(r<e)return n;let i=t[r],s=i<128?1:(i&224)===192?2:(i&240)===224?3:(i&248)===240?4:1;return n-r>=s?n:r>e?r:n}function*bt(t,e,n){let{min:r,avg:i,max:s}=e,o=t.length,a=1;for(let c=0;c<ae-1;c++)a=Math.imul(a,fe);let l=0,h=0;for(let c=0;c<o;c++){let u=t[c];c>=l+ae?(h=h-Math.imul(t[c-ae],a)|0,h=Math.imul(h,fe),h=h+u|0):(h=Math.imul(h,fe),h=h+u|0);let f=c-l+1,g=f>=r&&un(h,i);if(f>=s&&(g=!0),g){let m=n?gn(t,l,c+1):c+1;yield{offset:l,bytes:t.subarray(l,m)},l=m,h=0,c=m-1}}l<o&&(yield{offset:l,bytes:t.subarray(l,o)})}var wt=class{constructor(e,n,r){this.buf=e;this.sizes=n;this.pPowW=r}buf;sizes;pPowW;used=0;hash=0;scanTo(e,n){let{min:r,avg:i,max:s}=this.sizes,o=this.buf,a=this.pPowW,l=this.used,h=this.hash;for(let c=n;c<e.length;c++){let u=e[c];if(o[l++]=u,l>=ae+1?(h=h-Math.imul(o[l-1-ae],a)|0,h=Math.imul(h,fe),h=h+u|0):(h=Math.imul(h,fe),h=h+u|0),l>=s||l>=r&&un(h,i))return this.used=l,this.hash=h,c+1}return this.used=l,this.hash=h,-1}};async function*mn(t,e,n){let{max:r}=e,i=1;for(let u=0;u<ae-1;u++)i=Math.imul(i,fe);let s=new Uint8Array(Math.max(r,ae*2)),o=0,a=0,l=0,h=()=>{let u=n?gn(s,0,o):o,f={offset:l,bytes:s.slice(0,u)};l+=u;let g=o-u;s.copyWithin(0,u,o),o=g,a=0;for(let m=0;m<g;m++)a=Math.imul(a,fe),a=a+s[m]|0;return f},c=new wt(s,e,i);for await(let u of t){let f=0;for(;;){let g=c.scanTo(u,f);if(g<0)break;o=c.used,yield h(),c.used=o,c.hash=a,f=g}o=c.used}if(o>0){let u={offset:l,bytes:s.slice(0,o)};l+=o,o=0,yield u}}var Vr=new Set(["md","txt","canvas","json","csv","yml","yaml","xml","html","css","js","ts","svg","bib","tex"]);function pn(t){let e=t.lastIndexOf(".");return e<0?!1:Vr.has(t.slice(e+1).toLowerCase())}var Jr=new Set(["canvas","json"]);function yn(t){let e=t.lastIndexOf(".");return e<0?!1:Jr.has(t.slice(e+1).toLowerCase())}var kn=gr(vn(),1),Kr=-1,Hr=0,jr=1;function wn(t){let e=[],n=0;for(let[r,i]of t)r===Hr?n+=i.length:r===Kr?(e.push({start:n,end:n+i.length}),n+=i.length):e.push({start:n,end:n});return e}function Gr(t,e){for(let n of t)for(let r of e){let i=n.start===n.end,s=r.start===r.end;if(!(i&&s)){if(i){if(r.start<n.start&&n.start<r.end)return n;continue}if(s){if(n.start<r.start&&r.start<n.end)return r;continue}if(n.start<r.end&&r.start<n.end)return n}}}function Sn(t,e,n,r=()=>!0){if(e===n)return{kind:"take",text:e,why:"both sides already agree"};if(t===e)return{kind:"take",text:n,why:"no local change since the last sync"};if(t===n)return{kind:"take",text:e,why:"no remote change since the last sync"};let i=new kn.diff_match_patch,s=i.diff_main(t,e,!0,0);s.length>2&&(i.diff_cleanupSemantic(s),i.diff_cleanupEfficiency(s));let o=i.diff_main(t,n,!0,0);o.length>2&&(i.diff_cleanupSemantic(o),i.diff_cleanupEfficiency(o));let a=Gr(wn(s),wn(o));if(a!==void 0)return{kind:"conflict",why:`both devices changed the same text, at characters ${a.start} to ${a.end} of the last synced version`};let l=bn(i,t,s,n),h=bn(i,t,o,e);if(l.failed>0||h.failed>0){let u=Math.max(l.failed,h.failed),f=Math.max(l.total,h.total);return{kind:"conflict",why:`${u} of ${f} changes could not be placed in the other version`}}if(!qr(l.text,h.text))return{kind:"conflict",why:"merging the two versions in either order gives different content, so at least one change was placed wrongly"};let c=Zr(s,l.text);return c!==void 0?{kind:"conflict",why:`the merge reported success but ${Xr(c)} is not in the result`}:r(l.text)?{kind:"merged",text:l.text}:{kind:"conflict",why:"both sides merged cleanly and the result is no longer a valid file of its kind"}}function qr(t,e){if(t===e)return!0;let n=t.split(`
6
+ `}return n.join("").replace(/%20/g," ")};we.exports=d;we.exports.diff_match_patch=d;we.exports.DIFF_DELETE=T;we.exports.DIFF_INSERT=_;we.exports.DIFF_EQUAL=k});import{hostname as Di}from"node:os";import{resolve as Ui}from"node:path";import{createRequire as pr}from"module";var yr=pr("/"),ye,vr,wr;try{ye=yr("worker_threads"),vr=ye.Worker,wr=ye.isMarkedAsUntransferable}catch{}var N=Uint8Array,J=Uint16Array,ft=Int32Array,Oe=new N([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),Ne=new N([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),lt=new N([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),Ht=function(t,e){for(var n=new J(31),r=0;r<31;++r)n[r]=e+=1<<t[r-1];for(var i=new ft(n[30]),r=1;r<30;++r)for(var s=n[r];s<n[r+1];++s)i[s]=s-n[r]<<5|r;return{b:n,r:i}},ye=Ht(Oe,2),Jt=ye.b,ht=ye.r;Jt[28]=258,ht[258]=28;var Kt=Ht(Ne,0),br=Kt.b,Bt=Kt.r,ct=new J(32768);for(C=0;C<32768;++C)te=(C&43690)>>1|(C&21845)<<1,te=(te&52428)>>2|(te&13107)<<2,te=(te&61680)>>4|(te&3855)<<4,ct[C]=((te&65280)>>8|(te&255)<<8)>>1;var te,C,Q=(function(t,e,n){for(var r=t.length,i=0,s=new J(e);i<r;++i)t[i]&&++s[t[i]-1];var o=new J(e);for(i=1;i<e;++i)o[i]=o[i-1]+s[i-1]<<1;var a;if(n){a=new J(1<<e);var l=15-e;for(i=0;i<r;++i)if(t[i])for(var h=i<<4|t[i],c=e-t[i],u=o[t[i]-1]++<<c,f=u|(1<<c)-1;u<=f;++u)a[ct[u]>>l]=h}else for(a=new J(r),i=0;i<r;++i)t[i]&&(a[i]=ct[o[t[i]-1]++]>>15-t[i]);return a}),oe=new N(288);for(C=0;C<144;++C)oe[C]=8;var C;for(C=144;C<256;++C)oe[C]=9;var C;for(C=256;C<280;++C)oe[C]=7;var C;for(C=280;C<288;++C)oe[C]=8;var C,Me=new N(32);for(C=0;C<32;++C)Me[C]=5;var C,kr=Q(oe,9,0),Sr=Q(oe,9,1),Er=Q(Me,5,0),Ar=Q(Me,5,1),st=function(t){for(var e=t[0],n=1;n<t.length;++n)t[n]>e&&(e=t[n]);return e},Z=function(t,e,n){var r=e/8|0;return(t[r]|t[r+1]<<8)>>(e&7)&n},ot=function(t,e){var n=e/8|0;return(t[n]|t[n+1]<<8|t[n+2]<<16)>>(e&7)},dt=function(t){return(t+7)/8|0},jt=function(t,e,n){return(e==null||e<0)&&(e=0),(n==null||n>t.length)&&(n=t.length),new N(t.subarray(e,n))};var xr=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],re=function(t,e,n){var r=new Error(e||xr[t]);if(r.code=t,Error.captureStackTrace&&Error.captureStackTrace(r,re),!n)throw r;return r},Pr=function(t,e,n,r){var i=t.length,s=r?r.length:0;if(!i||e.f&&!e.l)return n||new N(0);var o=!n,a=o||e.i!=2,l=e.i;o&&(n=new N(i*3));var h=function(Ae){var xe=n.length;if(Ae>xe){var pe=new N(Math.max(xe*2,Ae));pe.set(n),n=pe}},c=e.f||0,u=e.p||0,f=e.b||0,g=e.l,m=e.d,p=e.m,x=e.n,w=i*8;do{if(!g){c=Z(t,u,1);var A=Z(t,u+1,3);if(u+=3,A)if(A==1)g=Sr,m=Ar,p=9,x=5;else if(A==2){var $=Z(t,u,31)+257,E=Z(t,u+10,15)+4,v=$+Z(t,u+5,31)+1;u+=14;for(var y=new N(v),D=new N(19),z=0;z<E;++z)D[lt[z]]=Z(t,u+z*3,7);u+=E*3;for(var O=st(D),se=(1<<O)-1,L=Q(D,O,1),z=0;z<v;){var W=L[Z(t,u,se)];u+=W&15;var b=W>>4;if(b<16)y[z++]=b;else{var U=0,R=0;for(b==16?(R=3+Z(t,u,3),u+=2,U=y[z-1]):b==17?(R=3+Z(t,u,7),u+=3):b==18&&(R=11+Z(t,u,127),u+=7);R--;)y[z++]=U}}var B=y.subarray(0,$),F=y.subarray($);p=st(B),x=st(F),g=Q(B,p,1),m=Q(F,x,1)}else re(1);else{var b=dt(u)+4,S=t[b-4]|t[b-3]<<8,M=b+S;if(M>i){l&&re(0);break}a&&h(f+S),n.set(t.subarray(b,M),f),e.b=f+=S,e.p=u=M*8,e.f=c;continue}if(u>w){l&&re(0);break}}a&&h(f+131072);for(var Ee=(1<<p)-1,G=(1<<x)-1,ee=u;;ee=u){var U=g[ot(t,u)&Ee],V=U>>4;if(u+=U&15,u>w){l&&re(0);break}if(U||re(2),V<256)n[f++]=V;else if(V==256){ee=u,g=null;break}else{var H=V-254;if(V>264){var z=V-257,I=Oe[z];H=Z(t,u,(1<<I)-1)+Jt[z],u+=I}var Y=m[ot(t,u)&G],ge=Y>>4;Y||re(3),u+=Y&15;var F=br[ge];if(ge>3){var I=Ne[ge];F+=ot(t,u)&(1<<I)-1,u+=I}if(u>w){l&&re(0);break}a&&h(f+131072);var me=f+H;if(f<F){var Ue=s-F,Fe=Math.min(F,me);for(Ue+f<0&&re(3);f<Fe;++f)n[f]=r[Ue+f]}for(;f<me;++f)n[f]=n[f-F]}}e.l=g,e.p=ee,e.b=f,e.f=c,g&&(c=1,e.m=p,e.d=m,e.n=x)}while(!c);return f!=n.length&&o?jt(n,0,f):n.subarray(0,f)},ne=function(t,e,n){n<<=e&7;var r=e/8|0;t[r]|=n,t[r+1]|=n>>8},Pe=function(t,e,n){n<<=e&7;var r=e/8|0;t[r]|=n,t[r+1]|=n>>8,t[r+2]|=n>>16},at=function(t,e){for(var n=[],r=0;r<t.length;++r)t[r]&&n.push({s:r,f:t[r]});var i=n.length,s=n.slice();if(!i)return{t:qt,l:0};if(i==1){var o=new N(n[0].s+1);return o[n[0].s]=1,{t:o,l:1}}n.sort(function(M,$){return M.f-$.f}),n.push({s:-1,f:25001});var a=n[0],l=n[1],h=0,c=1,u=2;for(n[0]={s:-1,f:a.f+l.f,l:a,r:l};c!=i-1;)a=n[n[h].f<n[u].f?h++:u++],l=n[h!=c&&n[h].f<n[u].f?h++:u++],n[c++]={s:-1,f:a.f+l.f,l:a,r:l};for(var f=s[0].s,r=1;r<i;++r)s[r].s>f&&(f=s[r].s);var g=new J(f+1),m=ut(n[c-1],g,0);if(m>e){var r=0,p=0,x=m-e,w=1<<x;for(s.sort(function($,E){return g[E.s]-g[$.s]||$.f-E.f});r<i;++r){var A=s[r].s;if(g[A]>e)p+=w-(1<<m-g[A]),g[A]=e;else break}for(p>>=x;p>0;){var b=s[r].s;g[b]<e?p-=1<<e-g[b]++-1:++r}for(;r>=0&&p;--r){var S=s[r].s;g[S]==e&&(--g[S],++p)}m=e}return{t:new N(g),l:m}},ut=function(t,e,n){return t.s==-1?Math.max(ut(t.l,e,n+1),ut(t.r,e,n+1)):e[t.s]=n},Lt=function(t){for(var e=t.length;e&&!t[--e];);for(var n=new J(++e),r=0,i=t[0],s=1,o=function(l){n[r++]=l},a=1;a<=e;++a)if(t[a]==i&&a!=e)++s;else{if(!i&&s>2){for(;s>138;s-=138)o(32754);s>2&&(o(s>10?s-11<<5|28690:s-3<<5|12305),s=0)}else if(s>3){for(o(i),--s;s>6;s-=6)o(8304);s>2&&(o(s-3<<5|8208),s=0)}for(;s--;)o(i);s=1,i=t[a]}return{c:n.subarray(0,r),n:e}},$e=function(t,e){for(var n=0,r=0;r<e.length;++r)n+=t[r]*e[r];return n},Gt=function(t,e,n){var r=n.length,i=dt(e+2);t[i]=r&255,t[i+1]=r>>8,t[i+2]=t[i]^255,t[i+3]=t[i+1]^255;for(var s=0;s<r;++s)t[i+s+4]=n[s];return(i+4+r)*8},Vt=function(t,e,n,r,i,s,o,a,l,h,c){ne(e,c++,n),++i[256];for(var u=at(i,15),f=u.t,g=u.l,m=at(s,15),p=m.t,x=m.l,w=Lt(f),A=w.c,b=w.n,S=Lt(p),M=S.c,$=S.n,E=new J(19),v=0;v<A.length;++v)++E[A[v]&31];for(var v=0;v<M.length;++v)++E[M[v]&31];for(var y=at(E,7),D=y.t,z=y.l,O=19;O>4&&!D[lt[O-1]];--O);var se=h+5<<3,L=$e(i,oe)+$e(s,Me)+o,W=$e(i,f)+$e(s,p)+o+14+3*O+$e(E,D)+2*E[16]+3*E[17]+7*E[18];if(l>=0&&se<=L&&se<=W)return Gt(e,c,t.subarray(l,l+h));var U,R,B,F;if(ne(e,c,1+(W<L)),c+=2,W<L){U=Q(f,g,0),R=f,B=Q(p,x,0),F=p;var Ee=Q(D,z,0);ne(e,c,b-257),ne(e,c+5,$-1),ne(e,c+10,O-4),c+=14;for(var v=0;v<O;++v)ne(e,c+3*v,D[lt[v]]);c+=3*O;for(var G=[A,M],ee=0;ee<2;++ee)for(var V=G[ee],v=0;v<V.length;++v){var H=V[v]&31;ne(e,c,Ee[H]),c+=D[H],H>15&&(ne(e,c,V[v]>>5&127),c+=V[v]>>12)}}else U=kr,R=oe,B=Er,F=Me;for(var v=0;v<a;++v){var I=r[v];if(I>255){var H=I>>18&31;Pe(e,c,U[H+257]),c+=R[H+257],H>7&&(ne(e,c,I>>23&31),c+=Oe[H]);var Y=I&31;Pe(e,c,B[Y]),c+=F[Y],Y>3&&(Pe(e,c,I>>5&8191),c+=Ne[Y])}else Pe(e,c,U[I]),c+=R[I]}return Pe(e,c,U[256]),c+R[256]},$r=new ft([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),qt=new N(0),Mr=function(t,e,n,r,i,s){var o=s.z||t.length,a=new N(r+o+5*(1+Math.ceil(o/7e3))+i),l=a.subarray(r,a.length-i),h=s.l,c=(s.r||0)&7;if(e){c&&(l[0]=s.r>>3);for(var u=$r[e-1],f=u>>13,g=u&8191,m=(1<<n)-1,p=s.p||new J(32768),x=s.h||new J(m+1),w=Math.ceil(n/3),A=2*w,b=function(it){return(t[it]^t[it+1]<<w^t[it+2]<<A)&m},S=new ft(25e3),M=new J(288),$=new J(32),E=0,v=0,y=s.i||0,D=0,z=s.w||0,O=0;y+2<o;++y){var se=b(y),L=y&32767,W=x[se];if(p[L]=W,x[se]=L,z<=y){var U=o-y;if((E>7e3||D>24576)&&(U>423||!h)){c=Vt(t,l,0,S,M,$,v,D,O,y-O,c),D=E=v=0,O=y;for(var R=0;R<286;++R)M[R]=0;for(var R=0;R<30;++R)$[R]=0}var B=2,F=0,Ee=g,G=L-W&32767;if(U>2&&se==b(y-G))for(var ee=Math.min(f,U)-1,V=Math.min(32767,y),H=Math.min(258,U);G<=V&&--Ee&&L!=W;){if(t[y+B]==t[y+B-G]){for(var I=0;I<H&&t[y+I]==t[y+I-G];++I);if(I>B){if(B=I,F=G,I>ee)break;for(var Y=Math.min(G,I-2),ge=0,R=0;R<Y;++R){var me=y-G+R&32767,Ue=p[me],Fe=me-Ue&32767;Fe>ge&&(ge=Fe,W=me)}}}L=W,W=p[L],G+=L-W&32767}if(F){S[D++]=268435456|ht[B]<<18|Bt[F];var Ae=ht[B]&31,xe=Bt[F]&31;v+=Oe[Ae]+Ne[xe],++M[257+Ae],++$[xe],z=y+B,++E}else S[D++]=t[y],++M[t[y]]}}for(y=Math.max(y,z);y<o;++y)S[D++]=t[y],++M[t[y]];c=Vt(t,l,h,S,M,$,v,D,O,y-O,c),h||(s.r=c&7|l[c/8|0]<<3,c-=7,s.h=x,s.p=p,s.i=y,s.w=z)}else{for(var y=s.w||0;y<o+h;y+=65535){var pe=y+65535;pe>=o&&(l[c/8|0]=h,pe=o),c=Gt(l,c+1,t.subarray(y,pe))}s.i=o}return jt(a,0,r+dt(c)+i)};var Cr=function(t,e,n,r,i){if(!i&&(i={l:1},e.dictionary)){var s=e.dictionary.subarray(-32768),o=new N(s.length+t.length);o.set(s),o.set(t,s.length),t=o,i.w=s.length}return Mr(t,e.level==null?6:e.level,e.mem==null?i.l?Math.ceil(Math.max(8,Math.min(13,Math.log(t.length)))*1.5):20:12+e.mem,n,r,i)};function gt(t,e){return Cr(t,e||{},0,0)}function Zt(t,e){return Pr(t,{i:2},e&&e.out,e&&e.dictionary)}var Tr=typeof TextDecoder<"u"&&new TextDecoder,_r=0;try{Tr.decode(qt,{stream:!0}),_r=1}catch{}var Yt="basalt/hkdf-aes-gcm/1",he=12,We=128,Qt=he+We/8+1,X=20,Be=new TextEncoder,Rr=new TextDecoder,Ce={auth:"basalt/auth/1",path:"basalt/path/1",content:"basalt/content/1",nonce:"basalt/nonce/1",meta:"basalt/meta/1"};function ce(){let t=globalThis.crypto;if(!t?.subtle)throw new Error("WebCrypto is unavailable, so this vault cannot be opened");return t.subtle}function en(){let t=globalThis.crypto;if(!t?.getRandomValues)throw new Error("no secure random source is available, so a vault cannot be created here");return t.getRandomValues(new Uint8Array(X))}async function tn(t){if(t.length<16)throw new Error(`root secret is ${t.length} bytes, need at least 16`);let e=ce(),n=await e.importKey("raw",ie(t),"HKDF",!1,["deriveKey","deriveBits"]),r=h=>({name:"HKDF",hash:"SHA-256",salt:new Uint8Array(0),info:Be.encode(h)}),[i,s,o,a,l]=await Promise.all([e.deriveBits(r(Ce.auth),n,256),e.deriveKey(r(Ce.path),n,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"]),e.deriveKey(r(Ce.content),n,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"]),e.deriveKey(r(Ce.nonce),n,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),e.deriveKey(r(Ce.meta),n,{name:"HMAC",hash:"SHA-256"},!1,["sign"])]);return{auth:new Uint8Array(i),path:s,content:o,nonce:a,meta:l}}async function nn(t,e,n){let r=ce(),i=await Ir(e,n),s=await r.encrypt({name:"AES-GCM",iv:ie(i),tagLength:We},t,ie(n)),o=new Uint8Array(he+s.byteLength);return o.set(i,0),o.set(new Uint8Array(s),he),o}async function rn(t,e){if(e.length<he+We/8)throw new Error(`sealed value is ${e.length} bytes, too short to contain a nonce and a tag`);let n=ce(),r=e.subarray(0,he),i=e.subarray(he);try{let s=await n.decrypt({name:"AES-GCM",iv:ie(r),tagLength:We},t,ie(i));return new Uint8Array(s)}catch(s){throw new Error("sealed value failed authentication, so it is not what was stored",{cause:s})}}async function Ir(t,e){let n=await ce().sign("HMAC",t,ie(e));return new Uint8Array(n,0,he)}async function Le(t,e){return Te(await nn(t.path,t.nonce,Be.encode(e)))}async function Ve(t,e){let n=await rn(t.path,He(e));return Rr.decode(n)}var sn=0,on=1;async function zr(t,e){let n=Dr(e)?gt(e,{level:6}):void 0,r=n!==void 0&&n.length<e.length,i=r?n:e,s=new Uint8Array(1+i.length);return s[0]=r?on:sn,s.set(i,1),nn(t.content,t.nonce,s)}function Dr(t){if(t.length===0)return!1;if(t.length<=Xt*2)return!0;let e=t.subarray(0,Xt);return gt(e,{level:6}).length<e.length}var Xt=4096;async function ue(t,e){return Promise.all([...e].map(async n=>{let r=await zr(t,n);return{name:await mt(r),bytes:r}}))}async function an(t,e){let n=await rn(t.content,e);if(n.length===0)throw new Error("sealed chunk carries no marker byte");let r=n[0],i=n.subarray(1);if(r===sn)return i;if(r===on)try{return Zt(i)}catch(s){throw new Error("sealed chunk claims to be deflated and is not",{cause:s})}throw new Error(`sealed chunk has an unknown marker byte ${r}`)}async function mt(t){let e=await ce().digest("SHA-256",ie(t));return pt(new Uint8Array(e))}function ln(t){return Te(t.auth)}function ie(t){return t.byteOffset===0&&t.byteLength===t.buffer.byteLength?t.buffer:t.slice().buffer}function pt(t){let e="";for(let n of t)e+=n.toString(16).padStart(2,"0");return e}var ve="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";function Te(t){let e="";for(let n=0;n<t.length;n+=3){let r=t[n],i=n+1<t.length?t[n+1]:void 0,s=n+2<t.length?t[n+2]:void 0;if(e+=ve[r>>2],e+=ve[(r&3)<<4|(i??0)>>4],i===void 0||(e+=ve[(i&15)<<2|(s??0)>>6],s===void 0))break;e+=ve[s&63]}return e}var Ur=(()=>{let t=new Int16Array(128).fill(-1);for(let e=0;e<ve.length;e++)t[ve.charCodeAt(e)]=e;return t})();function He(t){let e=t.length,n=new Uint8Array(Math.floor(e*3/4)),r=0,i=0,s=0;for(let o=0;o<e;o++){let a=t.charCodeAt(o),l=a<128?Ur[a]:-1;if(l<0)throw new Error(`invalid base64url character ${JSON.stringify(t[o])} at position ${o}`);i=i<<6|l,s+=6,s>=8&&(s-=8,n[r++]=i>>s&255)}return n.subarray(0,r)}function Fr(t){let e=[t.path,String(t.size),String(t.ctime),String(t.mtime),t.folder?"1":"0",t.deleted?"1":"0",t.prev??"",t.parent,String(t.chunks.length),...t.chunks];return Be.encode(e.map(n=>`${n.length}:${n}`).join(""))}async function yt(t,e){let n=await ce().sign("HMAC",t.meta,ie(Fr(e)));return pt(new Uint8Array(n))}async function hn(t,e,n){let r=await yt(t,e);if(r.length!==n.length)return!1;let i=0;for(let s=0;s<r.length;s++)i|=r.charCodeAt(s)^n.charCodeAt(s);return i===0}async function cn(t){if(t==="")return"";let e=await ce().digest("SHA-256",ie(Be.encode(t)));return pt(new Uint8Array(e))}var ae=48,fe=31,Or=1;function un(t,e){let n=t>>>0;return Math.floor(n/e)*e===n-Or}var fn={min:512,avg:1024,max:4096},Nr=64,Wr=fn.avg,Br=64*1024;function Lr(t){let e=Math.sqrt(Nr*Math.max(t,1)),n=Math.min(Br,Math.max(Wr,Math.round(e/512)*512));return{min:Math.max(fn.min,n/2),avg:n,max:n*4}}var vt={min:128*1024,avg:256*1024,max:1024*1024},Vr=4*1024*1024;function dn(t,e,n=vt.max){(!Number.isFinite(n)||n<=0)&&(n=vt.max);let r=e&&t<Vr?Lr(t):vt,i=Math.min(r.max,n-Qt),s=Math.max(i,ae*4);return{min:Math.min(r.min,s),avg:Math.min(r.avg,s),max:s}}function gn(t,e,n){let r=n-1;for(;r>e&&(t[r]&192)===128;)r--;if(r<e)return n;let i=t[r],s=i<128?1:(i&224)===192?2:(i&240)===224?3:(i&248)===240?4:1;return n-r>=s?n:r>e?r:n}function*bt(t,e,n){let{min:r,avg:i,max:s}=e,o=t.length,a=1;for(let c=0;c<ae-1;c++)a=Math.imul(a,fe);let l=0,h=0;for(let c=0;c<o;c++){let u=t[c];c>=l+ae?(h=h-Math.imul(t[c-ae],a)|0,h=Math.imul(h,fe),h=h+u|0):(h=Math.imul(h,fe),h=h+u|0);let f=c-l+1,g=f>=r&&un(h,i);if(f>=s&&(g=!0),g){let m=n?gn(t,l,c+1):c+1;yield{offset:l,bytes:t.subarray(l,m)},l=m,h=0,c=m-1}}l<o&&(yield{offset:l,bytes:t.subarray(l,o)})}var wt=class{constructor(e,n,r){this.buf=e;this.sizes=n;this.pPowW=r}buf;sizes;pPowW;used=0;hash=0;scanTo(e,n){let{min:r,avg:i,max:s}=this.sizes,o=this.buf,a=this.pPowW,l=this.used,h=this.hash;for(let c=n;c<e.length;c++){let u=e[c];if(o[l++]=u,l>=ae+1?(h=h-Math.imul(o[l-1-ae],a)|0,h=Math.imul(h,fe),h=h+u|0):(h=Math.imul(h,fe),h=h+u|0),l>=s||l>=r&&un(h,i))return this.used=l,this.hash=h,c+1}return this.used=l,this.hash=h,-1}};async function*mn(t,e,n){let{max:r}=e,i=1;for(let u=0;u<ae-1;u++)i=Math.imul(i,fe);let s=new Uint8Array(Math.max(r,ae*2)),o=0,a=0,l=0,h=()=>{let u=n?gn(s,0,o):o,f={offset:l,bytes:s.slice(0,u)};l+=u;let g=o-u;s.copyWithin(0,u,o),o=g,a=0;for(let m=0;m<g;m++)a=Math.imul(a,fe),a=a+s[m]|0;return f},c=new wt(s,e,i);for await(let u of t){let f=0;for(;;){let g=c.scanTo(u,f);if(g<0)break;o=c.used,yield h(),c.used=o,c.hash=a,f=g}o=c.used}if(o>0){let u={offset:l,bytes:s.slice(0,o)};l+=o,o=0,yield u}}var Hr=new Set(["md","txt","canvas","json","csv","yml","yaml","xml","html","css","js","ts","svg","bib","tex"]);function pn(t){let e=t.lastIndexOf(".");return e<0?!1:Hr.has(t.slice(e+1).toLowerCase())}var Jr=new Set(["canvas","json"]);function yn(t){let e=t.lastIndexOf(".");return e<0?!1:Jr.has(t.slice(e+1).toLowerCase())}var kn=mr(vn(),1),Kr=-1,jr=0,Gr=1;function wn(t){let e=[],n=0;for(let[r,i]of t)r===jr?n+=i.length:r===Kr?(e.push({start:n,end:n+i.length}),n+=i.length):e.push({start:n,end:n});return e}function qr(t,e){for(let n of t)for(let r of e){let i=n.start===n.end,s=r.start===r.end;if(!(i&&s)){if(i){if(r.start<n.start&&n.start<r.end)return n;continue}if(s){if(n.start<r.start&&r.start<n.end)return r;continue}if(n.start<r.end&&r.start<n.end)return n}}}function Sn(t,e,n,r=()=>!0){if(e===n)return{kind:"take",text:e,why:"both sides already agree"};if(t===e)return{kind:"take",text:n,why:"no local change since the last sync"};if(t===n)return{kind:"take",text:e,why:"no remote change since the last sync"};let i=new kn.diff_match_patch,s=i.diff_main(t,e,!0,0);s.length>2&&(i.diff_cleanupSemantic(s),i.diff_cleanupEfficiency(s));let o=i.diff_main(t,n,!0,0);o.length>2&&(i.diff_cleanupSemantic(o),i.diff_cleanupEfficiency(o));let a=qr(wn(s),wn(o));if(a!==void 0)return{kind:"conflict",why:`both devices changed the same text, at characters ${a.start} to ${a.end} of the last synced version`};let l=bn(i,t,s,n),h=bn(i,t,o,e);if(l.failed>0||h.failed>0){let u=Math.max(l.failed,h.failed),f=Math.max(l.total,h.total);return{kind:"conflict",why:`${u} of ${f} changes could not be placed in the other version`}}if(!Zr(l.text,h.text))return{kind:"conflict",why:"merging the two versions in either order gives different content, so at least one change was placed wrongly"};let c=Xr(s,l.text);return c!==void 0?{kind:"conflict",why:`the merge reported success but ${Yr(c)} is not in the result`}:r(l.text)?{kind:"merged",text:l.text}:{kind:"conflict",why:"both sides merged cleanly and the result is no longer a valid file of its kind"}}function Zr(t,e){if(t===e)return!0;let n=t.split(`
7
7
  `).sort(),r=e.split(`
8
- `).sort();if(n.length!==r.length)return!1;for(let i=0;i<n.length;i++)if(n[i]!==r[i])return!1;return!0}function bn(t,e,n,r){let[i,s]=t.patch_apply(t.patch_make(e,n),r);return{text:i,failed:s.filter(o=>!o).length,total:s.length}}function Zr(t,e){for(let[n,r]of t)if(n===jr&&!e.includes(r))return r}function Xr(t){let e=t.replace(/\s+/g," ").trim(),n=e.length>60?`${e.slice(0,57)}...`:e;return`a local edit (${JSON.stringify(n)})`}function En(t,e,n){let r=t.lastIndexOf("."),i=t.lastIndexOf("/"),s=r>i,o=s?t.slice(0,r):t,a=s?t.slice(r):"",l=(c,u=2)=>String(c).padStart(u,"0"),h=`${n.getFullYear()}${l(n.getMonth()+1)}${l(n.getDate())}${l(n.getHours())}${l(n.getMinutes())}`;return`${o} (Conflicted copy ${Yr(e)} ${h})${a}`}function Yr(t){return t.replace(/[-\\/:*?"<>|\s]/g,"-").replace(/-{2,}/g,"-").replace(/^[.\-\s]+|[.\-\s]+$/g,"").slice(0,32)||"device"}function kt(t){return{path:t,prev:"",folder:!1,ctime:0,mtime:0,size:0,hash:"",chunks:[],synchash:"",syncuid:0,synctime:0}}function An(t){let{local:e,remote:n,index:r,mergeable:i}=t,s=r.synchash;return e?.folder||n?.folder?Qr(e,n):e===void 0&&(n===void 0||n.deleted)?{kind:"nothing",why:"absent on both sides"}:n===void 0?{kind:"upload",why:"new file, the server has never held this path"}:e===void 0?ei(n,r):n.deleted?e.hash===s&&s!==""?{kind:"deleteLocal",why:"deleted on another device and unchanged here"}:s===""?{kind:"upload",why:"deleted on the server but never synced from here"}:{kind:"upload",why:"deleted on another device but edited here, so the edit is kept and re-sent"}:e.hash===n.hash?{kind:"nothing",why:"the two sides hold the same content"}:s===""?{kind:"conflict",why:"both sides have content and there is no last-synced version to merge from"}:e.hash===s?{kind:"download",why:"changed on another device and unchanged here"}:n.hash===s?{kind:"upload",why:"changed here and unchanged on the server"}:i?{kind:"merge",why:"changed on both sides since the last sync"}:{kind:"conflict",why:"changed on both sides and this file cannot be merged"}}function Qr(t,e){return t!==void 0&&t.folder?e===void 0?{kind:"upload",why:"new folder, the server has never held this path"}:e.deleted?{kind:"nothing",why:"folder deleted elsewhere; its files decide"}:e.folder?{kind:"nothing",why:"folder exists on both sides"}:{kind:"clash",why:"a folder here and a file of the same name on another device"}:e?.folder&&!e.deleted?t!==void 0?{kind:"clash",why:"a file here and a folder of the same name on another device"}:{kind:"createLocalFolder",why:"folder exists on the server and not here"}:{kind:"nothing",why:"folder absent on both sides"}}function ei(t,e){let n=e.synchash;return t.deleted?{kind:"nothing",why:"deleted on both sides"}:n===""?{kind:"download",why:"new on the server"}:t.hash===n?{kind:"deleteRemote",why:"deleted here and unchanged on the server"}:{kind:"restoreLocal",why:"deleted here but changed on another device, so the newer content is restored"}}function St(t,e,n){return t.hash===""||t.chunks.length===0?!0:t.mtime!==e||t.size!==n}function be(t,e){let n=Math.ceil(e.mtime),r=Math.ceil(e.ctime);if(e.folder){t.folder=!0,t.mtime=0,t.ctime=0,t.size=0,t.hash="",t.chunks=[];return}St(t,n,ti(e))&&(t.hash="",t.chunks=[]),t.folder=!1,t.mtime=n,t.ctime=r,t.size=e.size}function ti(t){return t.size}function xn(t,e,n){t.path=n,t.prev===""&&(t.prev=e),t.prev===n&&(t.prev=""),t.synctime=0}function de(t,e,n,r,i){t.hash=e,t.chunks=[...n],t.synchash=e,t.syncuid=r,t.synctime=i,t.prev=""}function Pn(t,e){if(!t.synctime)return!0;let n=t.size>102400?30:t.size>10240?20:10;return e-t.synctime>n*1e3}var Et=2,$n=6e4,_e=256;function Mn(t){return{size:t.size,ctime:t.ctime,mtime:t.mtime,folder:t.folder??!1,deleted:t.deleted??!1,...t.prev?{prev:t.prev}:{}}}var P=class extends Error{constructor(n,r){super(r);this.code=n;this.name="ProtocolError"}code;get fatal(){return["proto","auth","cursor","busy","protostate","badchunk","internal"].includes(this.code)}};function ni(t){return t.startsWith("wss://")?`. If that server has no TLS in front of it, pair with ws://${t.slice(6)} instead`:""}var j=class extends Error{constructor(e){super(e),this.name="ConnectionError"}},Ke=class{constructor(e,n){this.url=e;this.opts=n}url;opts;socket;replyWaiter;replyTimer;bodyWaiter;bodyQueue=[];expecting=0;requestsSent=0;closed=!1;closeReason;cursor=0;notifying=Promise.resolve();log(e,...n){this.opts.log?.(e,...n)}get appliedCursor(){return this.cursor}async connect(){if(this.socket)throw new Error("already connected");let n=(this.opts.socketFactory??ri)(this.url);n.binaryType="arraybuffer",this.socket=n,await new Promise((r,i)=>{n.onopen=()=>r(),n.onerror=()=>i(new j(`could not connect to ${this.url}${ni(this.url)}`)),n.onclose=s=>i(new j(`connection closed before opening: ${Cn(s)}`))}),n.onerror=()=>this.die(new j("the connection failed")),n.onclose=r=>this.die(new j(`the connection closed: ${Cn(r)}`)),n.onmessage=r=>this.onFrame(r.data)}die(e){if(this.closed)return;this.closed=!0,this.closeReason=e,this.log("transport closed",e.message),this.disarmReply();let n=this.replyWaiter,r=this.bodyWaiter;this.replyWaiter=void 0,this.bodyWaiter=void 0,n?.reject(e),r?.reject(e);try{this.socket?.close()}catch{}try{this.opts.onClosed?.(e)}catch{}}disarmReply(){this.replyTimer!==void 0&&(clearTimeout(this.replyTimer),this.replyTimer=void 0)}close(){this.die(new j("closed by this device"))}get isClosed(){return this.closed}onFrame(e){if(typeof e=="string"){let i;try{i=JSON.parse(e)}catch{this.die(new P("protostate",`server sent a frame that is not JSON: ${e.slice(0,120)}`));return}this.onTextFrame(i);return}let n=ii(e);if(n===void 0){this.die(new P("protostate","server sent a frame of an unexpected type"));return}let r=this.bodyWaiter;if(r){this.bodyWaiter=void 0,r.resolve(n);return}if(this.bodyQueue.length>=this.expecting){this.die(new P("protostate",`server sent a ${n.length} byte body with nothing outstanding to receive it`));return}this.bodyQueue.push(n)}onTextFrame(e){if(e.op==="batch"){this.queueNotification(()=>this.onBatchFrame(e));return}if(e.op==="caught-up"){let r=H(e.cursor);this.queueNotification(async()=>{if(r!==this.cursor){this.die(new P("protostate",`server says caught up at ${r}, this device reached ${this.cursor}`));return}this.opts.onCaughtUp?.(r)});return}let n=this.replyWaiter;if(!n){this.die(new P("protostate",`server sent an unexpected reply: ${JSON.stringify(e)}`));return}this.replyWaiter=void 0,n.resolve(e)}queueNotification(e){this.notifying=this.notifying.then(e).catch(n=>{this.die(n instanceof Error?n:new Error(String(n)))})}async onBatchFrame(e){let n=H(e.from),r=H(e.to),i=e.entries;if(!Array.isArray(i))throw new P("protostate",`batch ${n} to ${r} carries no entries array, so an empty batch cannot be told from a lost one`);let s=i;if(n!==this.cursor+1)throw new P("protostate",`batch covers ${n} to ${r} but this device has applied up to ${this.cursor}, so something was skipped`);if(r<n)throw new P("protostate",`batch covers an empty range, ${n} to ${r}`);for(let o of s){if(typeof o?.uid!="number"||!Number.isFinite(o.uid))throw new P("protostate",`batch ${n}..${r} contains an entry with no uid`);if(typeof o.path!="string"||o.path==="")throw new P("protostate",`batch ${n}..${r} contains uid ${o.uid} with no path`);if(!Array.isArray(o.chunks))throw new P("protostate",`batch ${n}..${r} contains uid ${o.uid} with no chunks array`);if(o.uid<n||o.uid>r)throw new P("protostate",`batch ${n}..${r} contains uid ${o.uid}`)}await this.opts.onBatch({from:n,to:r,entries:s}),this.cursor=r}send(e){if(this.closed||!this.socket)throw this.closeReason??new j("not connected");this.socket.send(JSON.stringify(e))}sendBody(e){if(this.closed||!this.socket)throw this.closeReason??new j("not connected");this.socket.send(e)}async request(e){if(this.replyWaiter)throw new Error("a request is already in flight");this.requestsSent++;let n=this.opts.timeoutMs??$n,r=await new Promise((i,s)=>{let o=setTimeout(()=>{this.die(new j(`no reply within ${n}ms`))},n);this.replyTimer=o,this.replyWaiter={resolve:a=>{this.disarmReply(),i(a)},reject:a=>{this.disarmReply(),s(a)}};try{this.send(e)}catch(a){this.disarmReply(),this.replyWaiter=void 0,s(a instanceof Error?a:new Error(String(a)))}});if(r.res==="err"){let i=new P(String(r.code??"unknown"),String(r.msg??"no message"));throw i.fatal&&this.die(i),i}return r}async body(){let e=this.bodyQueue.shift();if(e)return e;if(this.closed)throw this.closeReason??new j("not connected");return new Promise((n,r)=>{this.bodyWaiter={resolve:n,reject:r}})}async hello(e){this.cursor=e.cursor;let n=await this.request({op:"hello",proto:Et,crypto:Yt,vault:e.vault,token:e.token,device:e.device,cursor:e.cursor,...e.claim!==void 0?{claim:e.claim}:{}});if(n.res!=="ready")throw new P("protostate",`expected ready, got ${JSON.stringify(n)}`);let r={proto:H(n.proto),cursor:H(n.cursor),perFileMax:H(n.perFileMax),chunkMax:H(n.chunkMax),maxChunks:H(n.maxChunks)};if(r.proto!==Et)throw new P("proto",`server speaks protocol ${r.proto}, this client speaks ${Et}`);return this.log("ready",r),r}async put(e,n,r,i,s={mac:"",parent:""}){let o=await this.request({op:"put",path:e,meta:Mn(n),chunks:[...r],mac:s.mac,parent:s.parent});if(o.res==="have")return{uid:H(o.uid),uploaded:0,bytes:0};if(o.res!=="want")throw new P("protostate",`expected want or have, got ${JSON.stringify(o)}`);let a=At(o.chunks),l=new Set(r),h=0;for(let u of a){if(!l.has(u))throw new P("badchunk",`server asked for ${u}, which this put does not contain`);let f=await i(u);this.sendBody(f),h+=f.length}return{uid:await this.awaitAck(),uploaded:a.length,bytes:h}}async putMany(e,n){if(e.length===0)return{results:[],uploaded:0,bytes:0};if(e.length>_e)throw new P("toolarge",`${e.length} entries in one batch, the limit is ${_e}`);let r=await this.request({op:"putmany",entries:e.map(h=>({path:h.path,meta:Mn(h.meta),chunks:[...h.names],mac:h.mac,parent:h.parent}))}),i=r,s=0,o=0;if(r.res==="want"){let h=At(r.chunks),c=new Set;for(let u of e)for(let f of u.names)c.add(f);for(let u of h){if(!c.has(u))throw new P("badchunk",`server asked for ${u}, which this batch does not contain`);let f=await n(u);this.sendBody(f),o+=f.length}s=h.length,i=await this.awaitReply()}if(i.res!=="acks")throw new P("protostate",`expected acks, got ${JSON.stringify(i)}`);let a=i.results;if(!Array.isArray(a)||a.length!==e.length)throw new P("protostate",`${e.length} entries went up and ${Array.isArray(a)?a.length:"no"} results came back`);let l=a.map(h=>{let c=h??{};return c.code!==void 0?{uid:0,error:new P(String(c.code),String(c.msg??"no message"))}:{uid:H(c.uid)}});for(let h of l)h.error?.fatal&&this.die(h.error);return{results:l,uploaded:s,bytes:o}}async awaitAck(){let e=await this.awaitReply();if(e.res!=="ack")throw new P("protostate",`expected ack, got ${JSON.stringify(e)}`);return H(e.uid)}async awaitReply(){let e=await new Promise((n,r)=>{if(this.closed){r(this.closeReason??new j("not connected"));return}let i=this.opts.timeoutMs??$n,s=setTimeout(()=>{this.die(new j(`no acknowledgement within ${i}ms`))},i);this.replyWaiter={resolve:o=>{clearTimeout(s),n(o)},reject:o=>{clearTimeout(s),r(o)}}});if(e.res==="err"){let n=new P(String(e.code??"unknown"),String(e.msg??"no message"));throw n.fatal&&this.die(n),n}return e}async get(e){let n=await this.request({op:"get",uid:e});if(n.res!=="chunks")throw new P("protostate",`expected chunks, got ${JSON.stringify(n)}`);return{uid:H(n.uid),size:H(n.size),chunks:At(n.chunks)}}async history(e,n={}){let r=await this.request({op:"history",path:e,...n.before!==void 0?{before:n.before}:{},...n.limit!==void 0?{limit:n.limit}:{}});if(r.res!=="history")throw new P("protostate",`expected history, got ${JSON.stringify(r)}`);return Tn(r.entries,"history")}async deleted(e){let n=await this.request({op:"deleted",...e!==void 0?{limit:e}:{}});if(n.res!=="deleted")throw new P("protostate",`expected deleted, got ${JSON.stringify(n)}`);return{entries:Tn(n.entries,"deleted"),more:n.more===!0}}async fetch(e){if(e.length===0)return[];this.expecting+=e.length;let n=this.request({op:"fetch",chunks:[...e]}),r=[],i=[];try{for(let s=0;s<e.length;s++){let o=await Promise.race([this.body(),n.then(l=>{throw new P("protostate",`expected a chunk body, got ${JSON.stringify(l)}`)})]),a=e[s];i.push(mt(o).then(l=>{if(l!==a)throw new P("badchunk",`asked for ${a} and received ${o.length} bytes that hash to ${l}`)})),r.push(o)}await Promise.all(i)}catch(s){throw this.expecting=0,this.disarmReply(),this.replyWaiter=void 0,s instanceof P&&s.code==="badchunk"&&this.die(s),s}return this.expecting=Math.max(0,this.expecting-e.length),this.disarmReply(),this.replyWaiter=void 0,r}async ping(){let e=await this.request({op:"ping"});if(e.res!=="pong")throw new P("protostate",`expected pong, got ${JSON.stringify(e)}`)}},He=class{constructor(e=0,n=3e5,r=5e3,i=!0,s=Math.random){this.min=e;this.max=n;this.base=r;this.jitter=i;this.random=s}min;max;base;jitter;random;count=0;nextAt=0;success(e){this.count=0,this.nextAt=e+this.delay()}fail(e){this.count++,this.nextAt=e+this.delay()}delay(){if(this.count===0)return this.min;let e=this.base*Math.pow(2,this.count-1);return this.jitter&&(e*=.5+.5*this.random()),Math.floor(Math.min(this.max,this.min+e))}readyAt(){return this.nextAt}isReady(e){return e>=this.nextAt}get failures(){return this.count}};function ri(t){let e=globalThis.WebSocket;if(!e)throw new Error("no WebSocket available in this environment");return new e(t)}function ii(t){if(t instanceof Uint8Array)return t;if(t instanceof ArrayBuffer)return new Uint8Array(t);if(ArrayBuffer.isView(t))return new Uint8Array(t.buffer,t.byteOffset,t.byteLength)}function H(t){return typeof t=="number"&&Number.isFinite(t)?t:0}function At(t){return Array.isArray(t)?t.filter(e=>typeof e=="string"):[]}function Cn(t){let e=t.code??0,n=t.reason?`, ${t.reason}`:"";return`code ${e}${n}`}function Tn(t,e){if(!Array.isArray(t))throw new P("protostate",`${e} came back without a list of entries`);return t}function _n(t){let e=t.split("/");e.pop();let n=[],r="";for(let i of e)r=r?`${r}/${i}`:i,n.push(r);return n}function si(t){let e={...t};return t.hash===q(t.chunks)&&delete e.hash,t.synchash===t.hash&&delete e.synchash,e}function oi(t,e){let n={...kt(t),...e};return e.hash===void 0&&(n.hash=q(n.chunks)),e.synchash===void 0&&(n.synchash=n.hash),n}function q(t){return t.length===0?"-empty-":t.join(",")}function ai(t,e){if(t<e)throw new Error(`this server is at version ${t} and this device has already seen ${e}: refusing to sync, because a server behind its own clients is a restored backup or the wrong vault`)}var Rn={perFileMax:1<<28,maxChunks:65536};function In(t,e){return t>0?Math.min(t,e):e}function li(t){if(!t.folder&&!t.deleted&&t.size>0&&t.chunks.length===0)throw new Error(`version ${t.uid} declares ${t.size} bytes and names no chunks, which cannot both be true`);if(t.chunks.length>0&&(t.folder||t.deleted)){let e=t.folder?"a folder":"a deletion";throw new Error(`version ${t.uid} is ${e} and names ${t.chunks.length} chunks`)}}function hi(t){return t===""||t==="-empty-"?[]:t.split(",")}function zn(){return{uploaded:0,downloaded:0,merged:0,conflicted:0,deletedLocally:0,deletedRemotely:0,restored:0,foldersCreated:0,unchanged:0,waiting:0,retrying:0,skipped:0,blocked:0,chunksSent:0,bytesSent:0}}var je=class{constructor(e){this.opts=e}opts;entries=new Map;remote=new Map;pending=new Set;retries=new Map;skipped=new Map;blocked=new Set;cannotStream=!1;outbox=[];outboxBytes=0;inbox=[];inboxBytes=0;limits;unsealed=new Map;cursor=0;syncing=!1;again=!1;started=!1;now(){return this.opts.now?.()??Date.now()}log(e,...n){this.opts.log?.(e,...n)}mergeable(e){return(this.opts.mergeable??pn)(e)}sizesFor(e,n){return dn(e,n,this.limits?.chunkMax)}get coalesce(){return this.opts.coalesceWrites??!0}status(){let e=0;for(let n of this.entries.values())n.folder||e++;return{cursor:this.cursor,files:e,pending:this.pending.size,retrying:this.retries.size,skipped:this.skipped.size,syncing:this.syncing}}async start(){if(this.started)throw new Error("already started");this.started=!0;let e=await this.opts.store.load();if(e){this.cursor=e.cursor;for(let[r,i]of Object.entries(e.entries))this.entries.set(r,oi(r,i));for(let[r,i]of Object.entries(e.remote))this.remote.set(r,i);for(let r of e.pending)this.pending.add(r);this.log("index loaded",{cursor:this.cursor,entries:this.entries.size,pending:this.pending.size})}let n=await this.opts.transport.hello({vault:this.opts.vaultId,token:this.opts.token,device:this.opts.device,cursor:this.cursor,...this.opts.claim!==void 0?{claim:this.opts.claim}:{}});return ai(n.cursor,this.cursor),this.limits=n,this.log("connected",n),n}async authFor(e,n){let r=await cn(n);return{mac:await yt(this.opts.keys,{path:e.path,size:e.meta.size,ctime:e.meta.ctime,mtime:e.meta.mtime,folder:e.meta.folder??!1,deleted:e.meta.deleted??!1,prev:e.meta.prev,chunks:e.names,parent:r}),parent:r}}async acceptBatch(e){let n=e.entries.map(l=>({path:l.path,size:l.size,ctime:l.ctime,mtime:l.mtime,folder:l.folder,deleted:l.deleted,prev:l.prev,chunks:l.chunks,parent:l.parent??""})),i=(await Promise.all(n.map((l,h)=>hn(this.opts.keys,l,e.entries[h].mac)))).indexOf(!1);if(i>=0)throw new Error(`version ${e.entries[i].uid} is not authenticated by this vault's key, so nothing that holds the key wrote it`);for(let l of e.entries)li(l);let s=await Promise.all(e.entries.map(l=>this.plaintextPath(l.path))),o=await Promise.all(e.entries.map(l=>l.prev?this.plaintextPath(l.prev):void 0)),a=new Map;for(let l=0;l<e.entries.length;l++){let h=e.entries[l],c=s[l];if(a.set(c,{uid:h.uid,folder:h.folder,deleted:h.deleted,mtime:h.mtime,size:h.size,hash:q(h.chunks)}),h.prev){let u=o[l];a.set(u,{uid:h.uid,folder:!1,deleted:!0,mtime:h.mtime,size:0,hash:""})}}for(let[l,h]of a)this.remote.set(l,h),this.pending.add(l);this.cursor=e.to}async plaintextPath(e){let n=this.unsealed.get(e);if(n!==void 0)return n;let r=await Ve(this.opts.keys,e);return this.unsealed.set(e,r),r}async sync(e={}){if(this.syncing)return this.again=!0,zn();this.syncing=!0;try{let n=await this.pass(e);for(;this.again;){this.again=!1;let r=await this.pass(e);n=ci(n,r)}return n}finally{this.syncing=!1}}async pass(e={}){let n=zn(),r=this.now(),i=e.coalesceWrites??this.coalesce;(this.outbox.length>0||this.inbox.length>0)&&(this.log("a pass ended early, discarding what it had queued",{writes:this.outbox.length,reads:this.inbox.length}),this.outbox=[],this.outboxBytes=0,this.inbox=[],this.inboxBytes=0);let s=await this.opts.vault.list(),o=new Map(s.map(c=>[c.path,c]));for(let c of s){let u=this.entryFor(c.path);be(u,c)}let a=new Set;for(let[c,u]of o)u.folder||a.add(c);let l=new Set,h=new Set([...o.keys(),...this.entries.keys(),...this.remote.keys()]);for(let c of[...h].sort()){let u=this.skipped.get(c);if(u){if(xt(this.entries.get(c))===u.fingerprint){n.skipped++;continue}this.skipped.delete(c),this.log("skipped file changed, trying again",c)}let f=_n(c).find(m=>a.has(m));if(f!==void 0&&!o.has(c)){l.add(c),this.blocked.has(c)||this.log("cannot be both",c,`${f} is a file here and a folder elsewhere`),n.blocked++;continue}let g=this.retries.get(c);if(g&&g.at>r){n.retrying++;continue}try{this.opts.onProgress?.(c),await this.reconcile(c,o.get(c),n,r,i),this.retries.delete(c)}catch(m){this.recordFailure(c,m,n)}}return this.wroteThisPass=[],await this.fill(n),await this.applyDeletes(n),this.wroteThisPass=[],await this.flush(n),this.opts.onProgress?.(void 0),this.blocked=l,this.prune(o),await this.opts.vault.flush?.(),await this.save(),n}entryFor(e){let n=this.entries.get(e);return n||(n=kt(e),this.entries.set(e,n)),n}async reconcile(e,n,r,i,s){let o=this.entryFor(e),a=this.remote.get(e),l=this.limits?.perFileMax??0;if(n&&!n.folder&&l>0&&n.size>l){this.recordFailure(e,di(n.size,l),r);return}let h,c;n&&(!n.folder&&St(o,Math.ceil(n.mtime),n.size)&&(c=await this.rehash(o,e,n.size)),h={folder:n.folder,mtime:o.mtime,size:o.size,hash:o.hash});let u=An({local:h,remote:a,index:o,mergeable:this.mergeable(e)});if(s&&u.kind!=="nothing"&&h&&!n?.folder&&!Pn(o,i)){r.waiting++;return}await this.act(e,u,o,h,a,r,c),this.pending.delete(e)}async rehash(e,n,r){let i=await this.streamScan(e,n,r);if(i)return i;let s=await this.opts.vault.read(n),o=this.mergeable(n),a=[...bt(s,this.sizesFor(s.length,o),o)],l=a.map(h=>h.bytes);if(s.length<=Dn){let h=await ue(this.opts.keys,l);return e.chunks=h.map(c=>c.name),e.hash=q(e.chunks),e.size=s.length,{bytes:s,pieces:a,names:e.chunks,sealed:h}}return e.chunks=await this.namesOf(l),e.hash=q(e.chunks),e.size=s.length,{bytes:s,pieces:a,names:e.chunks}}async streamScan(e,n,r){let i=this.opts.vault;if(!(!i.readBlocks||!i.readRange||this.cannotStream)&&!(r===void 0||r<=Dn))try{return await this.streamed(e,n,r)}catch(s){this.cannotStream=!0,this.log("streaming is not available here, reading whole files instead",n,{why:s.message});return}}async streamed(e,n,r){let i=this.opts.vault,s=this.mergeable(n),o=[],a=[],l=0;for await(let h of mn(i.readBlocks(n),this.sizesFor(r,s),s)){let c=await ue(this.opts.keys,[h.bytes]);o.push(c[0].name),a.push({start:h.offset,end:h.offset+h.bytes.length}),l+=h.bytes.length}return e.chunks=o,e.hash=q(o),e.size=l,{names:o,spans:a,path:n,size:l}}namesOf(e){return ui(this.opts.keys,e)}async act(e,n,r,i,s,o,a){switch(n.kind){case"nothing":o.unchanged++,i&&s&&!s.deleted&&i.hash===s.hash&&de(r,i.hash,r.chunks,s.uid,this.now());return;case"upload":await this.upload(e,r,o,!0,a);return;case"download":case"restoreLocal":{if(!s)return;await this.receive(e,r,s,n.kind,n.why,o);return}case"createLocalFolder":await this.opts.vault.mkdir(e),r.folder=!0,s&&de(r,"",[],s.uid,this.now()),o.foldersCreated++;return;case"clash":this.skipped.set(e,{why:`${n.why}. Rename one of them, and it will sync.`,fingerprint:xt(r)}),o.skipped++,this.log("cannot be both",e,n.why);return;case"deleteLocal":this.pendingDeletes.push({path:e,why:n.why});return;case"deleteRemote":{let l=this.now(),h={path:await this.sealedPath(e),meta:{size:0,ctime:0,mtime:l,deleted:!0},names:[]};await this.queue({path:e,size:0,entry:{...h,...await this.authFor(h,r.synchash)},bodyOf:Fn,commit:c=>{this.remote.set(e,{uid:c,folder:!1,deleted:!0,mtime:this.now(),size:0,hash:""}),this.entries.delete(e),o.deletedRemotely++,this.log("deleted on the server",e,n.why)}},o);return}case"merge":await this.merge(e,r,s,o);return;case"conflict":await this.conflict(e,r,s,o,n.why);return}}async upload(e,n,r,i=!1,s){if(n.folder){let f={path:await this.sealedPath(e),meta:{size:0,ctime:0,mtime:0,folder:!0},names:[]};await this.queue({path:e,size:0,entry:{...f,...await this.authFor(f,"")},bodyOf:Fn,commit:g=>{de(n,"",[],g,this.now()),this.remote.set(e,{uid:g,folder:!0,deleted:!1,mtime:0,size:0,hash:""}),i&&r.uploaded++}},r);return}let o=await this.planUpload(n,e,s),a=n.hash,l=[...n.chunks],h=n.size,c=n.mtime,u={path:await this.sealedPath(e),meta:{size:h,ctime:n.ctime,mtime:c,...n.prev?{prev:await this.sealedPath(n.prev)}:{}},names:o.names};await this.queue({path:e,size:h,entry:{...u,...await this.authFor(u,n.synchash)},bodyOf:o.bodyOf,commit:f=>{de(n,a,l,f,this.now()),this.remote.set(e,{uid:f,folder:!1,deleted:!1,mtime:c,size:h,hash:a}),i&&r.uploaded++,this.log("uploaded",e)}},r)}async queue(e,n){this.outbox.push(e),this.outboxBytes+=e.size,(this.outbox.length>=_e||this.outboxBytes>=Un)&&await this.flush(n)}async flush(e){if(this.outbox.length===0)return;let n=this.outbox;this.outbox=[],this.outboxBytes=0;let r=new Map;for(let s of n)for(let o of s.entry.names)r.has(o)||r.set(o,s.bodyOf);let i;try{i=await this.opts.transport.putMany(n.map(s=>s.entry),async s=>{let o=r.get(s);if(!o)throw new Error(`server asked for ${s}, which no queued file contains`);return o(s)})}catch(s){for(let o of n)this.recordFailure(o.path,s,e);return}e.chunksSent+=i.uploaded,e.bytesSent+=i.bytes;for(let s=0;s<n.length;s++){let o=n[s],a=i.results[s];if(a.error){this.recordFailure(o.path,a.error,e);continue}o.commit(a.uid)}}async planUpload(e,n,r){let i=r??await this.scan(e,n);if(i.sealed){let h=new Map(i.sealed.map(c=>[c.name,c.bytes]));return{names:i.names,bodyOf:async c=>{let u=h.get(c);if(!u)throw new Error(`no sealed body for ${c} of ${n}`);return u}}}let s=this.opts.keys,o=this.opts.vault;if(i.spans){let h=new Map(i.names.map((c,u)=>[c,i.spans[u]]));return{names:i.names,bodyOf:async c=>{let u=h.get(c);if(!u)throw new Error(`no chunk named ${c} in ${n}`);let f=await o.readRange(i.path,u.start,u.end),g=await ue(s,[f]);if(g[0].name!==c)throw new Error(`${n} changed while it was being sent, so it was not sent`);return g[0].bytes}}}let a=new Map;for(let h=0;h<i.names.length;h++){let c=i.pieces[h];a.set(i.names[h],{start:c.offset,end:c.offset+c.bytes.length})}let l=i.bytes;return{names:i.names,bodyOf:async h=>{let c=a.get(h);if(!c)throw new Error(`no chunk named ${h} in ${n}`);return(await ue(s,[l.subarray(c.start,c.end)]))[0].bytes}}}scan(e,n){return this.rehash(e,n)}async receive(e,n,r,i,s,o){let a=hi(r.hash);this.checkChunkCount(r.uid,a.length),this.inbox.push({path:e,entry:n,remote:r,chunks:a,kind:i,why:s}),this.inboxBytes+=r.size,(this.inbox.length>=_e||this.inboxBytes>=Un)&&await this.fill(o)}async fill(e){if(this.inbox.length===0)return;let n=this.inbox;this.inbox=[],this.inboxBytes=0;let r=new Map,i=this.heldByContent();for(let l of n){let h=i.get(q(l.chunks));h!==void 0&&h!==l.path&&r.set(l,h)}let s=[],o=new Set;for(let l of n)if(!r.has(l))for(let h of l.chunks)o.has(h)||(o.add(h),s.push(h));let a=new Map;if(s.length>0)try{let l=await this.opts.transport.fetch(s);for(let h=0;h<s.length;h++)a.set(s[h],l[h])}catch(l){for(let h of n)this.recordFailure(h.path,l,e);return}for(let l of n)try{let h=r.get(l);if(h!==void 0&&await this.landFromLocal(l,h)){l.kind==="download"?e.downloaded++:e.restored++,this.log(l.kind,l.path,`${l.why}, from ${h} without asking`);continue}h!==void 0?await this.land(l,await this.fetchFor(l)):await this.land(l,a),l.kind==="download"?e.downloaded++:e.restored++,this.log(l.kind,l.path,l.why)}catch(h){this.recordFailure(l.path,h,e)}}pendingDeletes=[];wroteThisPass=[];async wouldUndoAWrite(e){let n=this.opts.vault;for(let r of this.wroteThisPass){if(r===e)continue;if(n.sameFile?await n.sameFile(r,e):r.normalize("NFC").toLowerCase()===e.normalize("NFC").toLowerCase())return r}}async applyDeletes(e){let n=this.pendingDeletes;this.pendingDeletes=[];for(let{path:r,why:i}of n)try{let s=await this.wouldUndoAWrite(r);if(s!==void 0){this.entries.delete(r),this.log("kept",r,`deleting it would remove ${s}, which is the same file`);continue}await this.opts.vault.remove(r),this.entries.delete(r),e.deletedLocally++,this.log("deleted locally",r,i)}catch(s){this.recordFailure(r,s,e)}}heldByContent(){let e=new Map;for(let[n,r]of this.entries)r.folder||r.hash===""||r.hash==="-empty-"||e.set(r.hash,n);return e}async fetchFor(e){let n=await this.opts.transport.fetch([...e.chunks]),r=new Map;return e.chunks.forEach((i,s)=>r.set(i,n[s])),r}async landFromLocal(e,n){let r;try{r=await this.opts.vault.read(n)}catch{return!1}if(r.length!==e.remote.size)return!1;let i=this.mergeable(n),s=[...bt(r,this.sizesFor(r.length,i),i)].map(a=>a.bytes),o=(await ue(this.opts.keys,s)).map(a=>a.name);return q(o)!==q(e.chunks)?!1:(await this.opts.vault.write(e.path,r,{mtime:e.remote.mtime,ctime:e.remote.mtime}),this.wroteThisPass.push(e.path),be(e.entry,{folder:!1,mtime:e.remote.mtime,ctime:e.remote.mtime,size:r.length}),e.entry.chunks=[...e.chunks],e.entry.hash=q(e.chunks),e.entry.size=r.length,de(e.entry,e.entry.hash,e.entry.chunks,e.remote.uid,this.now()),!0)}async land(e,n){let r=e.chunks.map(s=>{let o=n.get(s);if(!o)throw new Error(`the server did not send ${s}, which ${e.path} is made of`);return o}),i=await this.assemble(e.remote.uid,r);if(i.length!==e.remote.size)throw new Error(`version ${e.remote.uid} of ${e.path} assembled to ${i.length} bytes, not the ${e.remote.size} it declares`);await this.opts.vault.write(e.path,i,{mtime:e.remote.mtime,ctime:e.remote.mtime}),this.wroteThisPass.push(e.path),be(e.entry,{folder:!1,mtime:e.remote.mtime,ctime:e.remote.mtime,size:i.length}),e.entry.chunks=[...e.chunks],e.entry.hash=q(e.chunks),e.entry.size=i.length,de(e.entry,e.entry.hash,e.entry.chunks,e.remote.uid,this.now())}async contentOf(e,n,r){let i=n!==void 0?{chunks:n}:await this.opts.transport.get(e);if(r!==void 0&&q(i.chunks)!==r)throw new Error(`version ${e} is made of chunks this device did not record for it, so it is not the version it is being offered as`);return i.chunks.length===0?new Uint8Array(0):(this.checkChunkCount(e,i.chunks.length),this.assemble(e,await this.opts.transport.fetch(i.chunks)))}checkChunkCount(e,n){let r=In(this.limits?.maxChunks??0,Rn.maxChunks);if(n>r)throw new Error(`version ${e} names ${n} chunks, and this server said it stores at most ${r}`)}async assemble(e,n){if(n.length===0)return new Uint8Array(0);let r=[],i=0,s=In(this.limits?.perFileMax??0,Rn.perFileMax);for(let l=0;l<n.length;l+=Pt){let h=await Promise.all(n.slice(l,l+Pt).map(c=>an(this.opts.keys,c)));for(let c of h){if(i+=c.length,i>s)throw new Error(`version ${e} is over ${i} bytes, and this server said it stores at most ${s}`);r.push(c)}}let o=new Uint8Array(i),a=0;for(let l of r)o.set(l,a),a+=l.length;return o}async merge(e,n,r,i){if(!r)return;let s=new TextDecoder("utf-8",{fatal:!0}),o,a,l;try{o=s.decode(await this.contentOf(n.syncuid,void 0,n.synchash)),a=s.decode(await this.opts.vault.read(e)),l=s.decode(await this.contentOf(r.uid,void 0,r.hash))}catch{let u="one side is not valid UTF-8, so merging it would rewrite bytes nobody edited";this.log("merge refused",e,u),await this.conflict(e,n,r,i,u);return}let h=Sn(o,a,l,yn(e)?gi:void 0);if(h.kind==="conflict"){this.log("merge refused",e,h.why),await this.conflict(e,n,r,i,h.why);return}let c=h.text;c!==a&&await this.opts.vault.write(e,new TextEncoder().encode(c),{mtime:this.now(),ctime:n.ctime}),be(n,{folder:!1,mtime:this.now(),ctime:n.ctime,size:c.length}),await this.upload(e,n,i),i.merged++,this.log("merged",e,h.kind==="merged"?"three-way":h.why)}freeConflictPath(e){return fi(En(e,this.opts.device,new Date(this.now())),n=>this.opts.vault.exists(n))}async conflict(e,n,r,i,s){if(!r)return;let o=await this.freeConflictPath(e),a=await this.contentOf(r.uid,void 0,r.hash);await this.opts.vault.write(o,a,{mtime:r.mtime,ctime:r.mtime});let l=this.entryFor(o);be(l,{folder:!1,mtime:r.mtime,ctime:r.mtime,size:a.length}),await this.upload(o,l,i),await this.upload(e,n,i),i.conflicted++,this.log("kept both",e,{copy:o,why:s})}async sealedPath(e){let n=await We(this.opts.keys,e);return this.unsealed.set(n,e),n}recordFailure(e,n,r){let i=n instanceof Error?n.message:String(n),s=n?.code;if(s!==void 0&&["badentry","badname","toolarge"].includes(s)){this.skipped.set(e,{why:i,fingerprint:xt(this.entries.get(e))}),r.skipped++,this.log("skipped for good",e,i);return}let a=this.retries.get(e)??{count:0,error:"",at:0};a.count++,a.error=i,a.at=this.now()+Math.min(3e5,5e3*Math.pow(2,a.count)),this.retries.set(e,a),r.retrying++,this.log("will retry",e,{attempt:a.count,error:i})}prune(e){for(let[n,r]of this.entries){if(e.has(n))continue;let i=this.remote.get(n);i&&!i.deleted||r.synchash===""&&r.hash===""&&this.entries.delete(n)}for(let[n,r]of this.remote)r.deleted&&(e.has(n)||this.entries.has(n)||this.pending.has(n)||this.remote.delete(n))}async save(){let e={};for(let[r,i]of this.entries)e[r]=si(i);let n={};for(let[r,i]of this.remote)n[r]=i;await this.opts.store.save({cursor:this.cursor,entries:e,remote:n,pending:[...this.pending]})}noteRename(e,n){let r=this.entries.get(e);r&&(this.entries.delete(e),xn(r,e,n),this.entries.set(n,r))}};function ci(t,e){let n={...t};for(let r of Object.keys(n))n[r]=t[r]+e[r];return n}function xt(t){return t?`${t.mtime}:${t.size}`:"gone"}var Dn=8*1024*1024,Pt=16;async function ui(t,e,n=Pt){let r=[];for(let i=0;i<e.length;i+=n){let s=await ue(t,e.slice(i,i+n));for(let o of s)r.push(o.name)}return r}var Un=8*1024*1024;async function fi(t,e){if(!await e(t))return t;let n=t.lastIndexOf("."),r=t.lastIndexOf("/"),i=n>r,s=i?t.slice(0,n):t,o=i?t.slice(n):"";for(let a=2;a<1e3;a++){let l=`${s} ${a}${o}`;if(!await e(l))return l}throw new Error(`cannot find an unused name beside ${t}`)}function di(t,e){let n=new Error(`${t} bytes, and this server said it stores at most ${e}, so it was not read`);return n.code="toolarge",n}async function Fn(t){throw new Error(`this put has no bodies, and the server asked for ${t}`)}function gi(t){try{return JSON.parse(t),!0}catch{return!1}}var mi=150,Re=class{constructor(e){this.opts=e;let n;this.transport=new Ke(e.url,{onBatch:async r=>{await n.acceptBatch(r),r.entries.length>0&&this.soon()},onCaughtUp:()=>{this.caughtUp=!0},onClosed:r=>{this.endedWith=r,this.notifyEnded?.(r)},...e.timeoutMs!==void 0?{timeoutMs:e.timeoutMs}:{},...e.log!==void 0?{log:e.log}:{},...e.socketFactory!==void 0?{socketFactory:e.socketFactory}:{}}),n=new je({vault:e.vault,store:e.store,keys:e.keys,transport:this.transport,device:e.device,vaultId:e.vaultId,token:e.token,...e.claim!==void 0?{claim:e.claim}:{},...e.coalesceWrites!==void 0?{coalesceWrites:e.coalesceWrites}:{},...e.log!==void 0?{log:e.log}:{},...e.onProgress!==void 0?{onProgress:e.onProgress}:{}}),this.engine=n}opts;engine;transport;limits;soonTimer;caughtUp=!1;endedWith;notifyEnded;queue=Promise.resolve();serial(e){let n=this.queue.then(e,e);return this.queue=n.catch(()=>{}),n}get requestsSent(){return this.transport.requestsSent}get serverCursor(){return this.limits?.cursor??0}async connect(){await this.transport.connect(),this.limits=await this.engine.start();let e=this.opts.timeoutMs??3e4,n=Date.now()+e;for(;!this.caughtUp&&Date.now()<n;){if(this.endedWith)throw this.endedWith;await $t(25)}if(!this.caughtUp)throw new Error("the server never finished sending what it already had");return this.limits}async settle(e={},n=8){let r=await this.pass(e),i=r;for(let s=0;s<n&&wi(r);s++)await $t(60),r=await this.pass(e),i=vi(i,r);return i}pass(e){return this.serial(()=>this.engine.sync(e))}async runUntilClosed(e=3e4){if(this.endedWith)return this.endedWith;let n=this.opts.vault.watch?.(()=>{this.sync()}),r=setInterval(()=>{this.sync().then(()=>this.keepalive())},e);try{return await new Promise(i=>{this.notifyEnded=i})}finally{clearInterval(r),n?.()}}async keepalive(){try{await this.transport.ping()}catch{}}soon(){this.soonTimer===void 0&&(this.soonTimer=setTimeout(()=>{this.soonTimer=void 0,this.sync()},mi))}async sync(e={}){try{return await this.pass(e)}catch(n){this.opts.log?.("sync failed",n.message);return}}async history(e,n={}){let r=await We(this.opts.keys,e);return(await this.serial(()=>this.transport.history(r,n))).map(s=>this.asVersion(s,e))}async deleted(e){let n=await this.serial(()=>this.transport.deleted(e)),r=[];for(let i of n.entries)r.push({...this.asVersion(i,await Ve(this.opts.keys,i.path)),restorable:i.restorable??0});return{notes:r,more:n.more}}async contentAt(e){return e.deleted||e.folder?new Uint8Array(0):this.serial(()=>this.engine.contentOf(e.uid))}async restore(e,n){if(e.deleted)throw new Error(`version ${e.uid} of ${e.path} is the deletion itself, not a version to restore`);if(e.folder){let o=n??e.path;return await this.opts.vault.mkdir(o),{path:o,bytes:0}}let r=await this.serial(()=>this.engine.contentOf(e.uid)),i=n??e.path,s=await this.opts.vault.exists(i)?pi(i,e):i;return await this.opts.vault.write(s,r,{mtime:e.mtime,ctime:e.ctime}),{path:s,bytes:r.length}}async newestContentVersion(e){return(await this.history(e,{limit:50})).find(r=>!r.deleted)}asVersion(e,n){return{uid:e.uid,path:n,size:e.size,ctime:e.ctime,mtime:e.mtime,folder:e.folder,deleted:e.deleted,device:e.device,chunks:e.chunks?.length??0}}close(){this.soonTimer!==void 0&&(clearTimeout(this.soonTimer),this.soonTimer=void 0),this.transport.close()}};function pi(t,e){let n=t.lastIndexOf("/"),r=n===-1?"":t.slice(0,n+1),i=n===-1?t:t.slice(n+1),s=i.lastIndexOf("."),o=s<=0?i:i.slice(0,s),a=s<=0?"":i.slice(s);return`${r}${o} (restored ${e.uid})${a}`}async function On(t,e={}){let n=new He(0,3e5,5e3,!0);for(;e.keepGoing?.()??!0;){let r,i,s=!1;try{r=new Re(t),await r.connect(),s=!0,n.success(Date.now()),e.onClient?.(r),e.onSynced?.(await r.settle(),r.serverCursor),i=await r.runUntilClosed()}catch(l){i=l}finally{e.onClient?.(void 0),r?.close()}if(i&&yi(i)){e.onFatal?.(i);return}if(!(e.keepGoing?.()??!0))return;n.fail(Date.now());let o=n.delay(),a=i??new Error("the connection ended");s?e.onDisconnected?.(a,o):e.onUnreachable?.(a,o),await $t(o)}}function yi(t){return t instanceof P&&t.fatal}function vi(t,e){return{uploaded:t.uploaded+e.uploaded,downloaded:t.downloaded+e.downloaded,merged:t.merged+e.merged,conflicted:t.conflicted+e.conflicted,deletedLocally:t.deletedLocally+e.deletedLocally,deletedRemotely:t.deletedRemotely+e.deletedRemotely,restored:t.restored+e.restored,foldersCreated:t.foldersCreated+e.foldersCreated,chunksSent:t.chunksSent+e.chunksSent,bytesSent:t.bytesSent+e.bytesSent,unchanged:e.unchanged,waiting:e.waiting,retrying:e.retrying,skipped:e.skipped,blocked:e.blocked}}function wi(t){return t.uploaded+t.downloaded+t.merged+t.conflicted+t.deletedLocally+t.deletedRemotely+t.restored+t.foldersCreated+t.waiting>0}var $t=t=>new Promise(e=>setTimeout(e,t));var Ge="basalt2_",Mt=2,Ie=4,Nn=new TextEncoder,bi=new TextDecoder;function Ct(t){if(t.secret.length!==X)throw new Error(`a root secret is ${X} bytes, not ${t.secret.length}`);let e=[Nn.encode(t.url),Nn.encode(t.vaultId)];for(let o of e)if(o.length>255)throw new Error("a pairing field is too long to encode");let n=1+X+e.reduce((o,a)=>o+1+a.length,0),r=new Uint8Array(n);r[0]=Mt,r.set(t.secret,1);let i=1+X;for(let o of e)r[i++]=o.length,r.set(o,i),i+=o.length;let s=new Uint8Array(n+Ie);return s.set(r,0),s.set(Ln(r),n),Ge+Te(s)}function Bn(t){let e=t.trim();if(e.startsWith("basalt1_"))throw new Error("this is a version 1 pairing string, from before the server token was folded into the root secret. Run basalt invite on a device that is already paired to get a current one.");if(!e.startsWith(Ge))throw new Error(`not a pairing string: it should start with ${Ge}`);let n;try{n=Je(e.slice(Ge.length))}catch{throw new Error("this pairing string is damaged: it is not valid base64url")}if(n.length<1+X+2+Ie)throw new Error("this pairing string is too short to be complete");let r=n.subarray(0,n.length-Ie),i=n.subarray(n.length-Ie),s=Ln(r);for(let u=0;u<Ie;u++)if(i[u]!==s[u])throw new Error("this pairing string is damaged: it did not survive being copied");if(r[0]!==Mt)throw new Error(`this pairing string is version ${r[0]}, and this device understands ${Mt}`);let o=r.slice(1,1+X),a=1+X,l=u=>{if(a>=r.length)throw new Error(`this pairing string ends before its ${u}`);let f=r[a++];if(a+f>r.length)throw new Error(`this pairing string ends inside its ${u}`);let g=bi.decode(r.subarray(a,a+f));return a+=f,g},h=l("server address"),c=l("vault name");if(a!==r.length)throw new Error("this pairing string has more in it than it should");if(h===""||c==="")throw new Error("this pairing string has an empty field");return{url:h,secret:o,vaultId:c}}function Ln(t){let e=4294967295;for(let n of t){e^=n;for(let r=0;r<8;r++)e=e&1?e>>>1^3988292384:e>>>1}return e=(e^4294967295)>>>0,new Uint8Array([e>>>24&255,e>>>16&255,e>>>8&255,e&255])}function Wn(t){return{url:t.url,vaultId:t.vaultId,device:t.device,secret:Te(t.secret),...t.bootstrap?{bootstrap:t.bootstrap}:{}}}function Vn(t,e){if(typeof t!="object"||t===null)throw new Error(`${e} does not hold a configuration`);let n=t,r=o=>{let a=n[o];if(typeof a!="string"||a==="")throw new Error(`${e} has no ${o}`);return a},i=Je(r("secret"));if(i.length!==X)throw new Error(`${e} holds a ${i.length} byte secret, and a root secret is ${X}`);let s=n.bootstrap;return{url:r("url"),vaultId:r("vaultId"),device:r("device"),secret:i,...typeof s=="string"&&s!==""?{bootstrap:s}:{}}}function Jn(t){let e=t.trim().replace(/\/+$/,"");if(e==="")throw new Error("that is not a server address");if(e.startsWith("ws://")||e.startsWith("wss://"))return e;if(e.startsWith("http://"))return"ws://"+e.slice(7);if(e.startsWith("https://"))return"wss://"+e.slice(8);if(e.includes("://"))throw new Error(`a server address is ws:// or wss://, not ${e.split("://")[0]}://`);return"wss://"+e}import{constants as Tt,watch as ki}from"node:fs";import{access as _t,cp as Si,mkdir as Qe,open as et,readFile as qn,readdir as Kn,realpath as Hn,rename as Rt,rm as Ei,stat as qe,utimes as Ai}from"node:fs/promises";import{basename as Ze,dirname as le,join as Xe,relative as xi,resolve as jn,sep as Ye}from"node:path";var Zn=".trash",Pi=new Set([".basalt",Zn,".git",".DS_Store","node_modules"]),It=".obsidian";function zt(t){let e=t.replace(/^\/+|\/+$/g,"");if(e===""||e.includes("/"))throw new Error(`refusing to sync: the config folder ${JSON.stringify(t)} is not a plain name`);return e}var tt=class{root;ignore;constructor(e,n={}){this.root=jn(e);let r=zt(n.configDir??It);this.ignore=new Set([...Pi,r,...n.alsoIgnore??[]])}realRootOnce;unflushed=new Set;async flush(){let e=[...this.unflushed];this.unflushed.clear(),await Promise.all(e.map(n=>Qn(n)))}async insideForReal(e){let n=await(this.realRootOnce??=Hn(this.root)),r=le(e);for(;;){let i=await Hn(r).catch(o=>{if(o.code!=="ENOENT")throw o});if(i!==void 0){if(i!==n&&!i.startsWith(n+Ye))throw new Error(`refusing a path that leaves the vault through a link: ${e}`);return}let s=le(r);if(s===r)return;r=s}}absolute(e){let n=jn(this.root,e),r=xi(this.root,n);if(r===""||r===".."||r.startsWith(`..${Ye}`))throw new Error(`refusing a path outside the vault: ${e}`);let i=r.split(Ye)[0];if(this.ignore.has(i))throw new Error(`refusing to write inside ${i}, which is never synced: ${e}`);return n}async list(){let e=async(n,r)=>{let i;try{i=await Kn(n,{withFileTypes:!0})}catch(h){throw new Error(`cannot read ${n}: ${h.message}`)}let s=i.filter(h=>!this.ignore.has(h.name)&&!Gn(h.name)&&(h.isDirectory()||h.isFile())),o=await Promise.all(s.map(h=>h.isFile()?qe(Xe(n,h.name)):void 0)),a=await Promise.all(s.map(h=>h.isDirectory()?e(Xe(n,h.name),r?`${r}/${h.name}`:h.name):void 0)),l=[];for(let h=0;h<s.length;h++){let c=s[h],u=r?`${r}/${c.name}`:c.name;if(c.isDirectory())l.push({path:u,folder:!0,mtime:0,ctime:0,size:0}),l.push(...a[h]);else{let f=o[h];l.push({path:u,folder:!1,mtime:f.mtimeMs,ctime:f.birthtimeMs||f.ctimeMs,size:f.size})}}return l};return e(this.root,"")}async read(e){return new Uint8Array(await qn(this.absolute(e)))}async*readBlocks(e,n=1024*1024){let r=await et(this.absolute(e),"r");try{let i=new Uint8Array(n);for(;;){let{bytesRead:s}=await r.read(i,0,n,null);if(s===0)return;yield i.slice(0,s)}}finally{await r.close()}}async readRange(e,n,r){let i=await et(this.absolute(e),"r");try{let s=new Uint8Array(r-n),o=0;for(;o<s.length;){let{bytesRead:a}=await i.read(s,o,s.length-o,n+o);if(a===0)break;o+=a}return o===s.length?s:s.subarray(0,o)}finally{await i.close()}}async write(e,n,r){let i=this.absolute(e);if(await this.insideForReal(i),await Qe(le(i),{recursive:!0}),await this.matchCase(i),await Yn(i,n,!1),this.unflushed.add(le(i)),r.mtime>0){let s=r.mtime/1e3;await Ai(i,s,s)}}async matchCase(e){let n;try{n=await qe(e)}catch{return}if(n.isDirectory())return;let r=le(e),i=Ze(e),s;try{s=await Kn(r)}catch{return}if(s.includes(i))return;let o=i.normalize("NFC").toLowerCase(),a=s.find(l=>l.normalize("NFC").toLowerCase()===o);a!==void 0&&await Rt(Xe(r,a),e)}async remove(e){let n=this.absolute(e);await this.insideForReal(n);try{await _t(n,Tt.F_OK)}catch{return}let r=await this.freeTrashPath(e);await Qe(le(r),{recursive:!0});try{await Rt(n,r);return}catch(i){if(i.code!=="EXDEV")throw i}await Si(n,r,{recursive:!0}),await Ei(n,{recursive:!0,force:!0})}async freeTrashPath(e){let n=Xe(this.root,Zn,e),r=Ze(e).lastIndexOf("."),[i,s]=r<=0?[n,""]:[n.slice(0,n.length-(Ze(e).length-r)),n.slice(n.length-(Ze(e).length-r))];for(let o=0;o<1e3;o++){let a=o===0?n:`${i} (${o})${s}`;try{await _t(a,Tt.F_OK)}catch{return a}}throw new Error(`the trash already holds a thousand copies of ${e}`)}async mkdir(e){let n=this.absolute(e);await this.insideForReal(n),await Qe(n,{recursive:!0})}async exists(e){try{return await _t(this.absolute(e),Tt.F_OK),!0}catch{return!1}}async sameFile(e,n){if(e===n)return!0;try{let[r,i]=await Promise.all([qe(this.absolute(e)),qe(this.absolute(n))]);return r.dev===i.dev&&r.ino===i.ino}catch{return!1}}watch(e){let n,r="",i;try{i=ki(this.root,{recursive:!0,persistent:!0},(s,o)=>{if(!o)return;let a=o.toString().split(Ye).join("/");a.split("/").some(l=>this.ignore.has(l))||Gn(a)||(r=a,n&&clearTimeout(n),n=setTimeout(()=>{n=void 0,e(r)},150))}),i.on("error",()=>{})}catch{return()=>{}}return()=>{n&&clearTimeout(n),i?.close()}}},ze=class{constructor(e){this.file=e}file;lastWritten;async load(){let e;try{e=await qn(this.file,"utf8")}catch(n){if(n.code==="ENOENT")return;throw new Error(`cannot read the index at ${this.file}: ${n.message}`)}try{return JSON.parse(e)}catch(n){throw new Error(`the index at ${this.file} is not valid JSON, so it cannot be trusted: ${n.message}`)}}async save(e){let n=JSON.stringify(e);n!==this.lastWritten&&(await Qe(le(this.file),{recursive:!0}),await Yn(this.file,new TextEncoder().encode(n)),this.lastWritten=n)}},Xn=".basalt-tmp-";function Gn(t){return t.includes(Xn)}var $i=0;async function Mi(t){for(let e=0;e<64;e++){let n=`${t}${Xn}${($i++).toString(36)}${e?`-${e}`:""}`;try{return{tmp:n,handle:await et(n,"wx")}}catch(r){if(r.code!=="EEXIST")throw r}}throw new Error(`could not find an unused temporary name beside ${t}`)}async function Yn(t,e,n=!0){let{tmp:r,handle:i}=await Mi(t);try{await i.write(e),await i.sync()}finally{await i.close()}await Rt(r,t),n&&await Qn(le(t))}async function Qn(t){let e=await et(t,"r");try{await e.sync()}finally{await e.close()}}import{chmod as Ci,mkdir as Ti,readFile as _i,rename as Ri,rm as er,writeFile as Ii}from"node:fs/promises";import{join as Dt}from"node:path";var Ut=".basalt",Ft=t=>Dt(t,Ut,"config.json"),nt=t=>Dt(t,Ut,"index.json");async function De(t){let e=Ft(t),n;try{n=await _i(e,"utf8")}catch(i){if(i.code==="ENOENT")return;throw new Error(`cannot read ${e}: ${i.message}`)}let r;try{r=JSON.parse(n)}catch(i){throw new Error(`${e} is not valid JSON, so it cannot be trusted: ${i.message}`)}return Vn(r,e)}async function rt(t,e){let n=Dt(t,Ut);await Ti(n,{recursive:!0});let r=Ft(t),i=`${r}.tmp`;await Ii(i,JSON.stringify(Wn(e),null,2)+`
9
- `,{mode:384}),await Ci(i,384),await Ri(i,r)}async function tr(t){await er(Ft(t),{force:!0}),await er(nt(t),{force:!0})}var nr=`basalt: self-hosted sync for Obsidian
8
+ `).sort();if(n.length!==r.length)return!1;for(let i=0;i<n.length;i++)if(n[i]!==r[i])return!1;return!0}function bn(t,e,n,r){let[i,s]=t.patch_apply(t.patch_make(e,n),r);return{text:i,failed:s.filter(o=>!o).length,total:s.length}}function Xr(t,e){for(let[n,r]of t)if(n===Gr&&!e.includes(r))return r}function Yr(t){let e=t.replace(/\s+/g," ").trim(),n=e.length>60?`${e.slice(0,57)}...`:e;return`a local edit (${JSON.stringify(n)})`}function En(t,e,n){let r=t.lastIndexOf("."),i=t.lastIndexOf("/"),s=r>i,o=s?t.slice(0,r):t,a=s?t.slice(r):"",l=(c,u=2)=>String(c).padStart(u,"0"),h=`${n.getFullYear()}${l(n.getMonth()+1)}${l(n.getDate())}${l(n.getHours())}${l(n.getMinutes())}`;return`${o} (Conflicted copy ${Qr(e)} ${h})${a}`}function Qr(t){return t.replace(/[-\\/:*?"<>|\s]/g,"-").replace(/-{2,}/g,"-").replace(/^[.\-\s]+|[.\-\s]+$/g,"").slice(0,32)||"device"}function kt(t){return{path:t,prev:"",folder:!1,ctime:0,mtime:0,size:0,hash:"",chunks:[],synchash:"",syncuid:0,synctime:0}}function An(t){let{local:e,remote:n,index:r,mergeable:i}=t,s=r.synchash;return e?.folder||n?.folder?ei(e,n):e===void 0&&(n===void 0||n.deleted)?{kind:"nothing",why:"absent on both sides"}:n===void 0?{kind:"upload",why:"new file, the server has never held this path"}:e===void 0?ti(n,r):n.deleted?e.hash===s&&s!==""?{kind:"deleteLocal",why:"deleted on another device and unchanged here"}:s===""?{kind:"upload",why:"deleted on the server but never synced from here"}:{kind:"upload",why:"deleted on another device but edited here, so the edit is kept and re-sent"}:e.hash===n.hash?{kind:"nothing",why:"the two sides hold the same content"}:s===""?{kind:"conflict",why:"both sides have content and there is no last-synced version to merge from"}:e.hash===s?{kind:"download",why:"changed on another device and unchanged here"}:n.hash===s?{kind:"upload",why:"changed here and unchanged on the server"}:i?{kind:"merge",why:"changed on both sides since the last sync"}:{kind:"conflict",why:"changed on both sides and this file cannot be merged"}}function ei(t,e){return t!==void 0&&t.folder?e===void 0?{kind:"upload",why:"new folder, the server has never held this path"}:e.deleted?{kind:"nothing",why:"folder deleted elsewhere; its files decide"}:e.folder?{kind:"nothing",why:"folder exists on both sides"}:{kind:"clash",why:"a folder here and a file of the same name on another device"}:e?.folder&&!e.deleted?t!==void 0?{kind:"clash",why:"a file here and a folder of the same name on another device"}:{kind:"createLocalFolder",why:"folder exists on the server and not here"}:{kind:"nothing",why:"folder absent on both sides"}}function ti(t,e){let n=e.synchash;return t.deleted?{kind:"nothing",why:"deleted on both sides"}:n===""?{kind:"download",why:"new on the server"}:t.hash===n?{kind:"deleteRemote",why:"deleted here and unchanged on the server"}:{kind:"restoreLocal",why:"deleted here but changed on another device, so the newer content is restored"}}function St(t,e,n){return t.hash===""||t.chunks.length===0?!0:t.mtime!==e||t.size!==n}function be(t,e){let n=Math.ceil(e.mtime),r=Math.ceil(e.ctime);if(e.folder){t.folder=!0,t.mtime=0,t.ctime=0,t.size=0,t.hash="",t.chunks=[];return}St(t,n,ni(e))&&(t.hash="",t.chunks=[]),t.folder=!1,t.mtime=n,t.ctime=r,t.size=e.size}function ni(t){return t.size}function xn(t,e,n){t.path=n,t.prev===""&&(t.prev=e),t.prev===n&&(t.prev=""),t.synctime=0}function de(t,e,n,r,i){t.hash=e,t.chunks=[...n],t.synchash=e,t.syncuid=r,t.synctime=i,t.prev=""}function Pn(t,e){if(!t.synctime)return!0;let n=t.size>102400?30:t.size>10240?20:10;return e-t.synctime>n*1e3}var Et=2,$n=6e4,_e=256;function Mn(t){return{size:t.size,ctime:t.ctime,mtime:t.mtime,folder:t.folder??!1,deleted:t.deleted??!1,...t.prev?{prev:t.prev}:{}}}var P=class extends Error{constructor(n,r){super(r);this.code=n;this.name="ProtocolError"}code;get fatal(){return["proto","auth","cursor","busy","protostate","badchunk","internal"].includes(this.code)}};function ri(t){return t.startsWith("wss://")?`. If that server has no TLS in front of it, pair with ws://${t.slice(6)} instead`:""}var j=class extends Error{constructor(e){super(e),this.name="ConnectionError"}},Je=class{constructor(e,n){this.url=e;this.opts=n}url;opts;socket;replyWaiter;replyTimer;bodyWaiter;bodyQueue=[];expecting=0;requestsSent=0;closed=!1;closeReason;cursor=0;notifying=Promise.resolve();log(e,...n){this.opts.log?.(e,...n)}get appliedCursor(){return this.cursor}async connect(){if(this.socket)throw new Error("already connected");let n=(this.opts.socketFactory??ii)(this.url);n.binaryType="arraybuffer",this.socket=n,await new Promise((r,i)=>{n.onopen=()=>r(),n.onerror=()=>i(new j(`could not connect to ${this.url}${ri(this.url)}`)),n.onclose=s=>i(new j(`connection closed before opening: ${Cn(s)}`))}),n.onerror=()=>this.die(new j("the connection failed")),n.onclose=r=>this.die(new j(`the connection closed: ${Cn(r)}`)),n.onmessage=r=>this.onFrame(r.data)}die(e){if(this.closed)return;this.closed=!0,this.closeReason=e,this.log("transport closed",e.message),this.disarmReply();let n=this.replyWaiter,r=this.bodyWaiter;this.replyWaiter=void 0,this.bodyWaiter=void 0,n?.reject(e),r?.reject(e);try{this.socket?.close()}catch{}try{this.opts.onClosed?.(e)}catch{}}disarmReply(){this.replyTimer!==void 0&&(clearTimeout(this.replyTimer),this.replyTimer=void 0)}close(){this.die(new j("closed by this device"))}get isClosed(){return this.closed}onFrame(e){if(typeof e=="string"){let i;try{i=JSON.parse(e)}catch{this.die(new P("protostate",`server sent a frame that is not JSON: ${e.slice(0,120)}`));return}this.onTextFrame(i);return}let n=si(e);if(n===void 0){this.die(new P("protostate","server sent a frame of an unexpected type"));return}let r=this.bodyWaiter;if(r){this.bodyWaiter=void 0,r.resolve(n);return}if(this.bodyQueue.length>=this.expecting){this.die(new P("protostate",`server sent a ${n.length} byte body with nothing outstanding to receive it`));return}this.bodyQueue.push(n)}onTextFrame(e){if(e.op==="batch"){this.queueNotification(()=>this.onBatchFrame(e));return}if(e.op==="caught-up"){let r=K(e.cursor);this.queueNotification(async()=>{if(r!==this.cursor){this.die(new P("protostate",`server says caught up at ${r}, this device reached ${this.cursor}`));return}this.opts.onCaughtUp?.(r)});return}let n=this.replyWaiter;if(!n){this.die(new P("protostate",`server sent an unexpected reply: ${JSON.stringify(e)}`));return}this.replyWaiter=void 0,n.resolve(e)}queueNotification(e){this.notifying=this.notifying.then(e).catch(n=>{this.die(n instanceof Error?n:new Error(String(n)))})}async onBatchFrame(e){let n=K(e.from),r=K(e.to),i=e.entries;if(!Array.isArray(i))throw new P("protostate",`batch ${n} to ${r} carries no entries array, so an empty batch cannot be told from a lost one`);let s=i;if(n!==this.cursor+1)throw new P("protostate",`batch covers ${n} to ${r} but this device has applied up to ${this.cursor}, so something was skipped`);if(r<n)throw new P("protostate",`batch covers an empty range, ${n} to ${r}`);for(let o of s){if(typeof o?.uid!="number"||!Number.isFinite(o.uid))throw new P("protostate",`batch ${n}..${r} contains an entry with no uid`);if(typeof o.path!="string"||o.path==="")throw new P("protostate",`batch ${n}..${r} contains uid ${o.uid} with no path`);if(!Array.isArray(o.chunks))throw new P("protostate",`batch ${n}..${r} contains uid ${o.uid} with no chunks array`);if(o.uid<n||o.uid>r)throw new P("protostate",`batch ${n}..${r} contains uid ${o.uid}`)}await this.opts.onBatch({from:n,to:r,entries:s}),this.cursor=r}send(e){if(this.closed||!this.socket)throw this.closeReason??new j("not connected");this.socket.send(JSON.stringify(e))}sendBody(e){if(this.closed||!this.socket)throw this.closeReason??new j("not connected");this.socket.send(e)}async request(e){if(this.replyWaiter)throw new Error("a request is already in flight");this.requestsSent++;let n=this.opts.timeoutMs??$n,r=await new Promise((i,s)=>{let o=setTimeout(()=>{this.die(new j(`no reply within ${n}ms`))},n);this.replyTimer=o,this.replyWaiter={resolve:a=>{this.disarmReply(),i(a)},reject:a=>{this.disarmReply(),s(a)}};try{this.send(e)}catch(a){this.disarmReply(),this.replyWaiter=void 0,s(a instanceof Error?a:new Error(String(a)))}});if(r.res==="err"){let i=new P(String(r.code??"unknown"),String(r.msg??"no message"));throw i.fatal&&this.die(i),i}return r}async body(){let e=this.bodyQueue.shift();if(e)return e;if(this.closed)throw this.closeReason??new j("not connected");return new Promise((n,r)=>{this.bodyWaiter={resolve:n,reject:r}})}async hello(e){this.cursor=e.cursor;let n=await this.request({op:"hello",proto:Et,crypto:Yt,vault:e.vault,token:e.token,device:e.device,cursor:e.cursor,...e.claim!==void 0?{claim:e.claim}:{}});if(n.res!=="ready")throw new P("protostate",`expected ready, got ${JSON.stringify(n)}`);let r={proto:K(n.proto),cursor:K(n.cursor),perFileMax:K(n.perFileMax),chunkMax:K(n.chunkMax),maxChunks:K(n.maxChunks)};if(r.proto!==Et)throw new P("proto",`server speaks protocol ${r.proto}, this client speaks ${Et}`);return this.log("ready",r),r}async put(e,n,r,i,s={mac:"",parent:""}){let o=await this.request({op:"put",path:e,meta:Mn(n),chunks:[...r],mac:s.mac,parent:s.parent});if(o.res==="have")return{uid:K(o.uid),uploaded:0,bytes:0};if(o.res!=="want")throw new P("protostate",`expected want or have, got ${JSON.stringify(o)}`);let a=At(o.chunks),l=new Set(r),h=0;for(let u of a){if(!l.has(u))throw new P("badchunk",`server asked for ${u}, which this put does not contain`);let f=await i(u);this.sendBody(f),h+=f.length}return{uid:await this.awaitAck(),uploaded:a.length,bytes:h}}async putMany(e,n){if(e.length===0)return{results:[],uploaded:0,bytes:0};if(e.length>_e)throw new P("toolarge",`${e.length} entries in one batch, the limit is ${_e}`);let r=await this.request({op:"putmany",entries:e.map(h=>({path:h.path,meta:Mn(h.meta),chunks:[...h.names],mac:h.mac,parent:h.parent}))}),i=r,s=0,o=0;if(r.res==="want"){let h=At(r.chunks),c=new Set;for(let u of e)for(let f of u.names)c.add(f);for(let u of h){if(!c.has(u))throw new P("badchunk",`server asked for ${u}, which this batch does not contain`);let f=await n(u);this.sendBody(f),o+=f.length}s=h.length,i=await this.awaitReply()}if(i.res!=="acks")throw new P("protostate",`expected acks, got ${JSON.stringify(i)}`);let a=i.results;if(!Array.isArray(a)||a.length!==e.length)throw new P("protostate",`${e.length} entries went up and ${Array.isArray(a)?a.length:"no"} results came back`);let l=a.map(h=>{let c=h??{};return c.code!==void 0?{uid:0,error:new P(String(c.code),String(c.msg??"no message"))}:{uid:K(c.uid)}});for(let h of l)h.error?.fatal&&this.die(h.error);return{results:l,uploaded:s,bytes:o}}async awaitAck(){let e=await this.awaitReply();if(e.res!=="ack")throw new P("protostate",`expected ack, got ${JSON.stringify(e)}`);return K(e.uid)}async awaitReply(){let e=await new Promise((n,r)=>{if(this.closed){r(this.closeReason??new j("not connected"));return}let i=this.opts.timeoutMs??$n,s=setTimeout(()=>{this.die(new j(`no acknowledgement within ${i}ms`))},i);this.replyWaiter={resolve:o=>{clearTimeout(s),n(o)},reject:o=>{clearTimeout(s),r(o)}}});if(e.res==="err"){let n=new P(String(e.code??"unknown"),String(e.msg??"no message"));throw n.fatal&&this.die(n),n}return e}async get(e){let n=await this.request({op:"get",uid:e});if(n.res!=="chunks")throw new P("protostate",`expected chunks, got ${JSON.stringify(n)}`);return{uid:K(n.uid),size:K(n.size),chunks:At(n.chunks)}}async history(e,n={}){let r=await this.request({op:"history",path:e,...n.before!==void 0?{before:n.before}:{},...n.limit!==void 0?{limit:n.limit}:{}});if(r.res!=="history")throw new P("protostate",`expected history, got ${JSON.stringify(r)}`);return Tn(r.entries,"history")}async deleted(e){let n=await this.request({op:"deleted",...e!==void 0?{limit:e}:{}});if(n.res!=="deleted")throw new P("protostate",`expected deleted, got ${JSON.stringify(n)}`);return{entries:Tn(n.entries,"deleted"),more:n.more===!0}}async fetch(e){if(e.length===0)return[];this.expecting+=e.length;let n=this.request({op:"fetch",chunks:[...e]}),r=[],i=[];try{for(let s=0;s<e.length;s++){let o=await Promise.race([this.body(),n.then(l=>{throw new P("protostate",`expected a chunk body, got ${JSON.stringify(l)}`)})]),a=e[s];i.push(mt(o).then(l=>{if(l!==a)throw new P("badchunk",`asked for ${a} and received ${o.length} bytes that hash to ${l}`)})),r.push(o)}await Promise.all(i)}catch(s){throw this.expecting=0,this.disarmReply(),this.replyWaiter=void 0,s instanceof P&&s.code==="badchunk"&&this.die(s),s}return this.expecting=Math.max(0,this.expecting-e.length),this.disarmReply(),this.replyWaiter=void 0,r}async ping(){let e=await this.request({op:"ping"});if(e.res!=="pong")throw new P("protostate",`expected pong, got ${JSON.stringify(e)}`)}},Ke=class{constructor(e=0,n=3e5,r=5e3,i=!0,s=Math.random){this.min=e;this.max=n;this.base=r;this.jitter=i;this.random=s}min;max;base;jitter;random;count=0;nextAt=0;success(e){this.count=0,this.nextAt=e+this.delay()}fail(e){this.count++,this.nextAt=e+this.delay()}delay(){if(this.count===0)return this.min;let e=this.base*Math.pow(2,this.count-1);return this.jitter&&(e*=.5+.5*this.random()),Math.floor(Math.min(this.max,this.min+e))}readyAt(){return this.nextAt}isReady(e){return e>=this.nextAt}get failures(){return this.count}};function ii(t){let e=globalThis.WebSocket;if(!e)throw new Error("no WebSocket available in this environment");return new e(t)}function si(t){if(t instanceof Uint8Array)return t;if(t instanceof ArrayBuffer)return new Uint8Array(t);if(ArrayBuffer.isView(t))return new Uint8Array(t.buffer,t.byteOffset,t.byteLength)}function K(t){return typeof t=="number"&&Number.isFinite(t)?t:0}function At(t){return Array.isArray(t)?t.filter(e=>typeof e=="string"):[]}function Cn(t){let e=t.code??0,n=t.reason?`, ${t.reason}`:"";return`code ${e}${n}`}function Tn(t,e){if(!Array.isArray(t))throw new P("protostate",`${e} came back without a list of entries`);return t}function _n(t){let e=t.split("/");e.pop();let n=[],r="";for(let i of e)r=r?`${r}/${i}`:i,n.push(r);return n}function oi(t){let e={...t};return t.hash===q(t.chunks)&&delete e.hash,t.synchash===t.hash&&delete e.synchash,e}function ai(t,e){let n={...kt(t),...e};return e.hash===void 0&&(n.hash=q(n.chunks)),e.synchash===void 0&&(n.synchash=n.hash),n}function q(t){return t.length===0?"-empty-":t.join(",")}function li(t,e){if(t<e)throw new Error(`this server is at version ${t} and this device has already seen ${e}: refusing to sync, because a server behind its own clients is a restored backup or the wrong vault`)}var Rn={perFileMax:1<<28,maxChunks:65536};function In(t,e){return t>0?Math.min(t,e):e}function hi(t){if(!t.folder&&!t.deleted&&t.size>0&&t.chunks.length===0)throw new Error(`version ${t.uid} declares ${t.size} bytes and names no chunks, which cannot both be true`);if(t.chunks.length>0&&(t.folder||t.deleted)){let e=t.folder?"a folder":"a deletion";throw new Error(`version ${t.uid} is ${e} and names ${t.chunks.length} chunks`)}}function ci(t){return t===""||t==="-empty-"?[]:t.split(",")}var On=5;function zn(){return{uploaded:0,downloaded:0,merged:0,conflicted:0,deletedLocally:0,deletedRemotely:0,restored:0,foldersCreated:0,unchanged:0,waiting:0,retrying:0,skipped:0,blocked:0,inTheWay:[],chunksSent:0,bytesSent:0}}var je=class{constructor(e){this.opts=e}opts;entries=new Map;remote=new Map;pending=new Set;retries=new Map;skipped=new Map;blocked=new Set;cannotStream=!1;outbox=[];outboxBytes=0;inbox=[];inboxBytes=0;limits;unsealed=new Map;cursor=0;syncing=!1;again=!1;started=!1;now(){return this.opts.now?.()??Date.now()}log(e,...n){this.opts.log?.(e,...n)}mergeable(e){return(this.opts.mergeable??pn)(e)}sizesFor(e,n){return dn(e,n,this.limits?.chunkMax)}get coalesce(){return this.opts.coalesceWrites??!0}status(){let e=0;for(let n of this.entries.values())n.folder||e++;return{cursor:this.cursor,files:e,pending:this.pending.size,retrying:this.retries.size,skipped:this.skipped.size,syncing:this.syncing}}async start(){if(this.started)throw new Error("already started");this.started=!0;let e=await this.opts.store.load();if(e){this.cursor=e.cursor;for(let[r,i]of Object.entries(e.entries))this.entries.set(r,ai(r,i));for(let[r,i]of Object.entries(e.remote))this.remote.set(r,i);for(let r of e.pending)this.pending.add(r);this.log("index loaded",{cursor:this.cursor,entries:this.entries.size,pending:this.pending.size})}let n=await this.opts.transport.hello({vault:this.opts.vaultId,token:this.opts.token,device:this.opts.device,cursor:this.cursor,...this.opts.claim!==void 0?{claim:this.opts.claim}:{}});return li(n.cursor,this.cursor),this.limits=n,this.log("connected",n),n}async authFor(e,n){let r=await cn(n);return{mac:await yt(this.opts.keys,{path:e.path,size:e.meta.size,ctime:e.meta.ctime,mtime:e.meta.mtime,folder:e.meta.folder??!1,deleted:e.meta.deleted??!1,prev:e.meta.prev,chunks:e.names,parent:r}),parent:r}}async acceptBatch(e){let n=e.entries.map(l=>({path:l.path,size:l.size,ctime:l.ctime,mtime:l.mtime,folder:l.folder,deleted:l.deleted,prev:l.prev,chunks:l.chunks,parent:l.parent??""})),i=(await Promise.all(n.map((l,h)=>hn(this.opts.keys,l,e.entries[h].mac)))).indexOf(!1);if(i>=0)throw new Error(`version ${e.entries[i].uid} is not authenticated by this vault's key, so nothing that holds the key wrote it`);for(let l of e.entries)hi(l);let s=await Promise.all(e.entries.map(l=>this.plaintextPath(l.path))),o=await Promise.all(e.entries.map(l=>l.prev?this.plaintextPath(l.prev):void 0)),a=new Map;for(let l=0;l<e.entries.length;l++){let h=e.entries[l],c=s[l];if(a.set(c,{uid:h.uid,folder:h.folder,deleted:h.deleted,mtime:h.mtime,size:h.size,hash:q(h.chunks)}),h.prev){let u=o[l];a.set(u,{uid:h.uid,folder:!1,deleted:!0,mtime:h.mtime,size:0,hash:""})}}for(let[l,h]of a)this.remote.set(l,h),this.pending.add(l);this.cursor=e.to}async plaintextPath(e){let n=this.unsealed.get(e);if(n!==void 0)return n;let r=await Ve(this.opts.keys,e);return this.unsealed.set(e,r),r}async sync(e={}){if(this.syncing)return this.again=!0,zn();this.syncing=!0;try{let n=await this.pass(e);for(;this.again;){this.again=!1;let r=await this.pass(e);n=ui(n,r)}return n}finally{this.syncing=!1}}async pass(e={}){let n=zn(),r=this.now(),i=e.coalesceWrites??this.coalesce;(this.outbox.length>0||this.inbox.length>0)&&(this.log("a pass ended early, discarding what it had queued",{writes:this.outbox.length,reads:this.inbox.length}),this.outbox=[],this.outboxBytes=0,this.inbox=[],this.inboxBytes=0);let s=await this.opts.vault.list(),o=new Map(s.map(c=>[c.path,c]));for(let c of s){let u=this.entryFor(c.path);be(u,c)}let a=new Set;for(let[c,u]of o)u.folder||a.add(c);let l=new Set,h=new Set([...o.keys(),...this.entries.keys(),...this.remote.keys()]);for(let c of[...h].sort()){let u=this.skipped.get(c);if(u){if(xt(this.entries.get(c))===u.fingerprint){n.skipped++;continue}this.skipped.delete(c),this.log("skipped file changed, trying again",c)}let f=_n(c).find(m=>a.has(m));if(f!==void 0&&!o.has(c)){l.add(c),this.blocked.has(c)||this.log("cannot be both",c,`${f} is a file here and a folder elsewhere`),n.blocked++,n.inTheWay.length<On&&n.inTheWay.push({path:c,blockedBy:f});continue}let g=this.retries.get(c);if(g&&g.at>r){n.retrying++;continue}try{this.opts.onProgress?.(c),await this.reconcile(c,o.get(c),n,r,i),this.retries.delete(c)}catch(m){this.recordFailure(c,m,n)}}return this.wroteThisPass=[],await this.fill(n),await this.applyDeletes(n),this.wroteThisPass=[],await this.flush(n),this.opts.onProgress?.(void 0),this.blocked=l,this.prune(o),await this.opts.vault.flush?.(),await this.save(),n}entryFor(e){let n=this.entries.get(e);return n||(n=kt(e),this.entries.set(e,n)),n}async reconcile(e,n,r,i,s){let o=this.entryFor(e),a=this.remote.get(e),l=this.limits?.perFileMax??0;if(n&&!n.folder&&l>0&&n.size>l){this.recordFailure(e,gi(n.size,l),r);return}let h,c;n&&(!n.folder&&St(o,Math.ceil(n.mtime),n.size)&&(c=await this.rehash(o,e,n.size)),h={folder:n.folder,mtime:o.mtime,size:o.size,hash:o.hash});let u=An({local:h,remote:a,index:o,mergeable:this.mergeable(e)});if(s&&u.kind!=="nothing"&&h&&!n?.folder&&!Pn(o,i)){r.waiting++;return}await this.act(e,u,o,h,a,r,c),this.pending.delete(e)}async rehash(e,n,r){let i=await this.streamScan(e,n,r);if(i)return i;let s=await this.opts.vault.read(n),o=this.mergeable(n),a=[...bt(s,this.sizesFor(s.length,o),o)],l=a.map(h=>h.bytes);if(s.length<=Dn){let h=await ue(this.opts.keys,l);return e.chunks=h.map(c=>c.name),e.hash=q(e.chunks),e.size=s.length,{bytes:s,pieces:a,names:e.chunks,sealed:h}}return e.chunks=await this.namesOf(l),e.hash=q(e.chunks),e.size=s.length,{bytes:s,pieces:a,names:e.chunks}}async streamScan(e,n,r){let i=this.opts.vault;if(!(!i.readBlocks||!i.readRange||this.cannotStream)&&!(r===void 0||r<=Dn))try{return await this.streamed(e,n,r)}catch(s){this.cannotStream=!0,this.log("streaming is not available here, reading whole files instead",n,{why:s.message});return}}async streamed(e,n,r){let i=this.opts.vault,s=this.mergeable(n),o=[],a=[],l=0;for await(let h of mn(i.readBlocks(n),this.sizesFor(r,s),s)){let c=await ue(this.opts.keys,[h.bytes]);o.push(c[0].name),a.push({start:h.offset,end:h.offset+h.bytes.length}),l+=h.bytes.length}return e.chunks=o,e.hash=q(o),e.size=l,{names:o,spans:a,path:n,size:l}}namesOf(e){return fi(this.opts.keys,e)}async act(e,n,r,i,s,o,a){switch(n.kind){case"nothing":o.unchanged++,i&&s&&!s.deleted&&i.hash===s.hash&&de(r,i.hash,r.chunks,s.uid,this.now());return;case"upload":await this.upload(e,r,o,!0,a);return;case"download":case"restoreLocal":{if(!s)return;await this.receive(e,r,s,n.kind,n.why,o);return}case"createLocalFolder":await this.opts.vault.mkdir(e),r.folder=!0,s&&de(r,"",[],s.uid,this.now()),o.foldersCreated++;return;case"clash":this.skipped.set(e,{why:`${n.why}. Rename one of them, and it will sync.`,fingerprint:xt(r)}),o.skipped++,this.log("cannot be both",e,n.why);return;case"deleteLocal":this.pendingDeletes.push({path:e,why:n.why});return;case"deleteRemote":{let l=this.now(),h={path:await this.sealedPath(e),meta:{size:0,ctime:0,mtime:l,deleted:!0},names:[]};await this.queue({path:e,size:0,entry:{...h,...await this.authFor(h,r.synchash)},bodyOf:Fn,commit:c=>{this.remote.set(e,{uid:c,folder:!1,deleted:!0,mtime:this.now(),size:0,hash:""}),this.entries.delete(e),o.deletedRemotely++,this.log("deleted on the server",e,n.why)}},o);return}case"merge":await this.merge(e,r,s,o);return;case"conflict":await this.conflict(e,r,s,o,n.why);return}}async upload(e,n,r,i=!1,s){if(n.folder){let f={path:await this.sealedPath(e),meta:{size:0,ctime:0,mtime:0,folder:!0},names:[]};await this.queue({path:e,size:0,entry:{...f,...await this.authFor(f,"")},bodyOf:Fn,commit:g=>{de(n,"",[],g,this.now()),this.remote.set(e,{uid:g,folder:!0,deleted:!1,mtime:0,size:0,hash:""}),i&&r.uploaded++}},r);return}let o=await this.planUpload(n,e,s),a=n.hash,l=[...n.chunks],h=n.size,c=n.mtime,u={path:await this.sealedPath(e),meta:{size:h,ctime:n.ctime,mtime:c,...n.prev?{prev:await this.sealedPath(n.prev)}:{}},names:o.names};await this.queue({path:e,size:h,entry:{...u,...await this.authFor(u,n.synchash)},bodyOf:o.bodyOf,commit:f=>{de(n,a,l,f,this.now()),this.remote.set(e,{uid:f,folder:!1,deleted:!1,mtime:c,size:h,hash:a}),i&&r.uploaded++,this.log("uploaded",e)}},r)}async queue(e,n){this.outbox.push(e),this.outboxBytes+=e.size,(this.outbox.length>=_e||this.outboxBytes>=Un)&&await this.flush(n)}async flush(e){if(this.outbox.length===0)return;let n=this.outbox;this.outbox=[],this.outboxBytes=0;let r=new Map;for(let s of n)for(let o of s.entry.names)r.has(o)||r.set(o,s.bodyOf);let i;try{i=await this.opts.transport.putMany(n.map(s=>s.entry),async s=>{let o=r.get(s);if(!o)throw new Error(`server asked for ${s}, which no queued file contains`);return o(s)})}catch(s){for(let o of n)this.recordFailure(o.path,s,e);return}e.chunksSent+=i.uploaded,e.bytesSent+=i.bytes;for(let s=0;s<n.length;s++){let o=n[s],a=i.results[s];if(a.error){this.recordFailure(o.path,a.error,e);continue}o.commit(a.uid)}}async planUpload(e,n,r){let i=r??await this.scan(e,n);if(i.sealed){let h=new Map(i.sealed.map(c=>[c.name,c.bytes]));return{names:i.names,bodyOf:async c=>{let u=h.get(c);if(!u)throw new Error(`no sealed body for ${c} of ${n}`);return u}}}let s=this.opts.keys,o=this.opts.vault;if(i.spans){let h=new Map(i.names.map((c,u)=>[c,i.spans[u]]));return{names:i.names,bodyOf:async c=>{let u=h.get(c);if(!u)throw new Error(`no chunk named ${c} in ${n}`);let f=await o.readRange(i.path,u.start,u.end),g=await ue(s,[f]);if(g[0].name!==c)throw new Error(`${n} changed while it was being sent, so it was not sent`);return g[0].bytes}}}let a=new Map;for(let h=0;h<i.names.length;h++){let c=i.pieces[h];a.set(i.names[h],{start:c.offset,end:c.offset+c.bytes.length})}let l=i.bytes;return{names:i.names,bodyOf:async h=>{let c=a.get(h);if(!c)throw new Error(`no chunk named ${h} in ${n}`);return(await ue(s,[l.subarray(c.start,c.end)]))[0].bytes}}}scan(e,n){return this.rehash(e,n)}async receive(e,n,r,i,s,o){let a=ci(r.hash);this.checkChunkCount(r.uid,a.length),this.inbox.push({path:e,entry:n,remote:r,chunks:a,kind:i,why:s}),this.inboxBytes+=r.size,(this.inbox.length>=_e||this.inboxBytes>=Un)&&await this.fill(o)}async fill(e){if(this.inbox.length===0)return;let n=this.inbox;this.inbox=[],this.inboxBytes=0;let r=new Map,i=this.heldByContent();for(let l of n){let h=i.get(q(l.chunks));h!==void 0&&h!==l.path&&r.set(l,h)}let s=[],o=new Set;for(let l of n)if(!r.has(l))for(let h of l.chunks)o.has(h)||(o.add(h),s.push(h));let a=new Map;if(s.length>0)try{let l=await this.opts.transport.fetch(s);for(let h=0;h<s.length;h++)a.set(s[h],l[h])}catch(l){for(let h of n)this.recordFailure(h.path,l,e);return}for(let l of n)try{let h=r.get(l);if(h!==void 0&&await this.landFromLocal(l,h)){l.kind==="download"?e.downloaded++:e.restored++,this.log(l.kind,l.path,`${l.why}, from ${h} without asking`);continue}h!==void 0?await this.land(l,await this.fetchFor(l)):await this.land(l,a),l.kind==="download"?e.downloaded++:e.restored++,this.log(l.kind,l.path,l.why)}catch(h){this.recordFailure(l.path,h,e)}}pendingDeletes=[];wroteThisPass=[];async wouldUndoAWrite(e){let n=this.opts.vault;for(let r of this.wroteThisPass){if(r===e)continue;if(n.sameFile?await n.sameFile(r,e):r.normalize("NFC").toLowerCase()===e.normalize("NFC").toLowerCase())return r}}async applyDeletes(e){let n=this.pendingDeletes;this.pendingDeletes=[];for(let{path:r,why:i}of n)try{let s=await this.wouldUndoAWrite(r);if(s!==void 0){this.entries.delete(r),this.log("kept",r,`deleting it would remove ${s}, which is the same file`);continue}await this.opts.vault.remove(r),this.entries.delete(r),e.deletedLocally++,this.log("deleted locally",r,i)}catch(s){this.recordFailure(r,s,e)}}heldByContent(){let e=new Map;for(let[n,r]of this.entries)r.folder||r.hash===""||r.hash==="-empty-"||e.set(r.hash,n);return e}async fetchFor(e){let n=await this.opts.transport.fetch([...e.chunks]),r=new Map;return e.chunks.forEach((i,s)=>r.set(i,n[s])),r}async landFromLocal(e,n){let r;try{r=await this.opts.vault.read(n)}catch{return!1}if(r.length!==e.remote.size)return!1;let i=this.mergeable(n),s=[...bt(r,this.sizesFor(r.length,i),i)].map(a=>a.bytes),o=(await ue(this.opts.keys,s)).map(a=>a.name);return q(o)!==q(e.chunks)?!1:(await this.opts.vault.write(e.path,r,{mtime:e.remote.mtime,ctime:e.remote.mtime}),this.wroteThisPass.push(e.path),be(e.entry,{folder:!1,mtime:e.remote.mtime,ctime:e.remote.mtime,size:r.length}),e.entry.chunks=[...e.chunks],e.entry.hash=q(e.chunks),e.entry.size=r.length,de(e.entry,e.entry.hash,e.entry.chunks,e.remote.uid,this.now()),!0)}async land(e,n){let r=e.chunks.map(s=>{let o=n.get(s);if(!o)throw new Error(`the server did not send ${s}, which ${e.path} is made of`);return o}),i=await this.assemble(e.remote.uid,r);if(i.length!==e.remote.size)throw new Error(`version ${e.remote.uid} of ${e.path} assembled to ${i.length} bytes, not the ${e.remote.size} it declares`);await this.opts.vault.write(e.path,i,{mtime:e.remote.mtime,ctime:e.remote.mtime}),this.wroteThisPass.push(e.path),be(e.entry,{folder:!1,mtime:e.remote.mtime,ctime:e.remote.mtime,size:i.length}),e.entry.chunks=[...e.chunks],e.entry.hash=q(e.chunks),e.entry.size=i.length,de(e.entry,e.entry.hash,e.entry.chunks,e.remote.uid,this.now())}async contentOf(e,n,r){let i=n!==void 0?{chunks:n}:await this.opts.transport.get(e);if(r!==void 0&&q(i.chunks)!==r)throw new Error(`version ${e} is made of chunks this device did not record for it, so it is not the version it is being offered as`);return i.chunks.length===0?new Uint8Array(0):(this.checkChunkCount(e,i.chunks.length),this.assemble(e,await this.opts.transport.fetch(i.chunks)))}checkChunkCount(e,n){let r=In(this.limits?.maxChunks??0,Rn.maxChunks);if(n>r)throw new Error(`version ${e} names ${n} chunks, and this server said it stores at most ${r}`)}async assemble(e,n){if(n.length===0)return new Uint8Array(0);let r=[],i=0,s=In(this.limits?.perFileMax??0,Rn.perFileMax);for(let l=0;l<n.length;l+=Pt){let h=await Promise.all(n.slice(l,l+Pt).map(c=>an(this.opts.keys,c)));for(let c of h){if(i+=c.length,i>s)throw new Error(`version ${e} is over ${i} bytes, and this server said it stores at most ${s}`);r.push(c)}}let o=new Uint8Array(i),a=0;for(let l of r)o.set(l,a),a+=l.length;return o}async merge(e,n,r,i){if(!r)return;let s=new TextDecoder("utf-8",{fatal:!0}),o,a,l;try{o=s.decode(await this.contentOf(n.syncuid,void 0,n.synchash)),a=s.decode(await this.opts.vault.read(e)),l=s.decode(await this.contentOf(r.uid,void 0,r.hash))}catch{let u="one side is not valid UTF-8, so merging it would rewrite bytes nobody edited";this.log("merge refused",e,u),await this.conflict(e,n,r,i,u);return}let h=Sn(o,a,l,yn(e)?mi:void 0);if(h.kind==="conflict"){this.log("merge refused",e,h.why),await this.conflict(e,n,r,i,h.why);return}let c=h.text;c!==a&&await this.opts.vault.write(e,new TextEncoder().encode(c),{mtime:this.now(),ctime:n.ctime}),be(n,{folder:!1,mtime:this.now(),ctime:n.ctime,size:c.length}),await this.upload(e,n,i),i.merged++,this.log("merged",e,h.kind==="merged"?"three-way":h.why)}freeConflictPath(e){return di(En(e,this.opts.device,new Date(this.now())),n=>this.opts.vault.exists(n))}async conflict(e,n,r,i,s){if(!r)return;let o=await this.freeConflictPath(e),a=await this.contentOf(r.uid,void 0,r.hash);await this.opts.vault.write(o,a,{mtime:r.mtime,ctime:r.mtime});let l=this.entryFor(o);be(l,{folder:!1,mtime:r.mtime,ctime:r.mtime,size:a.length}),await this.upload(o,l,i),await this.upload(e,n,i),i.conflicted++,this.log("kept both",e,{copy:o,why:s})}async sealedPath(e){let n=await Le(this.opts.keys,e);return this.unsealed.set(n,e),n}recordFailure(e,n,r){let i=n instanceof Error?n.message:String(n),s=n?.code;if(s!==void 0&&["badentry","badname","toolarge"].includes(s)){this.skipped.set(e,{why:i,fingerprint:xt(this.entries.get(e))}),r.skipped++,this.log("skipped for good",e,i);return}let a=this.retries.get(e)??{count:0,error:"",at:0};a.count++,a.error=i,a.at=this.now()+Math.min(3e5,5e3*Math.pow(2,a.count)),this.retries.set(e,a),r.retrying++,this.log("will retry",e,{attempt:a.count,error:i})}prune(e){for(let[n,r]of this.entries){if(e.has(n))continue;let i=this.remote.get(n);i&&!i.deleted||r.synchash===""&&r.hash===""&&this.entries.delete(n)}for(let[n,r]of this.remote)r.deleted&&(e.has(n)||this.entries.has(n)||this.pending.has(n)||this.remote.delete(n))}async save(){let e={};for(let[r,i]of this.entries)e[r]=oi(i);let n={};for(let[r,i]of this.remote)n[r]=i;await this.opts.store.save({cursor:this.cursor,entries:e,remote:n,pending:[...this.pending]})}noteRename(e,n){let r=this.entries.get(e);if(!r)return;let i={...r,chunks:[...r.chunks]};xn(i,e,n),this.entries.set(n,i)}};function ui(t,e){let n={...t};for(let r of Object.keys(n))r!=="inTheWay"&&(n[r]=t[r]+e[r]);return n.inTheWay=[...t.inTheWay,...e.inTheWay].slice(0,On),n}function xt(t){return t?`${t.mtime}:${t.size}`:"gone"}var Dn=8*1024*1024,Pt=16;async function fi(t,e,n=Pt){let r=[];for(let i=0;i<e.length;i+=n){let s=await ue(t,e.slice(i,i+n));for(let o of s)r.push(o.name)}return r}var Un=8*1024*1024;async function di(t,e){if(!await e(t))return t;let n=t.lastIndexOf("."),r=t.lastIndexOf("/"),i=n>r,s=i?t.slice(0,n):t,o=i?t.slice(n):"";for(let a=2;a<1e3;a++){let l=`${s} ${a}${o}`;if(!await e(l))return l}throw new Error(`cannot find an unused name beside ${t}`)}function gi(t,e){let n=new Error(`${t} bytes, and this server said it stores at most ${e}, so it was not read`);return n.code="toolarge",n}async function Fn(t){throw new Error(`this put has no bodies, and the server asked for ${t}`)}function mi(t){try{return JSON.parse(t),!0}catch{return!1}}var pi=150,Re=class{constructor(e){this.opts=e;let n;this.transport=new Je(e.url,{onBatch:async r=>{await n.acceptBatch(r),r.entries.length>0&&this.soon()},onCaughtUp:()=>{this.caughtUp=!0},onClosed:r=>{this.endedWith=r,this.notifyEnded?.(r)},...e.timeoutMs!==void 0?{timeoutMs:e.timeoutMs}:{},...e.log!==void 0?{log:e.log}:{},...e.socketFactory!==void 0?{socketFactory:e.socketFactory}:{}}),n=new je({vault:e.vault,store:e.store,keys:e.keys,transport:this.transport,device:e.device,vaultId:e.vaultId,token:e.token,...e.claim!==void 0?{claim:e.claim}:{},...e.coalesceWrites!==void 0?{coalesceWrites:e.coalesceWrites}:{},...e.log!==void 0?{log:e.log}:{},...e.onProgress!==void 0?{onProgress:e.onProgress}:{}}),this.engine=n}opts;engine;transport;limits;soonTimer;caughtUp=!1;endedWith;notifyEnded;queue=Promise.resolve();serial(e){let n=this.queue.then(e,e);return this.queue=n.catch(()=>{}),n}get requestsSent(){return this.transport.requestsSent}get serverCursor(){return this.limits?.cursor??0}async connect(){await this.transport.connect(),this.limits=await this.engine.start();let e=this.opts.timeoutMs??3e4,n=Date.now()+e;for(;!this.caughtUp&&Date.now()<n;){if(this.endedWith)throw this.endedWith;await $t(25)}if(!this.caughtUp)throw new Error("the server never finished sending what it already had");return this.limits}async settle(e={},n=8){let r=await this.pass(e),i=r;for(let s=0;s<n&&bi(r);s++)await $t(60),r=await this.pass(e),i=wi(i,r);return i}pass(e){return this.serial(()=>this.engine.sync(e))}async runUntilClosed(e=3e4){if(this.endedWith)return this.endedWith;let n=this.opts.vault.watch?.(()=>{this.sync()}),r=setInterval(()=>{this.sync().then(()=>this.keepalive())},e);try{return await new Promise(i=>{this.notifyEnded=i})}finally{clearInterval(r),n?.()}}async keepalive(){try{await this.transport.ping()}catch{}}soon(){this.soonTimer===void 0&&(this.soonTimer=setTimeout(()=>{this.soonTimer=void 0,this.sync()},pi))}async sync(e={}){try{return await this.pass(e)}catch(n){this.opts.log?.("sync failed",n.message);return}}async history(e,n={}){let r=await Le(this.opts.keys,e);return(await this.serial(()=>this.transport.history(r,n))).map(s=>this.asVersion(s,e))}async deleted(e){let n=await this.serial(()=>this.transport.deleted(e)),r=[];for(let i of n.entries)r.push({...this.asVersion(i,await Ve(this.opts.keys,i.path)),restorable:i.restorable??0});return{notes:r,more:n.more}}async contentAt(e){return e.deleted||e.folder?new Uint8Array(0):this.serial(()=>this.engine.contentOf(e.uid))}async restore(e,n){if(e.deleted)throw new Error(`version ${e.uid} of ${e.path} is the deletion itself, not a version to restore`);if(e.folder){let o=n??e.path;return await this.opts.vault.mkdir(o),{path:o,bytes:0}}let r=await this.serial(()=>this.engine.contentOf(e.uid)),i=n??e.path,s=await this.opts.vault.exists(i)?yi(i,e):i;return await this.opts.vault.write(s,r,{mtime:e.mtime,ctime:e.ctime}),{path:s,bytes:r.length}}async newestContentVersion(e){return(await this.history(e,{limit:50})).find(r=>!r.deleted)}asVersion(e,n){return{uid:e.uid,path:n,size:e.size,ctime:e.ctime,mtime:e.mtime,folder:e.folder,deleted:e.deleted,device:e.device,chunks:e.chunks?.length??0}}close(){this.soonTimer!==void 0&&(clearTimeout(this.soonTimer),this.soonTimer=void 0),this.transport.close()}};function yi(t,e){let n=t.lastIndexOf("/"),r=n===-1?"":t.slice(0,n+1),i=n===-1?t:t.slice(n+1),s=i.lastIndexOf("."),o=s<=0?i:i.slice(0,s),a=s<=0?"":i.slice(s);return`${r}${o} (restored ${e.uid})${a}`}async function Nn(t,e={}){let n=new Ke(0,3e5,5e3,!0);for(;e.keepGoing?.()??!0;){let r,i,s=!1;try{r=new Re(t),await r.connect(),s=!0,n.success(Date.now()),e.onClient?.(r),e.onSynced?.(await r.settle(),r.serverCursor),i=await r.runUntilClosed()}catch(l){i=l}finally{e.onClient?.(void 0),r?.close()}if(i&&vi(i)){e.onFatal?.(i);return}if(!(e.keepGoing?.()??!0))return;n.fail(Date.now());let o=n.delay(),a=i??new Error("the connection ended");s?e.onDisconnected?.(a,o):e.onUnreachable?.(a,o),await $t(o)}}function vi(t){return t instanceof P&&t.fatal}function wi(t,e){return{uploaded:t.uploaded+e.uploaded,downloaded:t.downloaded+e.downloaded,merged:t.merged+e.merged,conflicted:t.conflicted+e.conflicted,deletedLocally:t.deletedLocally+e.deletedLocally,deletedRemotely:t.deletedRemotely+e.deletedRemotely,restored:t.restored+e.restored,foldersCreated:t.foldersCreated+e.foldersCreated,chunksSent:t.chunksSent+e.chunksSent,bytesSent:t.bytesSent+e.bytesSent,unchanged:e.unchanged,waiting:e.waiting,retrying:e.retrying,skipped:e.skipped,blocked:e.blocked,inTheWay:e.inTheWay}}function bi(t){return t.uploaded+t.downloaded+t.merged+t.conflicted+t.deletedLocally+t.deletedRemotely+t.restored+t.foldersCreated+t.waiting>0}var $t=t=>new Promise(e=>setTimeout(e,t));var Ge="basalt2_",Mt=2,Ie=4,Wn=new TextEncoder,ki=new TextDecoder;function Ct(t){if(t.secret.length!==X)throw new Error(`a root secret is ${X} bytes, not ${t.secret.length}`);let e=[Wn.encode(t.url),Wn.encode(t.vaultId)];for(let o of e)if(o.length>255)throw new Error("a pairing field is too long to encode");let n=1+X+e.reduce((o,a)=>o+1+a.length,0),r=new Uint8Array(n);r[0]=Mt,r.set(t.secret,1);let i=1+X;for(let o of e)r[i++]=o.length,r.set(o,i),i+=o.length;let s=new Uint8Array(n+Ie);return s.set(r,0),s.set(Ln(r),n),Ge+Te(s)}function Bn(t){let e=t.trim();if(e.startsWith("basalt1_"))throw new Error("this is a version 1 pairing string, from before the server token was folded into the root secret. Run basalt invite on a device that is already paired to get a current one.");if(!e.startsWith(Ge))throw new Error(`not a pairing string: it should start with ${Ge}`);let n;try{n=He(e.slice(Ge.length))}catch{throw new Error("this pairing string is damaged: it is not valid base64url")}if(n.length<1+X+2+Ie)throw new Error("this pairing string is too short to be complete");let r=n.subarray(0,n.length-Ie),i=n.subarray(n.length-Ie),s=Ln(r);for(let u=0;u<Ie;u++)if(i[u]!==s[u])throw new Error("this pairing string is damaged: it did not survive being copied");if(r[0]!==Mt)throw new Error(`this pairing string is version ${r[0]}, and this device understands ${Mt}`);let o=r.slice(1,1+X),a=1+X,l=u=>{if(a>=r.length)throw new Error(`this pairing string ends before its ${u}`);let f=r[a++];if(a+f>r.length)throw new Error(`this pairing string ends inside its ${u}`);let g=ki.decode(r.subarray(a,a+f));return a+=f,g},h=l("server address"),c=l("vault name");if(a!==r.length)throw new Error("this pairing string has more in it than it should");if(h===""||c==="")throw new Error("this pairing string has an empty field");return{url:h,secret:o,vaultId:c}}function Ln(t){let e=4294967295;for(let n of t){e^=n;for(let r=0;r<8;r++)e=e&1?e>>>1^3988292384:e>>>1}return e=(e^4294967295)>>>0,new Uint8Array([e>>>24&255,e>>>16&255,e>>>8&255,e&255])}function Vn(t){return{url:t.url,vaultId:t.vaultId,device:t.device,secret:Te(t.secret),...t.bootstrap?{bootstrap:t.bootstrap}:{}}}function Hn(t,e){if(typeof t!="object"||t===null)throw new Error(`${e} does not hold a configuration`);let n=t,r=o=>{let a=n[o];if(typeof a!="string"||a==="")throw new Error(`${e} has no ${o}`);return a},i=He(r("secret"));if(i.length!==X)throw new Error(`${e} holds a ${i.length} byte secret, and a root secret is ${X}`);let s=n.bootstrap;return{url:r("url"),vaultId:r("vaultId"),device:r("device"),secret:i,...typeof s=="string"&&s!==""?{bootstrap:s}:{}}}function Jn(t){let e=t.trim().replace(/\/+$/,"");if(e==="")throw new Error("that is not a server address");if(e.startsWith("ws://")||e.startsWith("wss://"))return e;if(e.startsWith("http://"))return"ws://"+e.slice(7);if(e.startsWith("https://"))return"wss://"+e.slice(8);if(e.includes("://"))throw new Error(`a server address is ws:// or wss://, not ${e.split("://")[0]}://`);return"wss://"+e}import{constants as Tt,watch as Si}from"node:fs";import{access as _t,cp as Ei,mkdir as Qe,open as et,readFile as Zn,readdir as Kn,realpath as jn,rename as Rt,rm as Ai,stat as qe,utimes as xi}from"node:fs/promises";import{basename as Ze,dirname as le,join as Xe,relative as Pi,resolve as Gn,sep as Ye}from"node:path";var Xn=".trash",$i=new Set([".basalt",Xn,".git",".DS_Store","node_modules"]),It=".obsidian";function zt(t){let e=t.replace(/^\/+|\/+$/g,"");if(e===""||e.includes("/"))throw new Error(`refusing to sync: the config folder ${JSON.stringify(t)} is not a plain name`);return e}var tt=class{root;ignore;constructor(e,n={}){this.root=Gn(e);let r=zt(n.configDir??It);this.ignore=new Set([...$i,r,...n.alsoIgnore??[]])}realRootOnce;unflushed=new Set;async flush(){let e=[...this.unflushed];this.unflushed.clear(),await Promise.all(e.map(n=>er(n)))}async insideForReal(e){let n=await(this.realRootOnce??=jn(this.root)),r=le(e);for(;;){let i=await jn(r).catch(o=>{if(o.code!=="ENOENT")throw o});if(i!==void 0){if(i!==n&&!i.startsWith(n+Ye))throw new Error(`refusing a path that leaves the vault through a link: ${e}`);return}let s=le(r);if(s===r)return;r=s}}absolute(e){let n=Gn(this.root,e),r=Pi(this.root,n);if(r===""||r===".."||r.startsWith(`..${Ye}`))throw new Error(`refusing a path outside the vault: ${e}`);let i=r.split(Ye)[0];if(this.ignore.has(i))throw new Error(`refusing to write inside ${i}, which is never synced: ${e}`);return n}async list(){let e=async(n,r)=>{let i;try{i=await Kn(n,{withFileTypes:!0})}catch(h){throw new Error(`cannot read ${n}: ${h.message}`)}let s=i.filter(h=>!this.ignore.has(h.name)&&!qn(h.name)&&(h.isDirectory()||h.isFile())),o=await Promise.all(s.map(h=>h.isFile()?qe(Xe(n,h.name)):void 0)),a=await Promise.all(s.map(h=>h.isDirectory()?e(Xe(n,h.name),r?`${r}/${h.name}`:h.name):void 0)),l=[];for(let h=0;h<s.length;h++){let c=s[h],u=r?`${r}/${c.name}`:c.name;if(c.isDirectory())l.push({path:u,folder:!0,mtime:0,ctime:0,size:0}),l.push(...a[h]);else{let f=o[h];l.push({path:u,folder:!1,mtime:f.mtimeMs,ctime:f.birthtimeMs||f.ctimeMs,size:f.size})}}return l};return e(this.root,"")}async read(e){return new Uint8Array(await Zn(this.absolute(e)))}async*readBlocks(e,n=1024*1024){let r=await et(this.absolute(e),"r");try{let i=new Uint8Array(n);for(;;){let{bytesRead:s}=await r.read(i,0,n,null);if(s===0)return;yield i.slice(0,s)}}finally{await r.close()}}async readRange(e,n,r){let i=await et(this.absolute(e),"r");try{let s=new Uint8Array(r-n),o=0;for(;o<s.length;){let{bytesRead:a}=await i.read(s,o,s.length-o,n+o);if(a===0)break;o+=a}return o===s.length?s:s.subarray(0,o)}finally{await i.close()}}async write(e,n,r){let i=this.absolute(e);if(await this.insideForReal(i),await Qe(le(i),{recursive:!0}),await this.matchCase(i),await Qn(i,n,!1),this.unflushed.add(le(i)),r.mtime>0){let s=r.mtime/1e3;await xi(i,s,s)}}async matchCase(e){let n;try{n=await qe(e)}catch{return}if(n.isDirectory())return;let r=le(e),i=Ze(e),s;try{s=await Kn(r)}catch{return}if(s.includes(i))return;let o=i.normalize("NFC").toLowerCase(),a=s.find(l=>l.normalize("NFC").toLowerCase()===o);a!==void 0&&await Rt(Xe(r,a),e)}async remove(e){let n=this.absolute(e);await this.insideForReal(n);try{await _t(n,Tt.F_OK)}catch{return}let r=await this.freeTrashPath(e);await Qe(le(r),{recursive:!0});try{await Rt(n,r);return}catch(i){if(i.code!=="EXDEV")throw i}await Ei(n,r,{recursive:!0}),await Ai(n,{recursive:!0,force:!0})}async freeTrashPath(e){let n=Xe(this.root,Xn,e),r=Ze(e).lastIndexOf("."),[i,s]=r<=0?[n,""]:[n.slice(0,n.length-(Ze(e).length-r)),n.slice(n.length-(Ze(e).length-r))];for(let o=0;o<1e3;o++){let a=o===0?n:`${i} (${o})${s}`;try{await _t(a,Tt.F_OK)}catch{return a}}throw new Error(`the trash already holds a thousand copies of ${e}`)}async mkdir(e){let n=this.absolute(e);await this.insideForReal(n),await Qe(n,{recursive:!0})}async exists(e){try{return await _t(this.absolute(e),Tt.F_OK),!0}catch{return!1}}async sameFile(e,n){if(e===n)return!0;try{let[r,i]=await Promise.all([qe(this.absolute(e)),qe(this.absolute(n))]);return r.dev===i.dev&&r.ino===i.ino}catch{return!1}}watch(e){let n,r="",i;try{i=Si(this.root,{recursive:!0,persistent:!0},(s,o)=>{if(!o)return;let a=o.toString().split(Ye).join("/");a.split("/").some(l=>this.ignore.has(l))||qn(a)||(r=a,n&&clearTimeout(n),n=setTimeout(()=>{n=void 0,e(r)},150))}),i.on("error",()=>{})}catch{return()=>{}}return()=>{n&&clearTimeout(n),i?.close()}}},ze=class{constructor(e){this.file=e}file;lastWritten;async load(){let e;try{e=await Zn(this.file,"utf8")}catch(n){if(n.code==="ENOENT")return;throw new Error(`cannot read the index at ${this.file}: ${n.message}`)}try{return JSON.parse(e)}catch(n){throw new Error(`the index at ${this.file} is not valid JSON, so it cannot be trusted: ${n.message}`)}}async save(e){let n=JSON.stringify(e);n!==this.lastWritten&&(await Qe(le(this.file),{recursive:!0}),await Qn(this.file,new TextEncoder().encode(n)),this.lastWritten=n)}},Yn=".basalt-tmp-";function qn(t){return t.includes(Yn)}var Mi=0;async function Ci(t){for(let e=0;e<64;e++){let n=`${t}${Yn}${(Mi++).toString(36)}${e?`-${e}`:""}`;try{return{tmp:n,handle:await et(n,"wx")}}catch(r){if(r.code!=="EEXIST")throw r}}throw new Error(`could not find an unused temporary name beside ${t}`)}async function Qn(t,e,n=!0){let{tmp:r,handle:i}=await Ci(t);try{await i.write(e),await i.sync()}finally{await i.close()}await Rt(r,t),n&&await er(le(t))}async function er(t){let e=await et(t,"r");try{await e.sync()}finally{await e.close()}}import{chmod as Ti,mkdir as _i,readFile as Ri,rename as Ii,rm as tr,writeFile as zi}from"node:fs/promises";import{join as Dt}from"node:path";var Ut=".basalt",Ft=t=>Dt(t,Ut,"config.json"),nt=t=>Dt(t,Ut,"index.json");async function De(t){let e=Ft(t),n;try{n=await Ri(e,"utf8")}catch(i){if(i.code==="ENOENT")return;throw new Error(`cannot read ${e}: ${i.message}`)}let r;try{r=JSON.parse(n)}catch(i){throw new Error(`${e} is not valid JSON, so it cannot be trusted: ${i.message}`)}return Hn(r,e)}async function rt(t,e){let n=Dt(t,Ut);await _i(n,{recursive:!0});let r=Ft(t),i=`${r}.tmp`;await zi(i,JSON.stringify(Vn(e),null,2)+`
9
+ `,{mode:384}),await Ti(i,384),await Ii(i,r)}async function nr(t){await tr(Ft(t),{force:!0}),await tr(nt(t),{force:!0})}var rr=`basalt: self-hosted sync for Obsidian
10
10
 
11
11
  basalt init --server URL --token TOKEN claim a new vault, with the server's first-run token
12
12
  basalt pair PAIRING-STRING pair this vault with an existing one
@@ -30,6 +30,6 @@ Options
30
30
  --limit N how many versions history shows (default: 20)
31
31
  --config-dir DIR Obsidian's config folder, if it is not .obsidian
32
32
  --ignore NAME a top-level name never to sync, repeatable
33
- `;async function ir(t,e){let n;try{n=Hi(t)}catch(r){return e.err(String(r.message)),2}if(n.help||n.command===void 0)return e.out(nr),n.command===void 0&&!n.help?2:0;try{switch(n.command){case"init":return await Ui(n,e);case"pair":return await Fi(n,e);case"invite":return await Oi(n,e);case"sync":return await Ni(n,e);case"status":return await Li(n,e);case"deleted":return await Wi(n,e);case"history":return await Vi(n,e);case"restore":return await Ji(n,e);case"unlink":return await Ki(n,e);default:return e.err(`no such command: ${n.command}`),e.err(nr),2}}catch(r){let i=r instanceof Error?r.message:String(r);return n.json?e.out(JSON.stringify({ok:!1,error:i})):e.err(`basalt: ${i}`),1}}async function Ui(t,e){if(!t.server||!t.token)throw new Error("init needs --server and --token, from the server's first run");if(await De(t.dir))throw new Error(`${t.dir} is already paired. Use unlink first if that is really what you want.`);let n={url:Jn(t.server),vaultId:t.vaultId,device:t.device,secret:en(),bootstrap:t.token};await rt(t.dir,n),(await ke(n,t,e)).close();let i=Ct(n);return t.json?e.out(JSON.stringify({ok:!0,paired:t.dir,device:n.device,pairing:i})):(e.out(`Paired ${t.dir} as "${n.device}".`),e.out(""),e.out("Give this to every other device. Anyone who has it has the vault:"),e.out(""),e.out(` ${i}`),e.out(""),e.out("It is the only copy. The server cannot reissue it, because the server"),e.out("has never seen the secret in it.")),0}async function Fi(t,e){if(!t.rest[0])throw new Error("pair needs the string another device printed");if(await De(t.dir))throw new Error(`${t.dir} is already paired. Use unlink first if that is really what you want.`);let r={...Bn(t.rest[0]),device:t.device};return await rt(t.dir,r),t.json?e.out(JSON.stringify({ok:!0,paired:t.dir,device:r.device,url:r.url})):e.out(`Paired ${t.dir} with ${r.url} as "${r.device}". Run basalt sync.`),0}async function Oi(t,e){let n=await Se(t.dir),r=Ct(n);return t.json?e.out(JSON.stringify({ok:!0,pairing:r})):(e.out(r),e.err("Anyone who has that string has this vault.")),0}async function Ni(t,e){let n=await Se(t.dir);if(t.watch)return await Bi(n,t,e);let r=await ke(n,t,e);try{let i=await r.settle();return or(i,t,e,r.serverCursor),i.skipped>0||i.retrying>0?1:0}finally{r.close()}}async function Bi(t,e,n){let r;return await On(await sr(t,e,n),{onSynced:(i,s)=>{or(i,e,n,s),e.json||n.err("Watching for changes. Ctrl-C to stop.")},onDisconnected:(i,s)=>{n.err(`Disconnected: ${i.message}. Trying again in ${rr(s)}.`)},onUnreachable:(i,s)=>{n.err(`Cannot reach the server: ${i.message}. Trying again in ${rr(s)}.`)},onFatal:i=>{r=i}}),r?(n.err(`basalt: ${r.message}`),n.err("That will not fix itself by trying again."),1):0}async function Li(t,e){let n=await Se(t.dir),r=await new ze(nt(t.dir)).load(),i={vault:t.dir,device:n.device,server:n.url,vaultId:n.vaultId,cursor:r?.cursor??0,tracked:r?Object.keys(r.entries).length:0,pending:r?.pending.length??0},s;try{let o=await ke(n,t,e);s={reachable:!0,cursor:o.serverCursor,behind:o.serverCursor-i.cursor},o.close()}catch(o){s={reachable:!1,error:o.message}}return t.json?(e.out(JSON.stringify({ok:!0,...i,server:s})),s.reachable?0:1):(e.out(`vault ${i.vault}`),e.out(`device ${i.device}`),e.out(`server ${i.server} (vault "${i.vaultId}")`),e.out(`tracked ${i.tracked} files`),e.out(`cursor ${i.cursor}`),i.pending>0&&e.out(`pending ${i.pending} files with work outstanding`),s.reachable?(e.out(s.behind===0?"state up to date with the server":`state ${s.behind} changes behind`),0):(e.out(`state cannot reach the server: ${s.error}`),1))}async function Wi(t,e){let n=await Se(t.dir),r=await ke(n,t,e);try{let i=await r.deleted(t.limit>20?t.limit:void 0);if(t.json)return e.out(JSON.stringify({ok:!0,deleted:i.notes,more:i.more})),0;if(i.notes.length===0)return e.out("Nothing has been deleted from this vault."),0;let s=0;for(let a of i.notes){let l=a.restorable===0?" (content purged)":"";a.restorable===0&&s++,e.out(`${Ot(a.mtime)} ${a.device.padEnd(12)} ${a.path}${l}`)}e.out("");let o=i.notes.length-s;return s===0?e.out(`${o} deleted, all still recoverable. basalt restore PATH brings one back.`):e.out(`${i.notes.length} deleted. ${o} can be restored; ${s} had their history purged and cannot be.`),i.more&&e.out("There are older deletions than these. --limit N shows more."),0}finally{r.close()}}async function Vi(t,e){let n=t.rest[0];if(!n)throw new Error("history needs the path of a note");let r=await Se(t.dir),i=await ke(r,t,e);try{let s=await i.history(n,{limit:t.limit});if(t.json)return e.out(JSON.stringify({ok:!0,path:n,versions:s})),0;if(s.length===0)return e.out(`The server holds no versions of ${n}.`),0;for(let o of s){let a=o.deleted?"deleted":o.folder?"folder":`${Nt(o.size)}`;e.out(`${String(o.uid).padStart(7)} ${Ot(o.mtime)} ${o.device.padEnd(12)} ${a}`)}return e.out(""),e.out("basalt restore PATH --uid N brings one of these back."),0}finally{i.close()}}async function Ji(t,e){let n=t.rest[0];if(!n)throw new Error("restore needs the path of a note");let r=await Se(t.dir),i=await ke(r,t,e);try{let s;if(t.uid!==void 0){if(s=(await i.history(n,{limit:500})).find(h=>h.uid===t.uid),!s)throw new Error(`the server has no version ${t.uid} of ${n}`)}else if(s=await i.newestContentVersion(n),!s)throw new Error(`the server holds no version of ${n} with any content in it`);let o=await i.restore(s,t.to),a=await i.settle({coalesceWrites:!1});return t.json?(e.out(JSON.stringify({ok:!0,path:o.path,uid:s.uid,bytes:o.bytes,sync:a})),0):(e.out(`Restored version ${s.uid} of ${n} (${Nt(o.bytes)}, from ${Ot(s.mtime)}).`),o.path!==(t.to??n)&&e.out(`Written to ${o.path}, because something is already at ${t.to??n}.`),a.uploaded>0&&e.out("Sent to the server, so your other devices will pick it up."),0)}finally{i.close()}}async function Ki(t,e){let n=await De(t.dir).catch(()=>{});return await tr(t.dir),t.json?e.out(JSON.stringify({ok:!0,unlinked:t.dir,wasPaired:n!==void 0})):(e.out(`Forgot the pairing for ${t.dir}. Every note is where it was.`),e.out("Nothing was removed from the server.")),0}async function ke(t,e,n){let r=new Re(await sr(t,e,n));if(await r.connect(),t.bootstrap){let{bootstrap:i,...s}=t;await rt(e.dir,s)}return r}async function sr(t,e,n){let r=await tn(t.secret),i=ln(r);return{vault:new tt(e.dir,{configDir:e.configDir,alsoIgnore:e.ignore}),store:new ze(nt(e.dir)),keys:r,url:t.url,token:t.bootstrap??i,claim:i,vaultId:t.vaultId,device:t.device,timeoutMs:e.timeout,coalesceWrites:e.watch,...e.watch&&n?{onProgress:s=>{s!==void 0&&n.err(` ... ${s}`)}}:{},...e.verbose&&n?{log:(s,...o)=>n.err(` ${s} ${o.map(ji).join(" ")}`.trimEnd())}:{}}}function or(t,e,n,r){if(e.json){n.out(JSON.stringify({ok:!0,...t,serverCursor:r}));return}let i=[],s=(o,a)=>{o>0&&i.push(`${String(o).padStart(5)} ${a}`)};if(s(t.uploaded,"uploaded"),s(t.downloaded,"downloaded"),s(t.merged,"merged"),s(t.conflicted,"kept both versions"),s(t.deletedLocally,"deleted here"),s(t.deletedRemotely,"deleted on the server"),s(t.restored,"brought back, having been edited elsewhere"),s(t.foldersCreated,"folders created"),s(t.waiting,"waiting for a write to settle"),s(t.retrying,"failed, will try again"),s(t.skipped,"cannot sync and will not be retried"),s(t.blocked,"waiting on a name that is a file here and a folder elsewhere"),i.length===0)n.out("Nothing to do. Everything here matches the server.");else for(let o of i)n.out(o);t.chunksSent>0&&n.out(`${String(t.chunksSent).padStart(5)} chunks sent, ${Nt(t.bytesSent)}`),t.conflicted>0&&n.err('Look for files with "Conflicted copy" in the name. Both versions are kept.')}function Hi(t){let e={rest:[],dir:process.cwd(),device:zi().split(".")[0]||"device",vaultId:"default",json:!1,watch:!1,limit:20,verbose:!1,help:!1,timeout:3e4,configDir:It,ignore:[]},n=new Set(["--dir","--device","--vault-id","--server","--token","--timeout","--uid","--to","--limit","--config-dir","--ignore"]);for(let r=0;r<t.length;r++){let i=t[r],s;if(n.has(i)&&(s=t[++r],s===void 0||s.startsWith("--")))throw new Error(`${i} needs a value`);switch(i){case"--dir":e.dir=Di(s);break;case"--device":e.device=s;break;case"--vault-id":e.vaultId=s;break;case"--server":e.server=s;break;case"--token":e.token=s;break;case"--timeout":{let o=Number(s);if(!Number.isFinite(o)||o<=0)throw new Error(`--timeout wants a number of milliseconds, not ${s}`);e.timeout=o;break}case"--uid":{let o=Number(s);if(!Number.isInteger(o)||o<=0)throw new Error(`--uid wants a version number, not ${s}`);e.uid=o;break}case"--to":e.to=s;break;case"--config-dir":e.configDir=zt(s);break;case"--ignore":e.ignore.push(s);break;case"--limit":{let o=Number(s);if(!Number.isInteger(o)||o<=0)throw new Error(`--limit wants a count, not ${s}`);e.limit=o;break}case"--json":e.json=!0;break;case"--watch":e.watch=!0;break;case"--verbose":case"-v":e.verbose=!0;break;case"--help":case"-h":e.help=!0;break;default:if(i.startsWith("-"))throw new Error(`no such option: ${i}`);e.command===void 0?e.command=i:e.rest.push(i)}}return e}async function Se(t){let e=await De(t);if(!e)throw new Error(`${t} is not paired. Run basalt init or basalt pair first.`);return e}function Ot(t){if(!Number.isFinite(t)||t<=0)return"unknown ";let e=new Date(t),n=r=>String(r).padStart(2,"0");return`${e.getFullYear()}-${n(e.getMonth()+1)}-${n(e.getDate())} ${n(e.getHours())}:${n(e.getMinutes())}`}function rr(t){return`${Math.max(1,Math.round(t/1e3))}s`}function Nt(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KiB`:`${(t/(1024*1024)).toFixed(1)} MiB`}function ji(t){return typeof t=="string"?t:JSON.stringify(t)}var Gi=await ir(process.argv.slice(2),{out:t=>process.stdout.write(t+`
33
+ `;async function sr(t,e){let n;try{n=ji(t)}catch(r){return e.err(String(r.message)),2}if(n.help||n.command===void 0)return e.out(rr),n.command===void 0&&!n.help?2:0;try{switch(n.command){case"init":return await Fi(n,e);case"pair":return await Oi(n,e);case"invite":return await Ni(n,e);case"sync":return await Wi(n,e);case"status":return await Li(n,e);case"deleted":return await Vi(n,e);case"history":return await Hi(n,e);case"restore":return await Ji(n,e);case"unlink":return await Ki(n,e);default:return e.err(`no such command: ${n.command}`),e.err(rr),2}}catch(r){let i=r instanceof Error?r.message:String(r);return n.json?e.out(JSON.stringify({ok:!1,error:i})):e.err(`basalt: ${i}`),1}}async function Fi(t,e){if(!t.server||!t.token)throw new Error("init needs --server and --token, from the server's first run");if(await De(t.dir))throw new Error(`${t.dir} is already paired. Use unlink first if that is really what you want.`);let n={url:Jn(t.server),vaultId:t.vaultId,device:t.device,secret:en(),bootstrap:t.token};await rt(t.dir,n),(await ke(n,t,e)).close();let i=Ct(n);return t.json?e.out(JSON.stringify({ok:!0,paired:t.dir,device:n.device,pairing:i})):(e.out(`Paired ${t.dir} as "${n.device}".`),e.out(""),e.out("Give this to every other device. Anyone who has it has the vault:"),e.out(""),e.out(` ${i}`),e.out(""),e.out("It is the only copy. The server cannot reissue it, because the server"),e.out("has never seen the secret in it.")),0}async function Oi(t,e){if(!t.rest[0])throw new Error("pair needs the string another device printed");if(await De(t.dir))throw new Error(`${t.dir} is already paired. Use unlink first if that is really what you want.`);let r={...Bn(t.rest[0]),device:t.device};return await rt(t.dir,r),t.json?e.out(JSON.stringify({ok:!0,paired:t.dir,device:r.device,url:r.url})):e.out(`Paired ${t.dir} with ${r.url} as "${r.device}". Run basalt sync.`),0}async function Ni(t,e){let n=await Se(t.dir),r=Ct(n);return t.json?e.out(JSON.stringify({ok:!0,pairing:r})):(e.out(r),e.err("Anyone who has that string has this vault.")),0}async function Wi(t,e){let n=await Se(t.dir);if(t.watch)return await Bi(n,t,e);let r=await ke(n,t,e);try{let i=await r.settle();return ar(i,t,e,r.serverCursor),i.skipped>0||i.retrying>0?1:0}finally{r.close()}}async function Bi(t,e,n){let r;return await Nn(await or(t,e,n),{onSynced:(i,s)=>{ar(i,e,n,s),e.json||n.err("Watching for changes. Ctrl-C to stop.")},onDisconnected:(i,s)=>{n.err(`Disconnected: ${i.message}. Trying again in ${ir(s)}.`)},onUnreachable:(i,s)=>{n.err(`Cannot reach the server: ${i.message}. Trying again in ${ir(s)}.`)},onFatal:i=>{r=i}}),r?(n.err(`basalt: ${r.message}`),n.err("That will not fix itself by trying again."),1):0}async function Li(t,e){let n=await Se(t.dir),r=await new ze(nt(t.dir)).load(),i={vault:t.dir,device:n.device,server:n.url,vaultId:n.vaultId,cursor:r?.cursor??0,tracked:r?Object.keys(r.entries).length:0,pending:r?.pending.length??0},s;try{let o=await ke(n,t,e);s={reachable:!0,cursor:o.serverCursor,behind:o.serverCursor-i.cursor},o.close()}catch(o){s={reachable:!1,error:o.message}}return t.json?(e.out(JSON.stringify({ok:!0,...i,server:s})),s.reachable?0:1):(e.out(`vault ${i.vault}`),e.out(`device ${i.device}`),e.out(`server ${i.server} (vault "${i.vaultId}")`),e.out(`tracked ${i.tracked} files`),e.out(`cursor ${i.cursor}`),i.pending>0&&e.out(`pending ${i.pending} files with work outstanding`),s.reachable?(e.out(s.behind!==0?`state ${s.behind} changes behind`:i.pending>0?`state caught up with the server, with ${i.pending} still not applied here`:"state up to date with the server"),0):(e.out(`state cannot reach the server: ${s.error}`),1))}async function Vi(t,e){let n=await Se(t.dir),r=await ke(n,t,e);try{let i=await r.deleted(t.limit>20?t.limit:void 0);if(t.json)return e.out(JSON.stringify({ok:!0,deleted:i.notes,more:i.more})),0;if(i.notes.length===0)return e.out("Nothing has been deleted from this vault."),0;let s=0;for(let a of i.notes){let l=a.restorable===0?" (content purged)":"";a.restorable===0&&s++,e.out(`${Ot(a.mtime)} ${a.device.padEnd(12)} ${a.path}${l}`)}e.out("");let o=i.notes.length-s;return s===0?e.out(`${o} deleted, all still recoverable. basalt restore PATH brings one back.`):e.out(`${i.notes.length} deleted. ${o} can be restored; ${s} had their history purged and cannot be.`),i.more&&e.out("There are older deletions than these. --limit N shows more."),0}finally{r.close()}}async function Hi(t,e){let n=t.rest[0];if(!n)throw new Error("history needs the path of a note");let r=await Se(t.dir),i=await ke(r,t,e);try{let s=await i.history(n,{limit:t.limit});if(t.json)return e.out(JSON.stringify({ok:!0,path:n,versions:s})),0;if(s.length===0)return e.out(`The server holds no versions of ${n}.`),0;for(let o of s){let a=o.deleted?"deleted":o.folder?"folder":`${Nt(o.size)}`;e.out(`${String(o.uid).padStart(7)} ${Ot(o.mtime)} ${o.device.padEnd(12)} ${a}`)}return e.out(""),e.out("basalt restore PATH --uid N brings one of these back."),0}finally{i.close()}}async function Ji(t,e){let n=t.rest[0];if(!n)throw new Error("restore needs the path of a note");let r=await Se(t.dir),i=await ke(r,t,e);try{let s;if(t.uid!==void 0){if(s=(await i.history(n,{limit:500})).find(h=>h.uid===t.uid),!s)throw new Error(`the server has no version ${t.uid} of ${n}`)}else if(s=await i.newestContentVersion(n),!s)throw new Error(`the server holds no version of ${n} with any content in it`);let o=await i.restore(s,t.to),a=await i.settle({coalesceWrites:!1});return t.json?(e.out(JSON.stringify({ok:!0,path:o.path,uid:s.uid,bytes:o.bytes,sync:a})),0):(e.out(`Restored version ${s.uid} of ${n} (${Nt(o.bytes)}, from ${Ot(s.mtime)}).`),o.path!==(t.to??n)&&e.out(`Written to ${o.path}, because something is already at ${t.to??n}.`),a.uploaded>0&&e.out("Sent to the server, so your other devices will pick it up."),0)}finally{i.close()}}async function Ki(t,e){let n=await De(t.dir).catch(()=>{});return await nr(t.dir),t.json?e.out(JSON.stringify({ok:!0,unlinked:t.dir,wasPaired:n!==void 0})):(e.out(`Forgot the pairing for ${t.dir}. Every note is where it was.`),e.out("Nothing was removed from the server.")),0}async function ke(t,e,n){let r=new Re(await or(t,e,n));if(await r.connect(),t.bootstrap){let{bootstrap:i,...s}=t;await rt(e.dir,s)}return r}async function or(t,e,n){let r=await tn(t.secret),i=ln(r);return{vault:new tt(e.dir,{configDir:e.configDir,alsoIgnore:e.ignore}),store:new ze(nt(e.dir)),keys:r,url:t.url,token:t.bootstrap??i,claim:i,vaultId:t.vaultId,device:t.device,timeoutMs:e.timeout,coalesceWrites:e.watch,...e.watch&&n?{onProgress:s=>{s!==void 0&&n.err(` ... ${s}`)}}:{},...e.verbose&&n?{log:(s,...o)=>n.err(` ${s} ${o.map(Gi).join(" ")}`.trimEnd())}:{}}}function ar(t,e,n,r){if(e.json){n.out(JSON.stringify({ok:!0,...t,serverCursor:r}));return}let i=[],s=(o,a)=>{o>0&&i.push(`${String(o).padStart(5)} ${a}`)};if(s(t.uploaded,"uploaded"),s(t.downloaded,"downloaded"),s(t.merged,"merged"),s(t.conflicted,"kept both versions"),s(t.deletedLocally,"deleted here"),s(t.deletedRemotely,"deleted on the server"),s(t.restored,"brought back, having been edited elsewhere"),s(t.foldersCreated,"folders created"),s(t.waiting,"waiting for a write to settle"),s(t.retrying,"failed, will try again"),s(t.skipped,"cannot sync and will not be retried"),s(t.blocked,"waiting on a name that is a file here and a folder elsewhere"),i.length===0)n.out("Nothing to do. Everything here matches the server.");else for(let o of i)n.out(o);if(t.inTheWay.length>0){n.out("");let o=[...new Set(t.inTheWay.map(l=>l.blockedBy))];for(let l of o)n.out(` "${l}" is a file here and a folder on another device.`);let a=t.inTheWay.map(l=>l.path);n.out(` Waiting to be written: ${a.join(", ")}${t.blocked>a.length?", \u2026":""}`),n.out(" Rename one of them, on whichever device meant the other thing.")}t.chunksSent>0&&n.out(`${String(t.chunksSent).padStart(5)} chunks sent, ${Nt(t.bytesSent)}`),t.conflicted>0&&n.err('Look for files with "Conflicted copy" in the name. Both versions are kept.')}function ji(t){let e={rest:[],dir:process.cwd(),device:Di().split(".")[0]||"device",vaultId:"default",json:!1,watch:!1,limit:20,verbose:!1,help:!1,timeout:3e4,configDir:It,ignore:[]},n=new Set(["--dir","--device","--vault-id","--server","--token","--timeout","--uid","--to","--limit","--config-dir","--ignore"]);for(let r=0;r<t.length;r++){let i=t[r],s;if(n.has(i)&&(s=t[++r],s===void 0||s.startsWith("--")))throw new Error(`${i} needs a value`);switch(i){case"--dir":e.dir=Ui(s);break;case"--device":e.device=s;break;case"--vault-id":e.vaultId=s;break;case"--server":e.server=s;break;case"--token":e.token=s;break;case"--timeout":{let o=Number(s);if(!Number.isFinite(o)||o<=0)throw new Error(`--timeout wants a number of milliseconds, not ${s}`);e.timeout=o;break}case"--uid":{let o=Number(s);if(!Number.isInteger(o)||o<=0)throw new Error(`--uid wants a version number, not ${s}`);e.uid=o;break}case"--to":e.to=s;break;case"--config-dir":e.configDir=zt(s);break;case"--ignore":e.ignore.push(s);break;case"--limit":{let o=Number(s);if(!Number.isInteger(o)||o<=0)throw new Error(`--limit wants a count, not ${s}`);e.limit=o;break}case"--json":e.json=!0;break;case"--watch":e.watch=!0;break;case"--verbose":case"-v":e.verbose=!0;break;case"--help":case"-h":e.help=!0;break;default:if(i.startsWith("-"))throw new Error(`no such option: ${i}`);e.command===void 0?e.command=i:e.rest.push(i)}}return e}async function Se(t){let e=await De(t);if(!e)throw new Error(`${t} is not paired. Run basalt init or basalt pair first.`);return e}function Ot(t){if(!Number.isFinite(t)||t<=0)return"unknown ";let e=new Date(t),n=r=>String(r).padStart(2,"0");return`${e.getFullYear()}-${n(e.getMonth()+1)}-${n(e.getDate())} ${n(e.getHours())}:${n(e.getMinutes())}`}function ir(t){return`${Math.max(1,Math.round(t/1e3))}s`}function Nt(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KiB`:`${(t/(1024*1024)).toFixed(1)} MiB`}function Gi(t){return typeof t=="string"?t:JSON.stringify(t)}var qi=await sr(process.argv.slice(2),{out:t=>process.stdout.write(t+`
34
34
  `),err:t=>process.stderr.write(t+`
35
- `)});process.exit(Gi);
35
+ `)});process.exit(qi);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "basalt-sync",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Headless Obsidian vault sync for a Basalt server.",
5
5
  "keywords": [
6
6
  "obsidian",
@@ -38,14 +38,15 @@
38
38
  "format:check": "prettier --config ../.prettierrc --check \"src/**/*.ts\" \"*.ts\" \"*.mjs\"",
39
39
  "test": "vitest run",
40
40
  "test:watch": "vitest",
41
- "typecheck": "tsc --noEmit"
41
+ "typecheck": "tsc --noEmit",
42
+ "stress": "vitest run --config vitest.stress.config.ts"
42
43
  },
43
44
  "devDependencies": {
44
45
  "@types/diff-match-patch": "^1.0.36",
45
46
  "@types/node": "^24.10.13",
46
- "diff-match-patch": "^1.0.5",
47
+ "diff-match-patch": "1.0.5",
47
48
  "esbuild": "^0.28.1",
48
- "fflate": "^0.8.3",
49
+ "fflate": "0.8.3",
49
50
  "obsidian": "^1.13.1",
50
51
  "typescript": "^5.9.3",
51
52
  "vitest": "^4.1.8",