@pipeup/share 0.9.0 → 1.0.0
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/dist/cli.js +183 -169
- package/dist/pipeup+share.min.js +2 -2
- package/dist/share.esm.js +1 -1
- package/dist/share.headless.js +1 -1
- package/dist/share.min.js +1 -1
- package/package.json +2 -2
package/dist/share.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var S=(n,t)=>t?t.getAttribute(n):globalThis.Pipeup?.setting?.(n)??globalThis.document?.documentElement.getAttribute(n)??null;var m=n=>new TextEncoder().encode(n),W=n=>new TextDecoder().decode(n);function k(n){let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function w(n){let t=n.replace(/-/g,"+").replace(/_/g,"/"),e=atob(t+"=".repeat((4-t.length%4)%4));return Uint8Array.from(e,r=>r.charCodeAt(0))}function P(n){let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t)}function V(n){return Uint8Array.from(atob(n),t=>t.charCodeAt(0))}var T=n=>crypto.getRandomValues(new Uint8Array(n));async function D(n){return new Uint8Array(await crypto.subtle.digest("SHA-256",n))}var Te=/^([A-Za-z0-9_-]{16}):([A-Za-z0-9_-]{43})$/;function Q(n,t=document){let e=Te.exec((t===globalThis.document?S("data-pipeup-doc"):t.documentElement.getAttribute("data-pipeup-doc"))?.trim()??"");return e&&e[1]===n?w(e[2]):null}var Oe=n=>m(`pipeup/v1/doc:${n}`);async function pe(n,t,e,r){let i=await crypto.subtle.importKey("raw",n,"HKDF",!1,["deriveBits"]);return crypto.subtle.deriveBits({name:"HKDF",hash:"SHA-256",salt:Oe(t),info:m(e)},i,r)}function ee(n,t){return{docId:n,key:async e=>crypto.subtle.importKey("raw",await pe(t,n,`pipeup/v1/${e}/key`,256),"AES-GCM",!1,["encrypt","decrypt"]),addr:async e=>k(new Uint8Array(await pe(t,n,`pipeup/v1/${e}/address`,128))),aad:e=>`pipeup/v1/${e}:${n}`}}var Be=1e6,Ee=1e3;async function he(n,t,e=1/0){let r=new Blob([n]).stream().pipeThrough(t).getReader(),i=[],o=0;for(;;){let{done:d,value:l}=await r.read();if(d)break;if(o+=l.length,o>e)throw r.cancel(),new Error("too large");i.push(l)}let s=new Uint8Array(o),a=0;for(let d of i)s.set(d,a),a+=d.length;return s}var A=(n,t,e)=>L(n,t,{pipeup:1,doc:n.docId,sealed:!1,ops:e});async function L(n,t,e){let r=m(JSON.stringify(e)),i=T(12),o=await crypto.subtle.encrypt({name:"AES-GCM",iv:i,additionalData:m(n.aad(t))},await n.key(t),await he(r,new CompressionStream("deflate-raw"))),s=new Uint8Array(13+o.byteLength);return s[0]=1,s.set(i,1),s.set(new Uint8Array(o),13),s}async function C(n,t,e){try{if(e.length<29||e[0]!==1)return null;let r=await crypto.subtle.decrypt({name:"AES-GCM",iv:e.slice(1,13),additionalData:m(n.aad(t))},await n.key(t),e.slice(13));return JSON.parse(W(await he(new Uint8Array(r),new DecompressionStream("deflate-raw"),Be)))}catch{return null}}function N(n,t){let e=t;if(e?.pipeup!==1||e.doc!==n.docId||!Array.isArray(e.ops)||e.ops.length>Ee)return null;let r=0;for(let i of e.ops){let o=i?.body?.v;typeof o=="number"&&o!==1&&r++}return{ops:e.ops,newer:r}}async function te(n,t,e){return N(n,await C(n,t,e))}var ne=n=>`pu1.${k(n)}`,re=n=>{if(!/^pu1\.[A-Za-z0-9_-]+$/.test(n))return null;try{return w(n.slice(4))}catch{return null}};async function ie(n,t={}){let e=new Map,r={get:async i=>e.get(i),set:async(i,o)=>void e.set(i,o),lasting:!1};if(t.memory||typeof indexedDB>"u")return r;try{let i=await new Promise((s,a)=>{let d=setTimeout(()=>a(new Error("timed out")),4e3),l=indexedDB.open(`pipeup-addon-${n}`,1);l.onupgradeneeded=()=>l.result.createObjectStore("kv"),l.onsuccess=()=>{clearTimeout(d),s(l.result)},l.onerror=l.onblocked=()=>{clearTimeout(d),a(l.error??new Error("blocked"))}}),o=(s,a)=>new Promise((d,l)=>{let f=a(i.transaction("kv",s).objectStore("kv"));f.onsuccess=()=>d(f.result),f.onerror=()=>l(f.error)});return{get:s=>o("readonly",a=>a.get(s)),set:async(s,a)=>void await o("readwrite",d=>d.put(a,s)),lasting:!0}}catch{return r}}var u=class extends Error{constructor(e,r=!1){super(`try again in ${e} s`);this.seconds=e;this.limit=r;this.name="RetryAfter"}};var Ue=100,ue=5e3,_e=5e3,Me=3e5,Re=3600,M=class{constructor(t){this.o=t}state="idle";lastOk=0;known=new Set;outbox=new Map;rejected=new Set;inbound=[];inboundTimer=0;sendTimer=0;lastSend=0;backoff=0;off=null;listeners=new Set;stopped=!1;get waiting(){return this.outbox.size}pending(){return[...this.outbox.values()]}onState(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(t){t&&(this.state=t);for(let e of[...this.listeners])e()}async start(){let{document:t,transport:e,signal:r}=this.o;this.off=t.onChange((o,s,a)=>{if(a===this.o.id)return;let d=!1;for(let l of s)this.want(l)&&(d=this.outbox.set(l.body.id,l).size>0||d);d&&this.schedule(),this.emit()}),r.addEventListener("abort",()=>this.stop(),{once:!0}),await e.start(o=>this.deliver(o));let i=await e.remoteIds?.();if(i)for(let o of i)this.known.add(o);this.rescan()}stop(){this.stopped||(this.stopped=!0,this.off?.(),clearTimeout(this.inboundTimer),clearTimeout(this.sendTimer),this.o.transport.stop())}want(t){return!this.known.has(t.body.id)&&!this.rejected.has(t.body.id)&&this.o.sendable(t)}rescan(){this.outbox.clear();for(let t of this.o.document.ops())this.want(t)&&this.outbox.set(t.body.id,t);this.schedule(),this.emit()}deliver(t){this.inbound.push(...t),this.inboundTimer||(this.inboundTimer=setTimeout(()=>void this.drain(),Ue))}async drain(){this.inboundTimer=0;let t=this.inbound.splice(0),e=t.map(i=>i?.body?.id).filter(i=>typeof i=="string");for(let i=0;i<t.length;i+=ue)try{await this.o.merge(t.slice(i,i+ue))}catch(o){o?.name!=="UnsavedChangeError"&&globalThis.reportError?.(o)}let r=new Set(this.o.document.ops().map(i=>i.body.id));for(let i of e)r.has(i)?(this.known.add(i),this.outbox.delete(i)):this.rejected.add(i);this.lastOk=Date.now(),this.emit()}schedule(t=this.o.delay??0){if(this.stopped||this.sendTimer||this.outbox.size===0)return;let e=typeof this.o.gap=="function"?this.o.gap():this.o.gap??0,r=Math.max(t,this.lastSend+e-Date.now(),this.backoff);this.sendTimer=setTimeout(()=>void this.send(),r)}async send(){if(this.sendTimer=0,this.stopped||this.o.signal.aborted||this.outbox.size===0)return;let t=[...this.outbox.values()].slice(0,this.o.batch??500);this.emit("sending");try{await this.o.transport.send(t);for(let e of t)this.known.add(e.body.id),this.outbox.delete(e.body.id);this.lastSend=Date.now(),this.lastOk=this.lastSend,this.backoff=0,this.emit("idle")}catch(e){e instanceof u?(this.backoff=Math.min(e.seconds,Re)*1e3*(1+Math.random()*.2),this.emit("limited")):(this.backoff=Math.min(Me,this.backoff?this.backoff*2:_e),this.emit("offline"))}this.schedule()}};var gt=2n**256n-2n**32n-977n;var Ie="sharing isn't set up for this page",R="data-pipeup-share isn't a sharing address",De=/^(localhost|127\.0\.0\.1|\[::1\])$/,fe=/^[A-Za-z0-9_-]{43}$/,O=n=>({kind:"off",reason:n});function me(n,t=!1){let e=n?.trim();if(!e)return O(Ie);let r;try{r=new URL(e)}catch{return O(R)}if(r.protocol!=="https:"&&!(t&&r.protocol==="http:"&&De.test(r.hostname)))return O(R);let i=r.origin+r.pathname,o=r.hash.slice(1);if(o.startsWith("pm1.")){let[a,d,...l]=o.slice(4).split(".");return!fe.test(a??"")||l.length||d!==void 0&&!/^[A-Za-z0-9_-]+$/.test(d)||r.search||!/\/m\/[A-Za-z0-9_-]{22,64}$/.test(i)?O(R):{kind:"mailbox",mailbox:i,key:w(a),...d?{token:d}:{}}}let s=/^\?([A-Za-z0-9_-]{8,64})$/.exec(r.search)?.[1];return!s||!fe.test(o)?O(R):{kind:"privatebin",base:i,paste:s,key:w(o)}}var b=class extends Error{constructor(e){super(e);this.kind=e;this.name="Problem"}},ge={Accept:"application/json"},z=(n,t,e)=>n(t,{headers:ge,credentials:"omit",signal:e}),B=n=>({method:"POST",headers:ge,credentials:"omit",body:JSON.stringify(n)}),ye=n=>{let t={403:"closed",404:"gone",507:"full"}[n];return t&&new b(t)};async function E(n){let t=await n.text(),e=t.length<1e7&&JSON.parse(t);if(!e||typeof e!="object")throw new Error("the answer was not usable");return[e,t.length]}function $(n,t=9e5){let e=[],r=0;for(let i of n)r+=JSON.stringify(i).length,(!e.length||r>t||e.at(-1).length>=500)&&(e.push([]),r=JSON.stringify(i).length),e.at(-1).push(i);return e}var U=(n,t)=>new Promise(e=>{let r=setTimeout(e,n);t?.addEventListener("abort",()=>{clearTimeout(r),e()},{once:!0})}),H=(n,t=10)=>{let e=Number(n.headers.get("Retry-After"));return e>0?Math.min(e,3600):t},_=n=>n.map(t=>t?.body?.id).filter(t=>typeof t=="string");var Le=524288,Ce=1e3,Ne=async n=>k(await D(m(n))).slice(0,22),j=class{constructor(t,e,r,i){this.a=t;this.l=e;this.f=r;this.signal=i}ids=new Set;expires=null;moved=!1;big=!1;cursor="";seen=new Set;memo=null;async read(){let t=[],e=0;for(let r=0;r<Ce;){let i=`${this.a.mailbox}/ops${this.cursor?`?since=${encodeURIComponent(this.cursor)}`:""}`,o=await z(this.f,i,this.signal);if(o.status===404)throw new b("gone");if(o.status===409&&e++<2){this.cursor="";continue}if(!o.ok)throw new Error(`HTTP ${o.status}`);r++;let[s]=await E(o);for(let a of Array.isArray(s.batches)?s.batches:[]){if(typeof a?.id!="string"||typeof a.ct!="string"||this.seen.has(a.id))continue;this.seen.add(a.id);let d=re(a.ct),l=d&&await te(this.l,"share",d);if(l){t.push(l);for(let f of _(l.ops))this.ids.add(f)}}if(typeof s.next=="string"&&(this.cursor=s.next),this.expires=typeof s.expires=="string"&&Date.parse(s.expires)||null,s.more!==!0)break}return t}async req(t){let e=t.map(o=>o.body.id).join();if(this.memo?.key!==e){let o=ne(await A(this.l,"share",t));this.memo={key:e,ct:o,id:await Ne(o)}}let{ct:r,id:i}=this.memo;return[`${this.a.mailbox}/ops`,B({id:i,ct:r,token:this.a.token})]}async post(t){for(let e of $(t))await this.one(e)}async one(t){let[e,r]=await this.req(t),i=async()=>{let d=t.length>>1;await this.one(t.slice(0,d)),await this.one(t.slice(d))};if(r.body.length>Le&&t.length>1)return i();let o=await this.f(e,{...r,signal:this.signal}),s=o.status;if(s===200||s===201||s===400)return;if(s===413&&t.length>1)return i();let a=ye(s);if(a)throw a;if(s===429){let[d]=await E(o).catch(()=>[{}]),l=Number(d.retryAfter);throw new u(o.headers.get("Retry-After")?H(o):l>0?Math.min(l,3600):10)}throw new Error(`HTTP ${s}`)}};var ze=new Set(["reply","addressed","react","unreact"]),be=()=>(S("data-pipeup-agents")??"").split(/[\s,]+/).filter(Boolean),q=class{constructor(t,e,r=()=>[]){this.me=t;this.agents=r;this.held=new Set(e?.held),this.kept=new Set(e?.kept),this.recv=new Set(e?.recv),this.chosen=new Set(e?.chosen),this.follow=new Set(e?.follow),this.legacy=new Set(e?.legacy??(e?.init&&e.chosen===void 0?e.kept:void 0)),this.send=e?.send??!0,this.noted=e?.noted??!1,this.gen=e?.gen??null,this.init=e?.init??!1}held;kept;recv;chosen;follow;legacy;send;noted;gen;init;saved(){let{init:t,held:e,kept:r,recv:i,chosen:o,follow:s,legacy:a,send:d,noted:l,gen:f}=this;return{init:t,held:e,kept:r,recv:i,chosen:o,follow:s,legacy:a,send:d,noted:l,gen:f}}hold(t){if(!this.init){this.init=!0;for(let{body:e}of t)e.author===this.me&&this.held.add(e.id)}}sendable(t){let e=t.body;if(this.recv.has(e.id))return!0;if(ze.has(e.kind)&&this.agents().includes(e.author))return this.send&&![e.target,e.thread].some(i=>i&&(this.held.has(i)||this.kept.has(i)));if(e.author!==this.me||this.held.has(e.id)||this.kept.has(e.id))return!1;let r=[e.target,e.thread].find(i=>i&&(this.held.has(i)||this.kept.has(i)));if(r)(this.held.has(r)?this.held:this.kept).add(e.id),this.follow.add(e.id);else if(!this.send)this.kept.add(e.id);else return(e.kind==="create"||e.kind==="reply")&&(this.noted=!0),!0;return!1}local(t){let e=t.body;return e.author!==this.me||this.recv.has(e.id)||!(this.held.has(e.id)||this.kept.has(e.id))?null:this.follow.has(e.id)?"thread":this.held.has(e.id)?"held":this.chosen.has(e.id)?"chosen":this.legacy.has(e.id)?"unknown":"kept"}decide(t){if(!t)for(let e of this.held)this.kept.add(e),this.chosen.add(e);this.held.clear()}};var He=256e3,je=16,qe=/^[A-Za-z0-9_-]{8,64}$/,we=()=>[P(T(16)),P(T(8)),1e5,256,128,"aes","gcm","none"],Fe=(n,t)=>({v:2,adata:we(),ct:P(t),pasteid:n,parentid:n});async function F(n){if(n.status===429)throw new u(H(n));if(!n.ok)throw new Error(`HTTP ${n.status}`);let[t]=await E(n);if(t.status===0)return t;let e=Number(/\d+/.exec(String(t.message??""))?.[0]);throw new u(e>=1&&e<=3600?e:30,!0)}async function Ke(n,t,e,r="never",i){let o=P(await A(e,"share",[])),s=await F(await n(t,{...B({v:2,adata:[we(),"plaintext",1,0],ct:o,meta:{expire:r}}),signal:i}));if(typeof s.id!="string"||typeof s.deletetoken!="string")throw new Error("the answer had no paste id");return{id:s.id,deleteToken:s.deletetoken}}async function Ge(n,t,e,r){return n(t,B({pasteid:e,deletetoken:r}))}async function Je(n,t,e,r){let i=await z(n,`${t}?${e}`,r);if(i.status===404)return null;if(!i.ok)throw new Error(`HTTP ${i.status}`);let[o,s]=await E(i);if(o.status!==0)return null;let a=o.meta?.time_to_live;return{j:o,size:s,ttl:typeof a=="number"&&a>0?a*1e3:null}}var K=class{constructor(t,e,r,i,o,s=()=>{}){this.a=t;this.l=e;this.f=r;this.signal=i;this.onGen=s;this.head=o||t.paste,this.trail.push(t.paste)}ids=new Set;expires=null;moved=!1;big=!1;head;trail=[];seen=new Map;async scan(t,e){let r;for(let i of Array.isArray(t)?t:[]){if(typeof i?.id!="string"||typeof i.ct!="string")continue;let o=this.seen.get(i.id);if(o===void 0){o=null;let s=null;try{i.ct.length<14e5&&(s=V(i.ct))}catch{}let a=s&&await C(this.l,"share",s),d=a?.next,l=typeof d=="string"?null:N(this.l,a);if(typeof d=="string"&&qe.test(d))o=d;else if(l){e.push(l);for(let f of _(l.ops))this.ids.add(f)}this.seen.set(i.id,o)}r??=o??void 0}return r}async read(){let t=[],e=this.head,r=!1,i=!1;for(let o=0;;){let s=await Je(this.f,this.a.base,e,this.signal);if(!s){if(i)break;if(!r&&e!==this.a.paste){r=!0,e=this.a.paste;continue}throw new b("gone")}i=!0,e!==this.head&&(this.head=e,this.trail.push(e),this.ids.clear(),this.moved=!0,this.onGen(e));let a=s.j.comments,d=await this.scan(a,t);this.expires=s.ttl&&Date.now()+s.ttl;let l=(Array.isArray(a)?a[0]?.ct?.length:0)??0;if(this.big=!d&&s.size>He&&s.size>2*l,!d||++o>=je)break;e=d}return t}ask(t,e){return[this.a.base,B(Fe(t,e))]}async req(t){return this.ask(this.head,await A(this.l,"share",t))}go(t,e){return this.f(t,{...e,signal:this.signal})}async post(t){for(let e of $(t))await F(await this.go(...await this.req(e)))}async roll(t){let e=async s=>{for(let a=0;;a++)try{return await s()}catch(d){if(!(d instanceof u)||a>5||this.signal?.aborted)throw d;await U(d.seconds*1e3*(1+Math.random()*.2),this.signal)}},r=this.head,i=await e(()=>Ke(this.f,this.a.base,this.l,"never",this.signal));for(let s of $(t))await e(async()=>F(await this.go(...this.ask(i.id,await A(this.l,"share",s)))));await e(async()=>F(await this.go(...this.ask(r,await L(this.l,"share",{next:i.id})))));let o=await this.read();return this.head!==i.id&&await Ge(this.f,this.a.base,i.id,i.deleteToken).catch(()=>{}),o}};function Ye(n,t,e,r=Math.random()){let i=t?3e5:n<6e5?25e3+r*1e4:n<18e5?12e4:3e5;return Math.min(3e5,i*2**e)}var G=class{constructor(t,e){this.b=t;this.o=e}id="share";problem=null;offline=!1;newer=0;deliver=()=>{};active=Date.now();errors=0;ok=!1;timer;busy=null;again=!1;limit=0;rolling=!1;ready=null;stopped=!1;off=[];async start(t){this.deliver=t;let e=(i,o,s)=>{i&&(i.addEventListener(o,s),this.off.push(()=>i.removeEventListener(o,s)))},r=globalThis;r.addEventListener&&(e(globalThis,"focus",()=>void this.poll()),e(globalThis,"online",()=>void this.poll()),e(globalThis,"pagehide",()=>this.flush()),e(r.document,"visibilitychange",()=>r.document?.visibilityState==="visible"&&void this.poll())),await this.poll()}stop(){this.stopped=!0,clearTimeout(this.timer);for(let t of this.off.splice(0))t()}async remoteIds(){return this.ok?this.b.ids:null}poll(){if(this.busy)return this.again=!0,this.busy;let t=async()=>{do this.again=!1,await this.read();while(this.again&&!this.stopped)};return this.busy=t().finally(()=>this.busy=null)}bump(){this.active=Date.now()}async read(){clearTimeout(this.timer);try{let t=await this.b.read();this.offline=!1,this.errors=0,this.ok=!0,this.newer+=t.reduce((r,i)=>r+i.newer,0);let e=t.flatMap(r=>r.ops);if(e.length&&(this.active=Date.now(),this.deliver(e)),this.b.moved){this.b.moved=!1;let r=this.o.allowed().filter(i=>!this.b.ids.has(i.body.id));r.length&&await this.b.post(r).catch(()=>{})}this.b.big&&!this.rolling&&this.roll()}catch(t){t instanceof b?this.problem=t.kind:(this.offline=!0,this.errors++)}if(this.o.changed(),!this.stopped&&this.problem!=="gone"){let t=globalThis.document?.visibilityState==="hidden";this.timer=setTimeout(()=>void this.poll(),Ye(Date.now()-this.active,t,this.errors))}}lock(t,e=0){let r=this.o.lock,i=globalThis.navigator?.locks;return r?i?new Promise((o,s)=>{i.request(r,{signal:this.o.signal},async()=>{try{o(await t())}catch(a){s(a)}await U(e,this.o.signal)}).catch(s)}):U(Math.random()*5e3,this.o.signal).then(t):t()}get gap(){return Math.max(this.o.gap,this.limit)}async guarded(t){try{await this.lock(t,this.gap),this.offline=!1}catch(e){throw e instanceof u?(e.limit&&(this.limit=Math.max(this.limit,e.seconds*1e3)),e):e instanceof b?(this.problem=e.kind,this.o.changed(),new u(3600)):(this.offline=!0,this.o.changed(),e)}}async send(t){if(this.problem)throw new u(3600);this.bump(),await this.guarded(async()=>{await this.poll();let e=t.filter(r=>!this.b.ids.has(r.body.id));if(e.length){await this.b.post(e);for(let r of e)this.b.ids.add(r.body.id)}})}async prepare(t){try{this.ready=t.length&&JSON.stringify(t).length<4e4?await this.b.req(t):null}catch{this.ready=null}}flush(){let t=this.ready;this.ready=null,!(!t||this.problem||typeof t[1].body!="string"||t[1].body.length>64e3)&&(this.o.fetch??fetch)(t[0],{...t[1],keepalive:!0}).catch(()=>{})}async roll(){this.rolling=!0;try{await U(Math.random()*3e4,this.o.signal),await this.guarded(async()=>{if(await this.poll(),!this.b.big||!this.b.roll)return;let e=(await this.b.roll(this.o.allowed())).flatMap(r=>r.ops);e.length&&this.deliver(e)})}catch{}finally{this.rolling=!1}}};var Ze="Comments here are shared with everyone who has this page.",Xe=7*864e5,ve=["M12 16V4M7 9l5-5 5 5M5 20h14"],We=["M4 7a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3z","M4 9.5h16M7.5 6.8h.01"],Ve="Only in this browser",Qe={held:"Written before this page was shared. Share it or keep it from the menu.",kept:"Written while Send my comments was off. It stays here.",chosen:"You chose to keep it here.",thread:"Part of a comment that stays in this browser.",unknown:"It stays in this browser and isn't sent to the shared copy."};function ke(n={}){return{id:"share",api:1,version:"0.4.1",needs:["sync","menu","notify","note","commentmark"],network:{when:"page-configured",to:["page"],says:"Sends the comments you choose to share, sealed so only people with this page can read them, to the sharing service the page names. That service sees when and how much is sent, and your network address."},async setup(t){let e=t.document,r=me(n.share??S("data-pipeup-share"),!0);if(r.kind==="off")return t.off(r.reason);if(!Q(e.id))return t.off("needs a document identity");let i=ee(e.id,r.key),o=await ie("share",{memory:t.ephemeral}),s=await o.get(`doc:${e.id}`),a=new q(e.me,s,be);a.hold(e.ops());let d=Promise.resolve(),l=()=>{d=d.then(()=>o.set(`doc:${e.id}`,a.saved())).catch(()=>{})};l();let f={gone:"The shared copy is gone",closed:"This page can't send to the shared copy; ask the author for the current file",full:"The shared copy is full; the author can start it again"},oe=new Set,se=(c,p)=>oe.has(c)||(oe.add(c),t.notify(p)),J=new Set,ae=()=>{let c=new Set;for(let p of e.ops()){if(p.body.kind!=="create"&&p.body.kind!=="reply")continue;let g=h.problem==="gone"?null:a.local(p);g&&(c.add(p.body.id),t.setCommentMark(p.body.id,{text:Ve,tip:Qe[g]}))}for(let p of J)c.has(p)||t.setCommentMark(p,null);J.clear();for(let p of c)J.add(p)},x=()=>{ae();for(let c of I)c.update();t.setStatus({text:ce()}),t.setComposerNote(a.send&&!a.noted&&h.problem!=="gone"?Ze:null),h.newer&&se("newer",`${h.newer} comments come from a newer version of Pipeup; this page can't show them yet.`),h.problem&&se(h.problem,f[h.problem]+(h.problem==="gone"?". Your comments stay in this browser.":"."))},de=(c,p)=>fetch(c,p),le=r.kind==="mailbox"?new j(r,i,de,t.signal):new K(r,i,de,t.signal,a.gen,c=>{a.gen=c,l()}),h=new G(le,{signal:t.signal,gap:1e4,lock:`pipeup-share:${e.id}`,allowed:()=>e.ops().filter(c=>a.sendable(c)),changed:x}),v=new M({id:"share",document:e,merge:async c=>{let p=await t.merge(c),g=new Set(e.ops().map(y=>y.body.id));for(let y of _(c))g.has(y)&&a.recv.add(y);return l(),p},transport:h,sendable:c=>{let p=a.sendable(c);return a.noted!==s?.noted&&l(),p},signal:t.signal,delay:2e3,gap:()=>h.gap}),Y;v.onState(()=>{x(),clearTimeout(Y),Y=setTimeout(()=>void h.prepare(v.pending()),1e3)});let Se=new URL(r.kind==="mailbox"?r.mailbox:r.base).host,ce=()=>{let c=v.waiting;return h.problem?f[h.problem]:a.send?h.offline||v.state==="offline"?"Shared \xB7 offline, sends when back":c?`Shared \xB7 ${c} change${c===1?"":"s"} waiting`:"Shared \xB7 up to date":"Shared \xB7 not sending your comments"},Ae=()=>{let c=le.expires;return h.problem==="gone"?"Your comments stay in this browser. The author can start sharing again.":[`Your comments are encrypted and sent to ${Se} so others with this page see them. The service can't read them. People who already have your comments, by file or live, could pass them on.`,t.ephemeral&&"This browser can't keep comments; the shared copy keeps the ones you send.",!o.lasting&&"This browser won't remember this choice.",c&&c-Date.now()<Xe&&`The shared copy expires on ${new Date(c).toLocaleDateString("en-GB",{day:"numeric",month:"long"})}.`].filter(Boolean).join(" ")},I=[],Z=(c,p,g,y,xe,Pe)=>I[I.push(t.addMenuItem({id:c,icon:p,label:g,hint:y,select:xe,checked:Pe}))-1],X=e.ops().filter(c=>a.held.has(c.body.id)&&(c.body.kind==="create"||c.body.kind==="reply")).length;if(Z("send",ve,()=>a.send||h.problem?ce():"Send my comments",Ae,()=>{a.send=!a.send,l(),v.rescan(),x()},()=>a.send),X){let c=o.lasting?"":" This browser won't remember this choice.",p=g=>()=>{a.decide(g),l();for(let y of I.splice(1))y.remove();v.rescan(),x()};Z("share-earlier",ve,()=>`Share my ${X} earlier comment${X===1?"":"s"}`,()=>`Send the comments you wrote before this page was shared.${c}`,p(!0)),Z("keep-earlier",We,()=>"Keep them in this browser",()=>`Never send the comments you wrote before this page was shared.${c}`,p(!1))}return x(),v.start().then(x,c=>globalThis.reportError?.(c)),e.onChange((c,p,g)=>{if(g==="local"&&p.length){for(let y of p)a.sendable(y);h.bump(),ae(),l()}}),()=>{clearTimeout(Y),v.stop()}}}}export{ke as createAddon,ke as default};
|
|
1
|
+
var A=(n,t)=>t?t.getAttribute(n):globalThis.Pipeup?.setting?.(n)??globalThis.document?.documentElement.getAttribute(n)??null;var m=n=>new TextEncoder().encode(n),V=n=>new TextDecoder().decode(n);function k(n){let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function w(n){let t=n.replace(/-/g,"+").replace(/_/g,"/"),e=atob(t+"=".repeat((4-t.length%4)%4));return Uint8Array.from(e,r=>r.charCodeAt(0))}function T(n){let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t)}function Q(n){return Uint8Array.from(atob(n),t=>t.charCodeAt(0))}var O=n=>crypto.getRandomValues(new Uint8Array(n));async function L(n){return new Uint8Array(await crypto.subtle.digest("SHA-256",n))}var Te=/^([A-Za-z0-9_-]{16}):([A-Za-z0-9_-]{43})$/;function ee(n,t=document){let e=Te.exec((t===globalThis.document?A("data-pipeup-doc"):t.documentElement.getAttribute("data-pipeup-doc"))?.trim()??"");return e&&e[1]===n?w(e[2]):null}var Oe=n=>m(`pipeup/v1/doc:${n}`);async function pe(n,t,e,r){let i=await crypto.subtle.importKey("raw",n,"HKDF",!1,["deriveBits"]);return crypto.subtle.deriveBits({name:"HKDF",hash:"SHA-256",salt:Oe(t),info:m(e)},i,r)}function te(n,t){return{docId:n,key:async e=>crypto.subtle.importKey("raw",await pe(t,n,`pipeup/v1/${e}/key`,256),"AES-GCM",!1,["encrypt","decrypt"]),addr:async e=>k(new Uint8Array(await pe(t,n,`pipeup/v1/${e}/address`,128))),aad:e=>`pipeup/v1/${e}:${n}`}}var Be=1e6,Ee=1e3;async function he(n,t,e=1/0){let r=new Blob([n]).stream().pipeThrough(t).getReader(),i=[],o=0;for(;;){let{done:d,value:c}=await r.read();if(d)break;if(o+=c.length,o>e)throw r.cancel(),new Error("too large");i.push(c)}let s=new Uint8Array(o),a=0;for(let d of i)s.set(d,a),a+=d.length;return s}var x=(n,t,e)=>C(n,t,{pipeup:1,doc:n.docId,sealed:!1,ops:e});async function C(n,t,e){let r=m(JSON.stringify(e)),i=O(12),o=await crypto.subtle.encrypt({name:"AES-GCM",iv:i,additionalData:m(n.aad(t))},await n.key(t),await he(r,new CompressionStream("deflate-raw"))),s=new Uint8Array(13+o.byteLength);return s[0]=1,s.set(i,1),s.set(new Uint8Array(o),13),s}async function N(n,t,e){try{if(e.length<29||e[0]!==1)return null;let r=await crypto.subtle.decrypt({name:"AES-GCM",iv:e.slice(1,13),additionalData:m(n.aad(t))},await n.key(t),e.slice(13));return JSON.parse(V(await he(new Uint8Array(r),new DecompressionStream("deflate-raw"),Be)))}catch{return null}}function z(n,t){let e=t;if(e?.pipeup!==1||e.doc!==n.docId||!Array.isArray(e.ops)||e.ops.length>Ee)return null;let r=0;for(let i of e.ops){let o=i?.body?.v;typeof o=="number"&&o!==1&&r++}return{ops:e.ops,newer:r}}async function ne(n,t,e){return z(n,await N(n,t,e))}var re=n=>`pu1.${k(n)}`,ie=n=>{if(!/^pu1\.[A-Za-z0-9_-]+$/.test(n))return null;try{return w(n.slice(4))}catch{return null}};async function oe(n,t={}){let e=t.keep;if(e)return{get:async o=>await e.get(o),set:(o,s)=>e.set(o,s),lasting:!0};let r=new Map,i={get:async o=>r.get(o),set:async(o,s)=>void r.set(o,s),lasting:!1};if(t.memory||typeof indexedDB>"u")return i;try{let o=await new Promise((a,d)=>{let c=setTimeout(()=>d(new Error("timed out")),4e3),h=indexedDB.open(`pipeup-addon-${n}`,1);h.onupgradeneeded=()=>h.result.createObjectStore("kv"),h.onsuccess=()=>{clearTimeout(c),a(h.result)},h.onerror=h.onblocked=()=>{clearTimeout(c),d(h.error??new Error("blocked"))}}),s=(a,d)=>new Promise((c,h)=>{let S=d(o.transaction("kv",a).objectStore("kv"));S.onsuccess=()=>c(S.result),S.onerror=()=>h(S.error)});return{get:a=>s("readonly",d=>d.get(a)),set:async(a,d)=>void await s("readwrite",c=>c.put(d,a)),lasting:!0}}catch{return i}}var f=class extends Error{constructor(e,r=!1){super(`try again in ${e} s`);this.seconds=e;this.limit=r;this.name="RetryAfter"}};var Ue=100,ue=5e3,_e=5e3,Me=3e5,Re=3600,R=class{constructor(t){this.o=t}state="idle";lastOk=0;known=new Set;outbox=new Map;rejected=new Set;inbound=[];inboundTimer=0;sendTimer=0;lastSend=0;backoff=0;off=null;listeners=new Set;stopped=!1;get waiting(){return this.outbox.size}pending(){return[...this.outbox.values()]}onState(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(t){t&&(this.state=t);for(let e of[...this.listeners])e()}async start(){let{document:t,transport:e,signal:r}=this.o;this.off=t.onChange((o,s,a)=>{if(a===this.o.id)return;let d=!1;for(let c of s)this.want(c)&&(d=this.outbox.set(c.body.id,c).size>0||d);d&&this.schedule(),this.emit()}),r.addEventListener("abort",()=>this.stop(),{once:!0}),await e.start(o=>this.deliver(o));let i=await e.remoteIds?.();if(i)for(let o of i)this.known.add(o);this.rescan()}stop(){this.stopped||(this.stopped=!0,this.off?.(),clearTimeout(this.inboundTimer),clearTimeout(this.sendTimer),this.o.transport.stop())}want(t){return!this.known.has(t.body.id)&&!this.rejected.has(t.body.id)&&this.o.sendable(t)}rescan(){this.outbox.clear();for(let t of this.o.document.ops())this.want(t)&&this.outbox.set(t.body.id,t);this.schedule(),this.emit()}deliver(t){this.inbound.push(...t),this.inboundTimer||(this.inboundTimer=setTimeout(()=>void this.drain(),Ue))}async drain(){this.inboundTimer=0;let t=this.inbound.splice(0),e=t.map(i=>i?.body?.id).filter(i=>typeof i=="string");for(let i=0;i<t.length;i+=ue)try{await this.o.merge(t.slice(i,i+ue))}catch(o){o?.name!=="UnsavedChangeError"&&globalThis.reportError?.(o)}let r=new Set(this.o.document.ops().map(i=>i.body.id));for(let i of e)r.has(i)?(this.known.add(i),this.outbox.delete(i)):this.rejected.add(i);this.lastOk=Date.now(),this.emit()}schedule(t=this.o.delay??0){if(this.stopped||this.sendTimer||this.outbox.size===0)return;let e=typeof this.o.gap=="function"?this.o.gap():this.o.gap??0,r=Math.max(t,this.lastSend+e-Date.now(),this.backoff);this.sendTimer=setTimeout(()=>void this.send(),r)}async send(){if(this.sendTimer=0,this.stopped||this.o.signal.aborted||this.outbox.size===0)return;let t=[...this.outbox.values()].slice(0,this.o.batch??500);this.emit("sending");try{await this.o.transport.send(t);for(let e of t)this.known.add(e.body.id),this.outbox.delete(e.body.id);this.lastSend=Date.now(),this.lastOk=this.lastSend,this.backoff=0,this.emit("idle")}catch(e){e instanceof f?(this.backoff=Math.min(e.seconds,Re)*1e3*(1+Math.random()*.2),this.emit("limited")):(this.backoff=Math.min(Me,this.backoff?this.backoff*2:_e),this.emit("offline"))}this.schedule()}};var gt=2n**256n-2n**32n-977n;var Ie="sharing isn't set up for this page",$="data-pipeup-share isn't a sharing address",De=/^(localhost|127\.0\.0\.1|\[::1\])$/,fe=/^[A-Za-z0-9_-]{43}$/,B=n=>({kind:"off",reason:n});function me(n,t=!1){let e=n?.trim();if(!e)return B(Ie);let r;try{r=new URL(e)}catch{return B($)}if(r.protocol!=="https:"&&!(t&&r.protocol==="http:"&&De.test(r.hostname)))return B($);let i=r.origin+r.pathname,o=r.hash.slice(1);if(o.startsWith("pm1.")){let[a,d,...c]=o.slice(4).split(".");return!fe.test(a??"")||c.length||d!==void 0&&!/^[A-Za-z0-9_-]+$/.test(d)||r.search||!/\/m\/[A-Za-z0-9_-]{22,64}$/.test(i)?B($):{kind:"mailbox",mailbox:i,key:w(a),...d?{token:d}:{}}}let s=/^\?([A-Za-z0-9_-]{8,64})$/.exec(r.search)?.[1];return!s||!fe.test(o)?B($):{kind:"privatebin",base:i,paste:s,key:w(o)}}var b=class extends Error{constructor(e){super(e);this.kind=e;this.name="Problem"}},ge={Accept:"application/json"},H=(n,t,e)=>n(t,{headers:ge,credentials:"omit",signal:e}),E=n=>({method:"POST",headers:ge,credentials:"omit",body:JSON.stringify(n)}),ye=n=>{let t={403:"closed",404:"gone",507:"full"}[n];return t&&new b(t)};async function U(n){let t=await n.text(),e=t.length<1e7&&JSON.parse(t);if(!e||typeof e!="object")throw new Error("the answer was not usable");return[e,t.length]}function I(n,t=9e5){let e=[],r=0;for(let i of n)r+=JSON.stringify(i).length,(!e.length||r>t||e.at(-1).length>=500)&&(e.push([]),r=JSON.stringify(i).length),e.at(-1).push(i);return e}var _=(n,t)=>new Promise(e=>{let r=setTimeout(e,n);t?.addEventListener("abort",()=>{clearTimeout(r),e()},{once:!0})}),j=(n,t=10)=>{let e=Number(n.headers.get("Retry-After"));return e>0?Math.min(e,3600):t},M=n=>n.map(t=>t?.body?.id).filter(t=>typeof t=="string");var Le=524288,Ce=1e3,Ne=async n=>k(await L(m(n))).slice(0,22),K=class{constructor(t,e,r,i){this.a=t;this.l=e;this.f=r;this.signal=i}ids=new Set;expires=null;moved=!1;big=!1;cursor="";seen=new Set;memo=null;async read(){let t=[],e=0;for(let r=0;r<Ce;){let i=`${this.a.mailbox}/ops${this.cursor?`?since=${encodeURIComponent(this.cursor)}`:""}`,o=await H(this.f,i,this.signal);if(o.status===404)throw new b("gone");if(o.status===409&&e++<2){this.cursor="";continue}if(!o.ok)throw new Error(`HTTP ${o.status}`);r++;let[s]=await U(o);for(let a of Array.isArray(s.batches)?s.batches:[]){if(typeof a?.id!="string"||typeof a.ct!="string"||this.seen.has(a.id))continue;this.seen.add(a.id);let d=ie(a.ct),c=d&&await ne(this.l,"share",d);if(c){t.push(c);for(let h of M(c.ops))this.ids.add(h)}}if(typeof s.next=="string"&&(this.cursor=s.next),this.expires=typeof s.expires=="string"&&Date.parse(s.expires)||null,s.more!==!0)break}return t}async req(t){let e=t.map(o=>o.body.id).join();if(this.memo?.key!==e){let o=re(await x(this.l,"share",t));this.memo={key:e,ct:o,id:await Ne(o)}}let{ct:r,id:i}=this.memo;return[`${this.a.mailbox}/ops`,E({id:i,ct:r,token:this.a.token})]}async post(t){for(let e of I(t))await this.one(e)}async one(t){let[e,r]=await this.req(t),i=async()=>{let d=t.length>>1;await this.one(t.slice(0,d)),await this.one(t.slice(d))};if(r.body.length>Le&&t.length>1)return i();let o=await this.f(e,{...r,signal:this.signal}),s=o.status;if(s===200||s===201||s===400)return;if(s===413&&t.length>1)return i();let a=ye(s);if(a)throw a;if(s===429){let[d]=await U(o).catch(()=>[{}]),c=Number(d.retryAfter);throw new f(o.headers.get("Retry-After")?j(o):c>0?Math.min(c,3600):10)}throw new Error(`HTTP ${s}`)}};var ze=new Set(["reply","addressed","react","unreact"]),be=()=>(A("data-pipeup-agents")??"").split(/[\s,]+/).filter(Boolean),q=class{constructor(t,e,r=()=>[]){this.me=t;this.agents=r;this.held=new Set(e?.held),this.kept=new Set(e?.kept),this.recv=new Set(e?.recv),this.chosen=new Set(e?.chosen),this.follow=new Set(e?.follow),this.legacy=new Set(e?.legacy??(e?.init&&e.chosen===void 0?e.kept:void 0)),this.send=e?.send??!0,this.noted=e?.noted??!1,this.gen=e?.gen??null,this.init=e?.init??!1}held;kept;recv;chosen;follow;legacy;send;noted;gen;init;saved(){let{init:t,held:e,kept:r,recv:i,chosen:o,follow:s,legacy:a,send:d,noted:c,gen:h}=this;return{init:t,held:e,kept:r,recv:i,chosen:o,follow:s,legacy:a,send:d,noted:c,gen:h}}hold(t){if(!this.init){this.init=!0;for(let{body:e}of t)e.author===this.me&&this.held.add(e.id)}}sendable(t){let e=t.body;if(this.recv.has(e.id))return!0;if(ze.has(e.kind)&&this.agents().includes(e.author))return this.send&&![e.target,e.thread].some(i=>i&&(this.held.has(i)||this.kept.has(i)));if(e.author!==this.me||this.held.has(e.id)||this.kept.has(e.id))return!1;let r=[e.target,e.thread].find(i=>i&&(this.held.has(i)||this.kept.has(i)));if(r)(this.held.has(r)?this.held:this.kept).add(e.id),this.follow.add(e.id);else if(!this.send)this.kept.add(e.id);else return(e.kind==="create"||e.kind==="reply")&&(this.noted=!0),!0;return!1}local(t){let e=t.body;return e.author!==this.me||this.recv.has(e.id)||!(this.held.has(e.id)||this.kept.has(e.id))?null:this.follow.has(e.id)?"thread":this.held.has(e.id)?"held":this.chosen.has(e.id)?"chosen":this.legacy.has(e.id)?"unknown":"kept"}decide(t){if(!t)for(let e of this.held)this.kept.add(e),this.chosen.add(e);this.held.clear()}};var He=256e3,je=16,Ke=/^[A-Za-z0-9_-]{8,64}$/,we=()=>[T(O(16)),T(O(8)),1e5,256,128,"aes","gcm","none"],qe=(n,t)=>({v:2,adata:we(),ct:T(t),pasteid:n,parentid:n});async function F(n){if(n.status===429)throw new f(j(n));if(!n.ok)throw new Error(`HTTP ${n.status}`);let[t]=await U(n);if(t.status===0)return t;let e=Number(/\d+/.exec(String(t.message??""))?.[0]);throw new f(e>=1&&e<=3600?e:30,!0)}async function Fe(n,t,e,r="never",i){let o=T(await x(e,"share",[])),s=await F(await n(t,{...E({v:2,adata:[we(),"plaintext",1,0],ct:o,meta:{expire:r}}),signal:i}));if(typeof s.id!="string"||typeof s.deletetoken!="string")throw new Error("the answer had no paste id");return{id:s.id,deleteToken:s.deletetoken}}async function Ge(n,t,e,r){return n(t,E({pasteid:e,deletetoken:r}))}async function Je(n,t,e,r){let i=await H(n,`${t}?${e}`,r);if(i.status===404)return null;if(!i.ok)throw new Error(`HTTP ${i.status}`);let[o,s]=await U(i);if(o.status!==0)return null;let a=o.meta?.time_to_live;return{j:o,size:s,ttl:typeof a=="number"&&a>0?a*1e3:null}}var G=class{constructor(t,e,r,i,o,s=()=>{}){this.a=t;this.l=e;this.f=r;this.signal=i;this.onGen=s;this.head=o||t.paste,this.trail.push(t.paste)}ids=new Set;expires=null;moved=!1;big=!1;head;trail=[];seen=new Map;async scan(t,e){let r;for(let i of Array.isArray(t)?t:[]){if(typeof i?.id!="string"||typeof i.ct!="string")continue;let o=this.seen.get(i.id);if(o===void 0){o=null;let s=null;try{i.ct.length<14e5&&(s=Q(i.ct))}catch{}let a=s&&await N(this.l,"share",s),d=a?.next,c=typeof d=="string"?null:z(this.l,a);if(typeof d=="string"&&Ke.test(d))o=d;else if(c){e.push(c);for(let h of M(c.ops))this.ids.add(h)}this.seen.set(i.id,o)}r??=o??void 0}return r}async read(){let t=[],e=this.head,r=!1,i=!1;for(let o=0;;){let s=await Je(this.f,this.a.base,e,this.signal);if(!s){if(i)break;if(!r&&e!==this.a.paste){r=!0,e=this.a.paste;continue}throw new b("gone")}i=!0,e!==this.head&&(this.head=e,this.trail.push(e),this.ids.clear(),this.moved=!0,this.onGen(e));let a=s.j.comments,d=await this.scan(a,t);this.expires=s.ttl&&Date.now()+s.ttl;let c=(Array.isArray(a)?a[0]?.ct?.length:0)??0;if(this.big=!d&&s.size>He&&s.size>2*c,!d||++o>=je)break;e=d}return t}ask(t,e){return[this.a.base,E(qe(t,e))]}async req(t){return this.ask(this.head,await x(this.l,"share",t))}go(t,e){return this.f(t,{...e,signal:this.signal})}async post(t){for(let e of I(t))await F(await this.go(...await this.req(e)))}async roll(t){let e=async s=>{for(let a=0;;a++)try{return await s()}catch(d){if(!(d instanceof f)||a>5||this.signal?.aborted)throw d;await _(d.seconds*1e3*(1+Math.random()*.2),this.signal)}},r=this.head,i=await e(()=>Fe(this.f,this.a.base,this.l,"never",this.signal));for(let s of I(t))await e(async()=>F(await this.go(...this.ask(i.id,await x(this.l,"share",s)))));await e(async()=>F(await this.go(...this.ask(r,await C(this.l,"share",{next:i.id})))));let o=await this.read();return this.head!==i.id&&await Ge(this.f,this.a.base,i.id,i.deleteToken).catch(()=>{}),o}};function Ye(n,t,e,r=Math.random()){let i=t?3e5:n<6e5?25e3+r*1e4:n<18e5?12e4:3e5;return Math.min(3e5,i*2**e)}var J=class{constructor(t,e){this.b=t;this.o=e}id="share";problem=null;offline=!1;newer=0;deliver=()=>{};active=Date.now();errors=0;ok=!1;timer;busy=null;again=!1;limit=0;rolling=!1;ready=null;stopped=!1;off=[];async start(t){this.deliver=t;let e=(i,o,s)=>{i&&(i.addEventListener(o,s),this.off.push(()=>i.removeEventListener(o,s)))},r=globalThis;r.addEventListener&&(e(globalThis,"focus",()=>void this.poll()),e(globalThis,"online",()=>void this.poll()),e(globalThis,"pagehide",()=>this.flush()),e(r.document,"visibilitychange",()=>r.document?.visibilityState==="visible"&&void this.poll())),await this.poll()}stop(){this.stopped=!0,clearTimeout(this.timer);for(let t of this.off.splice(0))t()}async remoteIds(){return this.ok?this.b.ids:null}poll(){if(this.busy)return this.again=!0,this.busy;let t=async()=>{do this.again=!1,await this.read();while(this.again&&!this.stopped)};return this.busy=t().finally(()=>this.busy=null)}bump(){this.active=Date.now()}async read(){clearTimeout(this.timer);try{let t=await this.b.read();this.offline=!1,this.errors=0,this.ok=!0,this.newer+=t.reduce((r,i)=>r+i.newer,0);let e=t.flatMap(r=>r.ops);if(e.length&&(this.active=Date.now(),this.deliver(e)),this.b.moved){this.b.moved=!1;let r=this.o.allowed().filter(i=>!this.b.ids.has(i.body.id));r.length&&await this.b.post(r).catch(()=>{})}this.b.big&&!this.rolling&&this.roll()}catch(t){t instanceof b?this.problem=t.kind:(this.offline=!0,this.errors++)}if(this.o.changed(),!this.stopped&&this.problem!=="gone"){let t=globalThis.document?.visibilityState==="hidden";this.timer=setTimeout(()=>void this.poll(),Ye(Date.now()-this.active,t,this.errors))}}lock(t,e=0){let r=this.o.lock,i=globalThis.navigator?.locks;return r?i?new Promise((o,s)=>{i.request(r,{signal:this.o.signal},async()=>{try{o(await t())}catch(a){s(a)}await _(e,this.o.signal)}).catch(s)}):_(Math.random()*5e3,this.o.signal).then(t):t()}get gap(){return Math.max(this.o.gap,this.limit)}async guarded(t){try{await this.lock(t,this.gap),this.offline=!1}catch(e){throw e instanceof f?(e.limit&&(this.limit=Math.max(this.limit,e.seconds*1e3)),e):e instanceof b?(this.problem=e.kind,this.o.changed(),new f(3600)):(this.offline=!0,this.o.changed(),e)}}async send(t){if(this.problem)throw new f(3600);this.bump(),await this.guarded(async()=>{await this.poll();let e=t.filter(r=>!this.b.ids.has(r.body.id));if(e.length){await this.b.post(e);for(let r of e)this.b.ids.add(r.body.id)}})}async prepare(t){try{this.ready=t.length&&JSON.stringify(t).length<4e4?await this.b.req(t):null}catch{this.ready=null}}flush(){let t=this.ready;this.ready=null,!(!t||this.problem||typeof t[1].body!="string"||t[1].body.length>64e3)&&(this.o.fetch??fetch)(t[0],{...t[1],keepalive:!0}).catch(()=>{})}async roll(){this.rolling=!0;try{await _(Math.random()*3e4,this.o.signal),await this.guarded(async()=>{if(await this.poll(),!this.b.big||!this.b.roll)return;let e=(await this.b.roll(this.o.allowed())).flatMap(r=>r.ops);e.length&&this.deliver(e)})}catch{}finally{this.rolling=!1}}};var Ze="Comments here are shared with everyone who has this page.",Xe=7*864e5,ve=["M12 16V4M7 9l5-5 5 5M5 20h14"],We=["M4 7a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3z","M4 9.5h16M7.5 6.8h.01"],Ve="Only in this browser",Qe={held:"Written before this page was shared. Share it or keep it from the menu.",kept:"Written while Send my comments was off. It stays here.",chosen:"You chose to keep it here.",thread:"Part of a comment that stays in this browser.",unknown:"It stays in this browser and isn't sent to the shared copy."};function ke(n={}){return{id:"share",api:1,version:"0.4.1",needs:["sync","menu","notify","note","commentmark"],network:{when:"page-configured",to:["page"],says:"Sends the comments you choose to share, sealed so only people with this page can read them, to the sharing service the page names. That service sees when and how much is sent, and your network address."},async setup(t){let e=t.document,r=me(n.share??A("data-pipeup-share"),!0);if(r.kind==="off")return t.off(r.reason);if(!ee(e.id))return t.off("needs a document identity");let i=te(e.id,r.key),o=await oe("share",{memory:t.ephemeral,keep:t.keep}),s=await o.get(`doc:${e.id}`),a=new q(e.me,s,be);a.hold(e.ops());let d=Promise.resolve(),c=()=>{d=d.then(()=>o.set(`doc:${e.id}`,a.saved())).catch(()=>{})};c();let h={gone:"The shared copy is gone",closed:"This page can't send to the shared copy; ask the author for the current file",full:"The shared copy is full; the author can start it again"},S=new Set,se=(l,p)=>S.has(l)||(S.add(l),t.notify(p)),Y=new Set,ae=()=>{let l=new Set;for(let p of e.ops()){if(p.body.kind!=="create"&&p.body.kind!=="reply")continue;let g=u.problem==="gone"?null:a.local(p);g&&(l.add(p.body.id),t.setCommentMark(p.body.id,{text:Ve,tip:Qe[g]}))}for(let p of Y)l.has(p)||t.setCommentMark(p,null);Y.clear();for(let p of l)Y.add(p)},P=()=>{ae();for(let l of D)l.update();t.setStatus({text:ce()}),t.setComposerNote(a.send&&!a.noted&&u.problem!=="gone"?Ze:null),u.newer&&se("newer",`${u.newer} comments come from a newer version of Pipeup; this page can't show them yet.`),u.problem&&se(u.problem,h[u.problem]+(u.problem==="gone"?". Your comments stay in this browser.":"."))},de=(l,p)=>fetch(l,p),le=r.kind==="mailbox"?new K(r,i,de,t.signal):new G(r,i,de,t.signal,a.gen,l=>{a.gen=l,c()}),u=new J(le,{signal:t.signal,gap:1e4,lock:`pipeup-share:${e.id}`,allowed:()=>e.ops().filter(l=>a.sendable(l)),changed:P}),v=new R({id:"share",document:e,merge:async l=>{let p=await t.merge(l),g=new Set(e.ops().map(y=>y.body.id));for(let y of M(l))g.has(y)&&a.recv.add(y);return c(),p},transport:u,sendable:l=>{let p=a.sendable(l);return a.noted!==s?.noted&&c(),p},signal:t.signal,delay:2e3,gap:()=>u.gap}),Z;v.onState(()=>{P(),clearTimeout(Z),Z=setTimeout(()=>void u.prepare(v.pending()),1e3)});let Se=new URL(r.kind==="mailbox"?r.mailbox:r.base).host,ce=()=>{let l=v.waiting;return u.problem?h[u.problem]:a.send?u.offline||v.state==="offline"?"Shared \xB7 offline, sends when back":l?`Shared \xB7 ${l} change${l===1?"":"s"} waiting`:"Shared \xB7 up to date":"Shared \xB7 not sending your comments"},Ae=()=>{let l=le.expires;return u.problem==="gone"?"Your comments stay in this browser. The author can start sharing again.":[`Your comments are encrypted and sent to ${Se} so others with this page see them. The service can't read them. People who already have your comments, by file or live, could pass them on.`,t.ephemeral&&"This browser can't keep comments; the shared copy keeps the ones you send.",!o.lasting&&"This browser won't remember this choice.",l&&l-Date.now()<Xe&&`The shared copy expires on ${new Date(l).toLocaleDateString("en-GB",{day:"numeric",month:"long"})}.`].filter(Boolean).join(" ")},D=[],X=(l,p,g,y,xe,Pe)=>D[D.push(t.addMenuItem({id:l,icon:p,label:g,hint:y,select:xe,checked:Pe}))-1],W=e.ops().filter(l=>a.held.has(l.body.id)&&(l.body.kind==="create"||l.body.kind==="reply")).length;if(X("send",ve,()=>a.send||u.problem?ce():"Send my comments",Ae,()=>{a.send=!a.send,c(),v.rescan(),P()},()=>a.send),W){let l=o.lasting?"":" This browser won't remember this choice.",p=g=>()=>{a.decide(g),c();for(let y of D.splice(1))y.remove();v.rescan(),P()};X("share-earlier",ve,()=>`Share my ${W} earlier comment${W===1?"":"s"}`,()=>`Send the comments you wrote before this page was shared.${l}`,p(!0)),X("keep-earlier",We,()=>"Keep them in this browser",()=>`Never send the comments you wrote before this page was shared.${l}`,p(!1))}return P(),v.start().then(P,l=>globalThis.reportError?.(l)),e.onChange((l,p,g)=>{if(g==="local"&&p.length){for(let y of p)a.sendable(y);u.bump(),ae(),c()}}),()=>{clearTimeout(Z),v.stop()}}}}export{ke as createAddon,ke as default};
|
package/dist/share.headless.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var p=n=>new TextEncoder().encode(n),F=n=>new TextDecoder().decode(n);function g(n){let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function m(n){let t=n.replace(/-/g,"+").replace(/_/g,"/"),e=atob(t+"=".repeat((4-t.length%4)%4));return Uint8Array.from(e,r=>r.charCodeAt(0))}function w(n){let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t)}function H(n){return Uint8Array.from(atob(n),t=>t.charCodeAt(0))}var v=n=>crypto.getRandomValues(new Uint8Array(n));async function E(n){return new Uint8Array(await crypto.subtle.digest("SHA-256",n))}var at=n=>p(`pipeup/v1/doc:${n}`);async function Y(n,t,e,r){let i=await crypto.subtle.importKey("raw",n,"HKDF",!1,["deriveBits"]);return crypto.subtle.deriveBits({name:"HKDF",hash:"SHA-256",salt:at(t),info:p(e)},i,r)}function K(n,t){return{docId:n,key:async e=>crypto.subtle.importKey("raw",await Y(t,n,`pipeup/v1/${e}/key`,256),"AES-GCM",!1,["encrypt","decrypt"]),addr:async e=>g(new Uint8Array(await Y(t,n,`pipeup/v1/${e}/address`,128))),aad:e=>`pipeup/v1/${e}:${n}`}}var dt=1e6,lt=1e3;async function V(n,t,e=1/0){let r=new Blob([n]).stream().pipeThrough(t).getReader(),i=[],o=0;for(;;){let{done:a,value:l}=await r.read();if(a)break;if(o+=l.length,o>e)throw r.cancel(),new Error("too large");i.push(l)}let s=new Uint8Array(o),d=0;for(let a of i)s.set(a,d),d+=a.length;return s}var y=(n,t,e)=>_(n,t,{pipeup:1,doc:n.docId,sealed:!1,ops:e});async function _(n,t,e){let r=p(JSON.stringify(e)),i=v(12),o=await crypto.subtle.encrypt({name:"AES-GCM",iv:i,additionalData:p(n.aad(t))},await n.key(t),await V(r,new CompressionStream("deflate-raw"))),s=new Uint8Array(13+o.byteLength);return s[0]=1,s.set(i,1),s.set(new Uint8Array(o),13),s}async function R(n,t,e){try{if(e.length<29||e[0]!==1)return null;let r=await crypto.subtle.decrypt({name:"AES-GCM",iv:e.slice(1,13),additionalData:p(n.aad(t))},await n.key(t),e.slice(13));return JSON.parse(F(await V(new Uint8Array(r),new DecompressionStream("deflate-raw"),dt)))}catch{return null}}function D(n,t){let e=t;if(e?.pipeup!==1||e.doc!==n.docId||!Array.isArray(e.ops)||e.ops.length>lt)return null;let r=0;for(let i of e.ops){let o=i?.body?.v;typeof o=="number"&&o!==1&&r++}return{ops:e.ops,newer:r}}async function G(n,t,e){return D(n,await R(n,t,e))}var J=n=>`pu1.${g(n)}`,Z=n=>{if(!/^pu1\.[A-Za-z0-9_-]+$/.test(n))return null;try{return m(n.slice(4))}catch{return null}};var c=class extends Error{constructor(e,r=!1){super(`try again in ${e} s`);this.seconds=e;this.limit=r;this.name="RetryAfter"}};var ct=100,W=5e3,pt=5e3,ut=3e5,ft=3600,O=class{constructor(t){this.o=t}state="idle";lastOk=0;known=new Set;outbox=new Map;rejected=new Set;inbound=[];inboundTimer=0;sendTimer=0;lastSend=0;backoff=0;off=null;listeners=new Set;stopped=!1;get waiting(){return this.outbox.size}pending(){return[...this.outbox.values()]}onState(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(t){t&&(this.state=t);for(let e of[...this.listeners])e()}async start(){let{document:t,transport:e,signal:r}=this.o;this.off=t.onChange((o,s,d)=>{if(d===this.o.id)return;let a=!1;for(let l of s)this.want(l)&&(a=this.outbox.set(l.body.id,l).size>0||a);a&&this.schedule(),this.emit()}),r.addEventListener("abort",()=>this.stop(),{once:!0}),await e.start(o=>this.deliver(o));let i=await e.remoteIds?.();if(i)for(let o of i)this.known.add(o);this.rescan()}stop(){this.stopped||(this.stopped=!0,this.off?.(),clearTimeout(this.inboundTimer),clearTimeout(this.sendTimer),this.o.transport.stop())}want(t){return!this.known.has(t.body.id)&&!this.rejected.has(t.body.id)&&this.o.sendable(t)}rescan(){this.outbox.clear();for(let t of this.o.document.ops())this.want(t)&&this.outbox.set(t.body.id,t);this.schedule(),this.emit()}deliver(t){this.inbound.push(...t),this.inboundTimer||(this.inboundTimer=setTimeout(()=>void this.drain(),ct))}async drain(){this.inboundTimer=0;let t=this.inbound.splice(0),e=t.map(i=>i?.body?.id).filter(i=>typeof i=="string");for(let i=0;i<t.length;i+=W)try{await this.o.merge(t.slice(i,i+W))}catch(o){o?.name!=="UnsavedChangeError"&&globalThis.reportError?.(o)}let r=new Set(this.o.document.ops().map(i=>i.body.id));for(let i of e)r.has(i)?(this.known.add(i),this.outbox.delete(i)):this.rejected.add(i);this.lastOk=Date.now(),this.emit()}schedule(t=this.o.delay??0){if(this.stopped||this.sendTimer||this.outbox.size===0)return;let e=typeof this.o.gap=="function"?this.o.gap():this.o.gap??0,r=Math.max(t,this.lastSend+e-Date.now(),this.backoff);this.sendTimer=setTimeout(()=>void this.send(),r)}async send(){if(this.sendTimer=0,this.stopped||this.o.signal.aborted||this.outbox.size===0)return;let t=[...this.outbox.values()].slice(0,this.o.batch??500);this.emit("sending");try{await this.o.transport.send(t);for(let e of t)this.known.add(e.body.id),this.outbox.delete(e.body.id);this.lastSend=Date.now(),this.lastOk=this.lastSend,this.backoff=0,this.emit("idle")}catch(e){e instanceof c?(this.backoff=Math.min(e.seconds,ft)*1e3*(1+Math.random()*.2),this.emit("limited")):(this.backoff=Math.min(ut,this.backoff?this.backoff*2:pt),this.emit("offline"))}this.schedule()}};var Ht=2n**256n-2n**32n-977n;var gt="sharing isn't set up for this page",B="data-pipeup-share isn't a sharing address",mt=/^(localhost|127\.0\.0\.1|\[::1\])$/,Q=/^[A-Za-z0-9_-]{43}$/,x=n=>({kind:"off",reason:n});function tt(n,t=!1){let e=n?.trim();if(!e)return x(gt);let r;try{r=new URL(e)}catch{return x(B)}if(r.protocol!=="https:"&&!(t&&r.protocol==="http:"&&mt.test(r.hostname)))return x(B);let i=r.origin+r.pathname,o=r.hash.slice(1);if(o.startsWith("pm1.")){let[d,a,...l]=o.slice(4).split(".");return!Q.test(d??"")||l.length||a!==void 0&&!/^[A-Za-z0-9_-]+$/.test(a)||r.search||!/\/m\/[A-Za-z0-9_-]{22,64}$/.test(i)?x(B):{kind:"mailbox",mailbox:i,key:m(d),...a?{token:a}:{}}}let s=/^\?([A-Za-z0-9_-]{8,64})$/.exec(r.search)?.[1];return!s||!Q.test(o)?x(B):{kind:"privatebin",base:i,paste:s,key:m(o)}}var f=class extends Error{constructor(e){super(e);this.kind=e;this.name="Problem"}},et={Accept:"application/json"},M=(n,t,e)=>n(t,{headers:et,credentials:"omit",signal:e}),A=n=>({method:"POST",headers:et,credentials:"omit",body:JSON.stringify(n)}),nt=n=>{let t={403:"closed",404:"gone",507:"full"}[n];return t&&new f(t)};async function k(n){let t=await n.text(),e=t.length<1e7&&JSON.parse(t);if(!e||typeof e!="object")throw new Error("the answer was not usable");return[e,t.length]}function U(n,t=9e5){let e=[],r=0;for(let i of n)r+=JSON.stringify(i).length,(!e.length||r>t||e.at(-1).length>=500)&&(e.push([]),r=JSON.stringify(i).length),e.at(-1).push(i);return e}var S=(n,t)=>new Promise(e=>{let r=setTimeout(e,n);t?.addEventListener("abort",()=>{clearTimeout(r),e()},{once:!0})}),L=(n,t=10)=>{let e=Number(n.headers.get("Retry-After"));return e>0?Math.min(e,3600):t},P=n=>n.map(t=>t?.body?.id).filter(t=>typeof t=="string");var yt=524288,bt=1e3,wt=async n=>g(await E(p(n))).slice(0,22),$=class{constructor(t,e,r,i){this.a=t;this.l=e;this.f=r;this.signal=i}ids=new Set;expires=null;moved=!1;big=!1;cursor="";seen=new Set;memo=null;async read(){let t=[],e=0;for(let r=0;r<bt;){let i=`${this.a.mailbox}/ops${this.cursor?`?since=${encodeURIComponent(this.cursor)}`:""}`,o=await M(this.f,i,this.signal);if(o.status===404)throw new f("gone");if(o.status===409&&e++<2){this.cursor="";continue}if(!o.ok)throw new Error(`HTTP ${o.status}`);r++;let[s]=await k(o);for(let d of Array.isArray(s.batches)?s.batches:[]){if(typeof d?.id!="string"||typeof d.ct!="string"||this.seen.has(d.id))continue;this.seen.add(d.id);let a=Z(d.ct),l=a&&await G(this.l,"share",a);if(l){t.push(l);for(let u of P(l.ops))this.ids.add(u)}}if(typeof s.next=="string"&&(this.cursor=s.next),this.expires=typeof s.expires=="string"&&Date.parse(s.expires)||null,s.more!==!0)break}return t}async req(t){let e=t.map(o=>o.body.id).join();if(this.memo?.key!==e){let o=J(await y(this.l,"share",t));this.memo={key:e,ct:o,id:await wt(o)}}let{ct:r,id:i}=this.memo;return[`${this.a.mailbox}/ops`,A({id:i,ct:r,token:this.a.token})]}async post(t){for(let e of U(t))await this.one(e)}async one(t){let[e,r]=await this.req(t),i=async()=>{let a=t.length>>1;await this.one(t.slice(0,a)),await this.one(t.slice(a))};if(r.body.length>yt&&t.length>1)return i();let o=await this.f(e,{...r,signal:this.signal}),s=o.status;if(s===200||s===201||s===400)return;if(s===413&&t.length>1)return i();let d=nt(s);if(d)throw d;if(s===429){let[a]=await k(o).catch(()=>[{}]),l=Number(a.retryAfter);throw new c(o.headers.get("Retry-After")?L(o):l>0?Math.min(l,3600):10)}throw new Error(`HTTP ${s}`)}};var vt=256e3,xt=16,At=/^[A-Za-z0-9_-]{8,64}$/,rt=()=>[w(v(16)),w(v(8)),1e5,256,128,"aes","gcm","none"],kt=(n,t)=>({v:2,adata:rt(),ct:w(t),pasteid:n,parentid:n});async function I(n){if(n.status===429)throw new c(L(n));if(!n.ok)throw new Error(`HTTP ${n.status}`);let[t]=await k(n);if(t.status===0)return t;let e=Number(/\d+/.exec(String(t.message??""))?.[0]);throw new c(e>=1&&e<=3600?e:30,!0)}async function St(n,t,e,r="never",i){let o=w(await y(e,"share",[])),s=await I(await n(t,{...A({v:2,adata:[rt(),"plaintext",1,0],ct:o,meta:{expire:r}}),signal:i}));if(typeof s.id!="string"||typeof s.deletetoken!="string")throw new Error("the answer had no paste id");return{id:s.id,deleteToken:s.deletetoken}}async function Pt(n,t,e,r){return n(t,A({pasteid:e,deletetoken:r}))}async function Tt(n,t,e,r){let i=await M(n,`${t}?${e}`,r);if(i.status===404)return null;if(!i.ok)throw new Error(`HTTP ${i.status}`);let[o,s]=await k(i);if(o.status!==0)return null;let d=o.meta?.time_to_live;return{j:o,size:s,ttl:typeof d=="number"&&d>0?d*1e3:null}}var C=class{constructor(t,e,r,i,o,s=()=>{}){this.a=t;this.l=e;this.f=r;this.signal=i;this.onGen=s;this.head=o||t.paste,this.trail.push(t.paste)}ids=new Set;expires=null;moved=!1;big=!1;head;trail=[];seen=new Map;async scan(t,e){let r;for(let i of Array.isArray(t)?t:[]){if(typeof i?.id!="string"||typeof i.ct!="string")continue;let o=this.seen.get(i.id);if(o===void 0){o=null;let s=null;try{i.ct.length<14e5&&(s=H(i.ct))}catch{}let d=s&&await R(this.l,"share",s),a=d?.next,l=typeof a=="string"?null:D(this.l,d);if(typeof a=="string"&&At.test(a))o=a;else if(l){e.push(l);for(let u of P(l.ops))this.ids.add(u)}this.seen.set(i.id,o)}r??=o??void 0}return r}async read(){let t=[],e=this.head,r=!1,i=!1;for(let o=0;;){let s=await Tt(this.f,this.a.base,e,this.signal);if(!s){if(i)break;if(!r&&e!==this.a.paste){r=!0,e=this.a.paste;continue}throw new f("gone")}i=!0,e!==this.head&&(this.head=e,this.trail.push(e),this.ids.clear(),this.moved=!0,this.onGen(e));let d=s.j.comments,a=await this.scan(d,t);this.expires=s.ttl&&Date.now()+s.ttl;let l=(Array.isArray(d)?d[0]?.ct?.length:0)??0;if(this.big=!a&&s.size>vt&&s.size>2*l,!a||++o>=xt)break;e=a}return t}ask(t,e){return[this.a.base,A(kt(t,e))]}async req(t){return this.ask(this.head,await y(this.l,"share",t))}go(t,e){return this.f(t,{...e,signal:this.signal})}async post(t){for(let e of U(t))await I(await this.go(...await this.req(e)))}async roll(t){let e=async s=>{for(let d=0;;d++)try{return await s()}catch(a){if(!(a instanceof c)||d>5||this.signal?.aborted)throw a;await S(a.seconds*1e3*(1+Math.random()*.2),this.signal)}},r=this.head,i=await e(()=>St(this.f,this.a.base,this.l,"never",this.signal));for(let s of U(t))await e(async()=>I(await this.go(...this.ask(i.id,await y(this.l,"share",s)))));await e(async()=>I(await this.go(...this.ask(r,await _(this.l,"share",{next:i.id})))));let o=await this.read();return this.head!==i.id&&await Pt(this.f,this.a.base,i.id,i.deleteToken).catch(()=>{}),o}};function Ot(n,t,e,r=Math.random()){let i=t?3e5:n<6e5?25e3+r*1e4:n<18e5?12e4:3e5;return Math.min(3e5,i*2**e)}var z=class{constructor(t,e){this.b=t;this.o=e}id="share";problem=null;offline=!1;newer=0;deliver=()=>{};active=Date.now();errors=0;ok=!1;timer;busy=null;again=!1;limit=0;rolling=!1;ready=null;stopped=!1;off=[];async start(t){this.deliver=t;let e=(i,o,s)=>{i&&(i.addEventListener(o,s),this.off.push(()=>i.removeEventListener(o,s)))},r=globalThis;r.addEventListener&&(e(globalThis,"focus",()=>void this.poll()),e(globalThis,"online",()=>void this.poll()),e(globalThis,"pagehide",()=>this.flush()),e(r.document,"visibilitychange",()=>r.document?.visibilityState==="visible"&&void this.poll())),await this.poll()}stop(){this.stopped=!0,clearTimeout(this.timer);for(let t of this.off.splice(0))t()}async remoteIds(){return this.ok?this.b.ids:null}poll(){if(this.busy)return this.again=!0,this.busy;let t=async()=>{do this.again=!1,await this.read();while(this.again&&!this.stopped)};return this.busy=t().finally(()=>this.busy=null)}bump(){this.active=Date.now()}async read(){clearTimeout(this.timer);try{let t=await this.b.read();this.offline=!1,this.errors=0,this.ok=!0,this.newer+=t.reduce((r,i)=>r+i.newer,0);let e=t.flatMap(r=>r.ops);if(e.length&&(this.active=Date.now(),this.deliver(e)),this.b.moved){this.b.moved=!1;let r=this.o.allowed().filter(i=>!this.b.ids.has(i.body.id));r.length&&await this.b.post(r).catch(()=>{})}this.b.big&&!this.rolling&&this.roll()}catch(t){t instanceof f?this.problem=t.kind:(this.offline=!0,this.errors++)}if(this.o.changed(),!this.stopped&&this.problem!=="gone"){let t=globalThis.document?.visibilityState==="hidden";this.timer=setTimeout(()=>void this.poll(),Ot(Date.now()-this.active,t,this.errors))}}lock(t,e=0){let r=this.o.lock,i=globalThis.navigator?.locks;return r?i?new Promise((o,s)=>{i.request(r,{signal:this.o.signal},async()=>{try{o(await t())}catch(d){s(d)}await S(e,this.o.signal)}).catch(s)}):S(Math.random()*5e3,this.o.signal).then(t):t()}get gap(){return Math.max(this.o.gap,this.limit)}async guarded(t){try{await this.lock(t,this.gap),this.offline=!1}catch(e){throw e instanceof c?(e.limit&&(this.limit=Math.max(this.limit,e.seconds*1e3)),e):e instanceof f?(this.problem=e.kind,this.o.changed(),new c(3600)):(this.offline=!0,this.o.changed(),e)}}async send(t){if(this.problem)throw new c(3600);this.bump(),await this.guarded(async()=>{await this.poll();let e=t.filter(r=>!this.b.ids.has(r.body.id));if(e.length){await this.b.post(e);for(let r of e)this.b.ids.add(r.body.id)}})}async prepare(t){try{this.ready=t.length&&JSON.stringify(t).length<4e4?await this.b.req(t):null}catch{this.ready=null}}flush(){let t=this.ready;this.ready=null,!(!t||this.problem||typeof t[1].body!="string"||t[1].body.length>64e3)&&(this.o.fetch??fetch)(t[0],{...t[1],keepalive:!0}).catch(()=>{})}async roll(){this.rolling=!0;try{await S(Math.random()*3e4,this.o.signal),await this.guarded(async()=>{if(await this.poll(),!this.b.big||!this.b.roll)return;let e=(await this.b.roll(this.o.allowed())).flatMap(r=>r.ops);e.length&&this.deliver(e)})}catch{}finally{this.rolling=!1}}};async function De(n,t){let e=tt(t.share,t.local);if(e.kind==="off")throw new Error(e.reason);let r=K(n.id,e.key),i=t.fetch??((h,T)=>fetch(h,T)),o=e.kind==="mailbox"?new $(e,r,i,t.signal):new C(e,r,i,t.signal),s=new Set,d=t.sendable??(h=>s.has(h.body.id)||h.body.author===n.me),a=t.signal??new AbortController().signal,l=new z(o,{signal:a,gap:t.gap??1e4,lock:!1,allowed:()=>n.ops().filter(d),changed(){}}),u=new O({id:"share",document:n,merge:async h=>{let T=await n.merge(h,"share"),N=new Set(n.ops().map(b=>b.body.id));for(let b of P(h))N.has(b)&&s.add(b);return T},transport:l,sendable:d,signal:a,delay:t.delay??0,gap:()=>l.gap});return await u.start(),{problem:()=>l.problem,waiting:()=>u.waiting,stop:()=>u.stop(),flush:async(h=12e4)=>(await l.poll(),new Promise((T,N)=>{let b=Date.now(),it=setInterval(j,250),ot=u.onState(j);function q(X){clearInterval(it),ot(),X?N(X):T()}function j(){if(l.problem)return q(new Error(`sharing stopped: the shared copy is ${l.problem==="gone"?"gone":l.problem==="full"?"full":"refusing this page"}`));if(!u.waiting&&u.state!=="sending")return q();Date.now()-b>h&&q(new Error("timed out before everything was sent"))}u.rescan(),j()}))}}export{De as connectShare};
|
|
1
|
+
var c=n=>new TextEncoder().encode(n),F=n=>new TextDecoder().decode(n);function g(n){let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function m(n){let t=n.replace(/-/g,"+").replace(/_/g,"/"),e=atob(t+"=".repeat((4-t.length%4)%4));return Uint8Array.from(e,r=>r.charCodeAt(0))}function w(n){let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t)}function K(n){return Uint8Array.from(atob(n),t=>t.charCodeAt(0))}var v=n=>crypto.getRandomValues(new Uint8Array(n));async function E(n){return new Uint8Array(await crypto.subtle.digest("SHA-256",n))}var at=n=>c(`pipeup/v1/doc:${n}`);async function Y(n,t,e,r){let i=await crypto.subtle.importKey("raw",n,"HKDF",!1,["deriveBits"]);return crypto.subtle.deriveBits({name:"HKDF",hash:"SHA-256",salt:at(t),info:c(e)},i,r)}function H(n,t){return{docId:n,key:async e=>crypto.subtle.importKey("raw",await Y(t,n,`pipeup/v1/${e}/key`,256),"AES-GCM",!1,["encrypt","decrypt"]),addr:async e=>g(new Uint8Array(await Y(t,n,`pipeup/v1/${e}/address`,128))),aad:e=>`pipeup/v1/${e}:${n}`}}var dt=1e6,pt=1e3;async function V(n,t,e=1/0){let r=new Blob([n]).stream().pipeThrough(t).getReader(),i=[],o=0;for(;;){let{done:a,value:p}=await r.read();if(a)break;if(o+=p.length,o>e)throw r.cancel(),new Error("too large");i.push(p)}let s=new Uint8Array(o),d=0;for(let a of i)s.set(a,d),d+=a.length;return s}var y=(n,t,e)=>_(n,t,{pipeup:1,doc:n.docId,sealed:!1,ops:e});async function _(n,t,e){let r=c(JSON.stringify(e)),i=v(12),o=await crypto.subtle.encrypt({name:"AES-GCM",iv:i,additionalData:c(n.aad(t))},await n.key(t),await V(r,new CompressionStream("deflate-raw"))),s=new Uint8Array(13+o.byteLength);return s[0]=1,s.set(i,1),s.set(new Uint8Array(o),13),s}async function R(n,t,e){try{if(e.length<29||e[0]!==1)return null;let r=await crypto.subtle.decrypt({name:"AES-GCM",iv:e.slice(1,13),additionalData:c(n.aad(t))},await n.key(t),e.slice(13));return JSON.parse(F(await V(new Uint8Array(r),new DecompressionStream("deflate-raw"),dt)))}catch{return null}}function D(n,t){let e=t;if(e?.pipeup!==1||e.doc!==n.docId||!Array.isArray(e.ops)||e.ops.length>pt)return null;let r=0;for(let i of e.ops){let o=i?.body?.v;typeof o=="number"&&o!==1&&r++}return{ops:e.ops,newer:r}}async function G(n,t,e){return D(n,await R(n,t,e))}var J=n=>`pu1.${g(n)}`,Z=n=>{if(!/^pu1\.[A-Za-z0-9_-]+$/.test(n))return null;try{return m(n.slice(4))}catch{return null}};var l=class extends Error{constructor(e,r=!1){super(`try again in ${e} s`);this.seconds=e;this.limit=r;this.name="RetryAfter"}};var lt=100,W=5e3,ct=5e3,ut=3e5,ft=3600,O=class{constructor(t){this.o=t}state="idle";lastOk=0;known=new Set;outbox=new Map;rejected=new Set;inbound=[];inboundTimer=0;sendTimer=0;lastSend=0;backoff=0;off=null;listeners=new Set;stopped=!1;get waiting(){return this.outbox.size}pending(){return[...this.outbox.values()]}onState(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(t){t&&(this.state=t);for(let e of[...this.listeners])e()}async start(){let{document:t,transport:e,signal:r}=this.o;this.off=t.onChange((o,s,d)=>{if(d===this.o.id)return;let a=!1;for(let p of s)this.want(p)&&(a=this.outbox.set(p.body.id,p).size>0||a);a&&this.schedule(),this.emit()}),r.addEventListener("abort",()=>this.stop(),{once:!0}),await e.start(o=>this.deliver(o));let i=await e.remoteIds?.();if(i)for(let o of i)this.known.add(o);this.rescan()}stop(){this.stopped||(this.stopped=!0,this.off?.(),clearTimeout(this.inboundTimer),clearTimeout(this.sendTimer),this.o.transport.stop())}want(t){return!this.known.has(t.body.id)&&!this.rejected.has(t.body.id)&&this.o.sendable(t)}rescan(){this.outbox.clear();for(let t of this.o.document.ops())this.want(t)&&this.outbox.set(t.body.id,t);this.schedule(),this.emit()}deliver(t){this.inbound.push(...t),this.inboundTimer||(this.inboundTimer=setTimeout(()=>void this.drain(),lt))}async drain(){this.inboundTimer=0;let t=this.inbound.splice(0),e=t.map(i=>i?.body?.id).filter(i=>typeof i=="string");for(let i=0;i<t.length;i+=W)try{await this.o.merge(t.slice(i,i+W))}catch(o){o?.name!=="UnsavedChangeError"&&globalThis.reportError?.(o)}let r=new Set(this.o.document.ops().map(i=>i.body.id));for(let i of e)r.has(i)?(this.known.add(i),this.outbox.delete(i)):this.rejected.add(i);this.lastOk=Date.now(),this.emit()}schedule(t=this.o.delay??0){if(this.stopped||this.sendTimer||this.outbox.size===0)return;let e=typeof this.o.gap=="function"?this.o.gap():this.o.gap??0,r=Math.max(t,this.lastSend+e-Date.now(),this.backoff);this.sendTimer=setTimeout(()=>void this.send(),r)}async send(){if(this.sendTimer=0,this.stopped||this.o.signal.aborted||this.outbox.size===0)return;let t=[...this.outbox.values()].slice(0,this.o.batch??500);this.emit("sending");try{await this.o.transport.send(t);for(let e of t)this.known.add(e.body.id),this.outbox.delete(e.body.id);this.lastSend=Date.now(),this.lastOk=this.lastSend,this.backoff=0,this.emit("idle")}catch(e){e instanceof l?(this.backoff=Math.min(e.seconds,ft)*1e3*(1+Math.random()*.2),this.emit("limited")):(this.backoff=Math.min(ut,this.backoff?this.backoff*2:ct),this.emit("offline"))}this.schedule()}};var Kt=2n**256n-2n**32n-977n;var gt="sharing isn't set up for this page",B="data-pipeup-share isn't a sharing address",mt=/^(localhost|127\.0\.0\.1|\[::1\])$/,Q=/^[A-Za-z0-9_-]{43}$/,x=n=>({kind:"off",reason:n});function tt(n,t=!1){let e=n?.trim();if(!e)return x(gt);let r;try{r=new URL(e)}catch{return x(B)}if(r.protocol!=="https:"&&!(t&&r.protocol==="http:"&&mt.test(r.hostname)))return x(B);let i=r.origin+r.pathname,o=r.hash.slice(1);if(o.startsWith("pm1.")){let[d,a,...p]=o.slice(4).split(".");return!Q.test(d??"")||p.length||a!==void 0&&!/^[A-Za-z0-9_-]+$/.test(a)||r.search||!/\/m\/[A-Za-z0-9_-]{22,64}$/.test(i)?x(B):{kind:"mailbox",mailbox:i,key:m(d),...a?{token:a}:{}}}let s=/^\?([A-Za-z0-9_-]{8,64})$/.exec(r.search)?.[1];return!s||!Q.test(o)?x(B):{kind:"privatebin",base:i,paste:s,key:m(o)}}var f=class extends Error{constructor(e){super(e);this.kind=e;this.name="Problem"}},et={Accept:"application/json"},M=(n,t,e)=>n(t,{headers:et,credentials:"omit",signal:e}),A=n=>({method:"POST",headers:et,credentials:"omit",body:JSON.stringify(n)}),nt=n=>{let t={403:"closed",404:"gone",507:"full"}[n];return t&&new f(t)};async function k(n){let t=await n.text(),e=t.length<1e7&&JSON.parse(t);if(!e||typeof e!="object")throw new Error("the answer was not usable");return[e,t.length]}function U(n,t=9e5){let e=[],r=0;for(let i of n)r+=JSON.stringify(i).length,(!e.length||r>t||e.at(-1).length>=500)&&(e.push([]),r=JSON.stringify(i).length),e.at(-1).push(i);return e}var S=(n,t)=>new Promise(e=>{let r=setTimeout(e,n);t?.addEventListener("abort",()=>{clearTimeout(r),e()},{once:!0})}),L=(n,t=10)=>{let e=Number(n.headers.get("Retry-After"));return e>0?Math.min(e,3600):t},P=n=>n.map(t=>t?.body?.id).filter(t=>typeof t=="string");var yt=524288,bt=1e3,wt=async n=>g(await E(c(n))).slice(0,22),$=class{constructor(t,e,r,i){this.a=t;this.l=e;this.f=r;this.signal=i}ids=new Set;expires=null;moved=!1;big=!1;cursor="";seen=new Set;memo=null;async read(){let t=[],e=0;for(let r=0;r<bt;){let i=`${this.a.mailbox}/ops${this.cursor?`?since=${encodeURIComponent(this.cursor)}`:""}`,o=await M(this.f,i,this.signal);if(o.status===404)throw new f("gone");if(o.status===409&&e++<2){this.cursor="";continue}if(!o.ok)throw new Error(`HTTP ${o.status}`);r++;let[s]=await k(o);for(let d of Array.isArray(s.batches)?s.batches:[]){if(typeof d?.id!="string"||typeof d.ct!="string"||this.seen.has(d.id))continue;this.seen.add(d.id);let a=Z(d.ct),p=a&&await G(this.l,"share",a);if(p){t.push(p);for(let u of P(p.ops))this.ids.add(u)}}if(typeof s.next=="string"&&(this.cursor=s.next),this.expires=typeof s.expires=="string"&&Date.parse(s.expires)||null,s.more!==!0)break}return t}async req(t){let e=t.map(o=>o.body.id).join();if(this.memo?.key!==e){let o=J(await y(this.l,"share",t));this.memo={key:e,ct:o,id:await wt(o)}}let{ct:r,id:i}=this.memo;return[`${this.a.mailbox}/ops`,A({id:i,ct:r,token:this.a.token})]}async post(t){for(let e of U(t))await this.one(e)}async one(t){let[e,r]=await this.req(t),i=async()=>{let a=t.length>>1;await this.one(t.slice(0,a)),await this.one(t.slice(a))};if(r.body.length>yt&&t.length>1)return i();let o=await this.f(e,{...r,signal:this.signal}),s=o.status;if(s===200||s===201||s===400)return;if(s===413&&t.length>1)return i();let d=nt(s);if(d)throw d;if(s===429){let[a]=await k(o).catch(()=>[{}]),p=Number(a.retryAfter);throw new l(o.headers.get("Retry-After")?L(o):p>0?Math.min(p,3600):10)}throw new Error(`HTTP ${s}`)}};var vt=256e3,xt=16,At=/^[A-Za-z0-9_-]{8,64}$/,rt=()=>[w(v(16)),w(v(8)),1e5,256,128,"aes","gcm","none"],kt=(n,t)=>({v:2,adata:rt(),ct:w(t),pasteid:n,parentid:n});async function I(n){if(n.status===429)throw new l(L(n));if(!n.ok)throw new Error(`HTTP ${n.status}`);let[t]=await k(n);if(t.status===0)return t;let e=Number(/\d+/.exec(String(t.message??""))?.[0]);throw new l(e>=1&&e<=3600?e:30,!0)}async function St(n,t,e,r="never",i){let o=w(await y(e,"share",[])),s=await I(await n(t,{...A({v:2,adata:[rt(),"plaintext",1,0],ct:o,meta:{expire:r}}),signal:i}));if(typeof s.id!="string"||typeof s.deletetoken!="string")throw new Error("the answer had no paste id");return{id:s.id,deleteToken:s.deletetoken}}async function Pt(n,t,e,r){return n(t,A({pasteid:e,deletetoken:r}))}async function Tt(n,t,e,r){let i=await M(n,`${t}?${e}`,r);if(i.status===404)return null;if(!i.ok)throw new Error(`HTTP ${i.status}`);let[o,s]=await k(i);if(o.status!==0)return null;let d=o.meta?.time_to_live;return{j:o,size:s,ttl:typeof d=="number"&&d>0?d*1e3:null}}var C=class{constructor(t,e,r,i,o,s=()=>{}){this.a=t;this.l=e;this.f=r;this.signal=i;this.onGen=s;this.head=o||t.paste,this.trail.push(t.paste)}ids=new Set;expires=null;moved=!1;big=!1;head;trail=[];seen=new Map;async scan(t,e){let r;for(let i of Array.isArray(t)?t:[]){if(typeof i?.id!="string"||typeof i.ct!="string")continue;let o=this.seen.get(i.id);if(o===void 0){o=null;let s=null;try{i.ct.length<14e5&&(s=K(i.ct))}catch{}let d=s&&await R(this.l,"share",s),a=d?.next,p=typeof a=="string"?null:D(this.l,d);if(typeof a=="string"&&At.test(a))o=a;else if(p){e.push(p);for(let u of P(p.ops))this.ids.add(u)}this.seen.set(i.id,o)}r??=o??void 0}return r}async read(){let t=[],e=this.head,r=!1,i=!1;for(let o=0;;){let s=await Tt(this.f,this.a.base,e,this.signal);if(!s){if(i)break;if(!r&&e!==this.a.paste){r=!0,e=this.a.paste;continue}throw new f("gone")}i=!0,e!==this.head&&(this.head=e,this.trail.push(e),this.ids.clear(),this.moved=!0,this.onGen(e));let d=s.j.comments,a=await this.scan(d,t);this.expires=s.ttl&&Date.now()+s.ttl;let p=(Array.isArray(d)?d[0]?.ct?.length:0)??0;if(this.big=!a&&s.size>vt&&s.size>2*p,!a||++o>=xt)break;e=a}return t}ask(t,e){return[this.a.base,A(kt(t,e))]}async req(t){return this.ask(this.head,await y(this.l,"share",t))}go(t,e){return this.f(t,{...e,signal:this.signal})}async post(t){for(let e of U(t))await I(await this.go(...await this.req(e)))}async roll(t){let e=async s=>{for(let d=0;;d++)try{return await s()}catch(a){if(!(a instanceof l)||d>5||this.signal?.aborted)throw a;await S(a.seconds*1e3*(1+Math.random()*.2),this.signal)}},r=this.head,i=await e(()=>St(this.f,this.a.base,this.l,"never",this.signal));for(let s of U(t))await e(async()=>I(await this.go(...this.ask(i.id,await y(this.l,"share",s)))));await e(async()=>I(await this.go(...this.ask(r,await _(this.l,"share",{next:i.id})))));let o=await this.read();return this.head!==i.id&&await Pt(this.f,this.a.base,i.id,i.deleteToken).catch(()=>{}),o}};function Ot(n,t,e,r=Math.random()){let i=t?3e5:n<6e5?25e3+r*1e4:n<18e5?12e4:3e5;return Math.min(3e5,i*2**e)}var z=class{constructor(t,e){this.b=t;this.o=e}id="share";problem=null;offline=!1;newer=0;deliver=()=>{};active=Date.now();errors=0;ok=!1;timer;busy=null;again=!1;limit=0;rolling=!1;ready=null;stopped=!1;off=[];async start(t){this.deliver=t;let e=(i,o,s)=>{i&&(i.addEventListener(o,s),this.off.push(()=>i.removeEventListener(o,s)))},r=globalThis;r.addEventListener&&(e(globalThis,"focus",()=>void this.poll()),e(globalThis,"online",()=>void this.poll()),e(globalThis,"pagehide",()=>this.flush()),e(r.document,"visibilitychange",()=>r.document?.visibilityState==="visible"&&void this.poll())),await this.poll()}stop(){this.stopped=!0,clearTimeout(this.timer);for(let t of this.off.splice(0))t()}async remoteIds(){return this.ok?this.b.ids:null}poll(){if(this.busy)return this.again=!0,this.busy;let t=async()=>{do this.again=!1,await this.read();while(this.again&&!this.stopped)};return this.busy=t().finally(()=>this.busy=null)}bump(){this.active=Date.now()}async read(){clearTimeout(this.timer);try{let t=await this.b.read();this.offline=!1,this.errors=0,this.ok=!0,this.newer+=t.reduce((r,i)=>r+i.newer,0);let e=t.flatMap(r=>r.ops);if(e.length&&(this.active=Date.now(),this.deliver(e)),this.b.moved){this.b.moved=!1;let r=this.o.allowed().filter(i=>!this.b.ids.has(i.body.id));r.length&&await this.b.post(r).catch(()=>{})}this.b.big&&!this.rolling&&this.roll()}catch(t){t instanceof f?this.problem=t.kind:(this.offline=!0,this.errors++)}if(this.o.changed(),!this.stopped&&this.problem!=="gone"){let t=globalThis.document?.visibilityState==="hidden";this.timer=setTimeout(()=>void this.poll(),Ot(Date.now()-this.active,t,this.errors))}}lock(t,e=0){let r=this.o.lock,i=globalThis.navigator?.locks;return r?i?new Promise((o,s)=>{i.request(r,{signal:this.o.signal},async()=>{try{o(await t())}catch(d){s(d)}await S(e,this.o.signal)}).catch(s)}):S(Math.random()*5e3,this.o.signal).then(t):t()}get gap(){return Math.max(this.o.gap,this.limit)}async guarded(t){try{await this.lock(t,this.gap),this.offline=!1}catch(e){throw e instanceof l?(e.limit&&(this.limit=Math.max(this.limit,e.seconds*1e3)),e):e instanceof f?(this.problem=e.kind,this.o.changed(),new l(3600)):(this.offline=!0,this.o.changed(),e)}}async send(t){if(this.problem)throw new l(3600);this.bump(),await this.guarded(async()=>{await this.poll();let e=t.filter(r=>!this.b.ids.has(r.body.id));if(e.length){await this.b.post(e);for(let r of e)this.b.ids.add(r.body.id)}})}async prepare(t){try{this.ready=t.length&&JSON.stringify(t).length<4e4?await this.b.req(t):null}catch{this.ready=null}}flush(){let t=this.ready;this.ready=null,!(!t||this.problem||typeof t[1].body!="string"||t[1].body.length>64e3)&&(this.o.fetch??fetch)(t[0],{...t[1],keepalive:!0}).catch(()=>{})}async roll(){this.rolling=!0;try{await S(Math.random()*3e4,this.o.signal),await this.guarded(async()=>{if(await this.poll(),!this.b.big||!this.b.roll)return;let e=(await this.b.roll(this.o.allowed())).flatMap(r=>r.ops);e.length&&this.deliver(e)})}catch{}finally{this.rolling=!1}}};async function De(n,t){let e=tt(t.share,t.local);if(e.kind==="off")throw new Error(e.reason);let r=H(n.id,e.key),i=t.fetch??((h,T)=>fetch(h,T)),o=e.kind==="mailbox"?new $(e,r,i,t.signal):new C(e,r,i,t.signal),s=new Set,d=t.sendable??(h=>s.has(h.body.id)||h.body.author===n.me),a=t.signal??new AbortController().signal,p=new z(o,{signal:a,gap:t.gap??1e4,lock:!1,allowed:()=>n.ops().filter(d),changed(){}}),u=new O({id:"share",document:n,merge:async h=>{let T=await n.merge(h,"share"),N=new Set(n.ops().map(b=>b.body.id));for(let b of P(h))N.has(b)&&s.add(b);return T},transport:p,sendable:d,signal:a,delay:t.delay??0,gap:()=>p.gap});return await u.start(),{problem:()=>p.problem,waiting:()=>u.waiting,stop:()=>u.stop(),flush:async(h=12e4)=>(await p.poll(),new Promise((T,N)=>{let b=Date.now(),it=setInterval(j,250),ot=u.onState(j);function q(X){clearInterval(it),ot(),X?N(X):T()}function j(){if(p.problem)return q(new Error(`sharing stopped: the shared copy is ${p.problem==="gone"?"gone":p.problem==="full"?"full":"refusing this page"}`));if(!u.waiting&&u.state!=="sending")return q();Date.now()-b>h&&q(new Error("timed out before everything was sent"))}u.rescan(),j()}))}}export{De as connectShare};
|
package/dist/share.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";(()=>{function W(n){let t=globalThis;(t.pipeupAddons||=[]).push(n),typeof addEventListener=="function"&&addEventListener("load",()=>{t.Pipeup||console.error(`pipeup: the "${n.id}" add-on needs Pipeup on the page too; load pipeup.min.js.`)},{once:!0})}var S=(n,t)=>t?t.getAttribute(n):globalThis.Pipeup?.setting?.(n)??globalThis.document?.documentElement.getAttribute(n)??null;var m=n=>new TextEncoder().encode(n),V=n=>new TextDecoder().decode(n);function k(n){let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function w(n){let t=n.replace(/-/g,"+").replace(/_/g,"/"),e=atob(t+"=".repeat((4-t.length%4)%4));return Uint8Array.from(e,r=>r.charCodeAt(0))}function P(n){let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t)}function Q(n){return Uint8Array.from(atob(n),t=>t.charCodeAt(0))}var T=n=>crypto.getRandomValues(new Uint8Array(n));async function D(n){return new Uint8Array(await crypto.subtle.digest("SHA-256",n))}var Oe=/^([A-Za-z0-9_-]{16}):([A-Za-z0-9_-]{43})$/;function ee(n,t=document){let e=Oe.exec((t===globalThis.document?S("data-pipeup-doc"):t.documentElement.getAttribute("data-pipeup-doc"))?.trim()??"");return e&&e[1]===n?w(e[2]):null}var Be=n=>m(`pipeup/v1/doc:${n}`);async function he(n,t,e,r){let i=await crypto.subtle.importKey("raw",n,"HKDF",!1,["deriveBits"]);return crypto.subtle.deriveBits({name:"HKDF",hash:"SHA-256",salt:Be(t),info:m(e)},i,r)}function te(n,t){return{docId:n,key:async e=>crypto.subtle.importKey("raw",await he(t,n,`pipeup/v1/${e}/key`,256),"AES-GCM",!1,["encrypt","decrypt"]),addr:async e=>k(new Uint8Array(await he(t,n,`pipeup/v1/${e}/address`,128))),aad:e=>`pipeup/v1/${e}:${n}`}}var Ee=1e6,Ue=1e3;async function ue(n,t,e=1/0){let r=new Blob([n]).stream().pipeThrough(t).getReader(),i=[],o=0;for(;;){let{done:d,value:l}=await r.read();if(d)break;if(o+=l.length,o>e)throw r.cancel(),new Error("too large");i.push(l)}let s=new Uint8Array(o),a=0;for(let d of i)s.set(d,a),a+=d.length;return s}var A=(n,t,e)=>L(n,t,{pipeup:1,doc:n.docId,sealed:!1,ops:e});async function L(n,t,e){let r=m(JSON.stringify(e)),i=T(12),o=await crypto.subtle.encrypt({name:"AES-GCM",iv:i,additionalData:m(n.aad(t))},await n.key(t),await ue(r,new CompressionStream("deflate-raw"))),s=new Uint8Array(13+o.byteLength);return s[0]=1,s.set(i,1),s.set(new Uint8Array(o),13),s}async function C(n,t,e){try{if(e.length<29||e[0]!==1)return null;let r=await crypto.subtle.decrypt({name:"AES-GCM",iv:e.slice(1,13),additionalData:m(n.aad(t))},await n.key(t),e.slice(13));return JSON.parse(V(await ue(new Uint8Array(r),new DecompressionStream("deflate-raw"),Ee)))}catch{return null}}function N(n,t){let e=t;if(e?.pipeup!==1||e.doc!==n.docId||!Array.isArray(e.ops)||e.ops.length>Ue)return null;let r=0;for(let i of e.ops){let o=i?.body?.v;typeof o=="number"&&o!==1&&r++}return{ops:e.ops,newer:r}}async function ne(n,t,e){return N(n,await C(n,t,e))}var re=n=>`pu1.${k(n)}`,ie=n=>{if(!/^pu1\.[A-Za-z0-9_-]+$/.test(n))return null;try{return w(n.slice(4))}catch{return null}};async function oe(n,t={}){let e=new Map,r={get:async i=>e.get(i),set:async(i,o)=>void e.set(i,o),lasting:!1};if(t.memory||typeof indexedDB>"u")return r;try{let i=await new Promise((s,a)=>{let d=setTimeout(()=>a(new Error("timed out")),4e3),l=indexedDB.open(`pipeup-addon-${n}`,1);l.onupgradeneeded=()=>l.result.createObjectStore("kv"),l.onsuccess=()=>{clearTimeout(d),s(l.result)},l.onerror=l.onblocked=()=>{clearTimeout(d),a(l.error??new Error("blocked"))}}),o=(s,a)=>new Promise((d,l)=>{let f=a(i.transaction("kv",s).objectStore("kv"));f.onsuccess=()=>d(f.result),f.onerror=()=>l(f.error)});return{get:s=>o("readonly",a=>a.get(s)),set:async(s,a)=>void await o("readwrite",d=>d.put(a,s)),lasting:!0}}catch{return r}}var u=class extends Error{constructor(e,r=!1){super(`try again in ${e} s`);this.seconds=e;this.limit=r;this.name="RetryAfter"}};var _e=100,fe=5e3,Me=5e3,Re=3e5,$e=3600,M=class{constructor(t){this.o=t}state="idle";lastOk=0;known=new Set;outbox=new Map;rejected=new Set;inbound=[];inboundTimer=0;sendTimer=0;lastSend=0;backoff=0;off=null;listeners=new Set;stopped=!1;get waiting(){return this.outbox.size}pending(){return[...this.outbox.values()]}onState(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(t){t&&(this.state=t);for(let e of[...this.listeners])e()}async start(){let{document:t,transport:e,signal:r}=this.o;this.off=t.onChange((o,s,a)=>{if(a===this.o.id)return;let d=!1;for(let l of s)this.want(l)&&(d=this.outbox.set(l.body.id,l).size>0||d);d&&this.schedule(),this.emit()}),r.addEventListener("abort",()=>this.stop(),{once:!0}),await e.start(o=>this.deliver(o));let i=await e.remoteIds?.();if(i)for(let o of i)this.known.add(o);this.rescan()}stop(){this.stopped||(this.stopped=!0,this.off?.(),clearTimeout(this.inboundTimer),clearTimeout(this.sendTimer),this.o.transport.stop())}want(t){return!this.known.has(t.body.id)&&!this.rejected.has(t.body.id)&&this.o.sendable(t)}rescan(){this.outbox.clear();for(let t of this.o.document.ops())this.want(t)&&this.outbox.set(t.body.id,t);this.schedule(),this.emit()}deliver(t){this.inbound.push(...t),this.inboundTimer||(this.inboundTimer=setTimeout(()=>void this.drain(),_e))}async drain(){this.inboundTimer=0;let t=this.inbound.splice(0),e=t.map(i=>i?.body?.id).filter(i=>typeof i=="string");for(let i=0;i<t.length;i+=fe)try{await this.o.merge(t.slice(i,i+fe))}catch(o){o?.name!=="UnsavedChangeError"&&globalThis.reportError?.(o)}let r=new Set(this.o.document.ops().map(i=>i.body.id));for(let i of e)r.has(i)?(this.known.add(i),this.outbox.delete(i)):this.rejected.add(i);this.lastOk=Date.now(),this.emit()}schedule(t=this.o.delay??0){if(this.stopped||this.sendTimer||this.outbox.size===0)return;let e=typeof this.o.gap=="function"?this.o.gap():this.o.gap??0,r=Math.max(t,this.lastSend+e-Date.now(),this.backoff);this.sendTimer=setTimeout(()=>void this.send(),r)}async send(){if(this.sendTimer=0,this.stopped||this.o.signal.aborted||this.outbox.size===0)return;let t=[...this.outbox.values()].slice(0,this.o.batch??500);this.emit("sending");try{await this.o.transport.send(t);for(let e of t)this.known.add(e.body.id),this.outbox.delete(e.body.id);this.lastSend=Date.now(),this.lastOk=this.lastSend,this.backoff=0,this.emit("idle")}catch(e){e instanceof u?(this.backoff=Math.min(e.seconds,$e)*1e3*(1+Math.random()*.2),this.emit("limited")):(this.backoff=Math.min(Re,this.backoff?this.backoff*2:Me),this.emit("offline"))}this.schedule()}};var yt=2n**256n-2n**32n-977n;var De="sharing isn't set up for this page",R="data-pipeup-share isn't a sharing address",Le=/^(localhost|127\.0\.0\.1|\[::1\])$/,me=/^[A-Za-z0-9_-]{43}$/,O=n=>({kind:"off",reason:n});function ge(n,t=!1){let e=n?.trim();if(!e)return O(De);let r;try{r=new URL(e)}catch{return O(R)}if(r.protocol!=="https:"&&!(t&&r.protocol==="http:"&&Le.test(r.hostname)))return O(R);let i=r.origin+r.pathname,o=r.hash.slice(1);if(o.startsWith("pm1.")){let[a,d,...l]=o.slice(4).split(".");return!me.test(a??"")||l.length||d!==void 0&&!/^[A-Za-z0-9_-]+$/.test(d)||r.search||!/\/m\/[A-Za-z0-9_-]{22,64}$/.test(i)?O(R):{kind:"mailbox",mailbox:i,key:w(a),...d?{token:d}:{}}}let s=/^\?([A-Za-z0-9_-]{8,64})$/.exec(r.search)?.[1];return!s||!me.test(o)?O(R):{kind:"privatebin",base:i,paste:s,key:w(o)}}var b=class extends Error{constructor(e){super(e);this.kind=e;this.name="Problem"}},ye={Accept:"application/json"},z=(n,t,e)=>n(t,{headers:ye,credentials:"omit",signal:e}),B=n=>({method:"POST",headers:ye,credentials:"omit",body:JSON.stringify(n)}),be=n=>{let t={403:"closed",404:"gone",507:"full"}[n];return t&&new b(t)};async function E(n){let t=await n.text(),e=t.length<1e7&&JSON.parse(t);if(!e||typeof e!="object")throw new Error("the answer was not usable");return[e,t.length]}function $(n,t=9e5){let e=[],r=0;for(let i of n)r+=JSON.stringify(i).length,(!e.length||r>t||e.at(-1).length>=500)&&(e.push([]),r=JSON.stringify(i).length),e.at(-1).push(i);return e}var U=(n,t)=>new Promise(e=>{let r=setTimeout(e,n);t?.addEventListener("abort",()=>{clearTimeout(r),e()},{once:!0})}),H=(n,t=10)=>{let e=Number(n.headers.get("Retry-After"));return e>0?Math.min(e,3600):t},_=n=>n.map(t=>t?.body?.id).filter(t=>typeof t=="string");var Ce=524288,Ne=1e3,ze=async n=>k(await D(m(n))).slice(0,22),j=class{constructor(t,e,r,i){this.a=t;this.l=e;this.f=r;this.signal=i}ids=new Set;expires=null;moved=!1;big=!1;cursor="";seen=new Set;memo=null;async read(){let t=[],e=0;for(let r=0;r<Ne;){let i=`${this.a.mailbox}/ops${this.cursor?`?since=${encodeURIComponent(this.cursor)}`:""}`,o=await z(this.f,i,this.signal);if(o.status===404)throw new b("gone");if(o.status===409&&e++<2){this.cursor="";continue}if(!o.ok)throw new Error(`HTTP ${o.status}`);r++;let[s]=await E(o);for(let a of Array.isArray(s.batches)?s.batches:[]){if(typeof a?.id!="string"||typeof a.ct!="string"||this.seen.has(a.id))continue;this.seen.add(a.id);let d=ie(a.ct),l=d&&await ne(this.l,"share",d);if(l){t.push(l);for(let f of _(l.ops))this.ids.add(f)}}if(typeof s.next=="string"&&(this.cursor=s.next),this.expires=typeof s.expires=="string"&&Date.parse(s.expires)||null,s.more!==!0)break}return t}async req(t){let e=t.map(o=>o.body.id).join();if(this.memo?.key!==e){let o=re(await A(this.l,"share",t));this.memo={key:e,ct:o,id:await ze(o)}}let{ct:r,id:i}=this.memo;return[`${this.a.mailbox}/ops`,B({id:i,ct:r,token:this.a.token})]}async post(t){for(let e of $(t))await this.one(e)}async one(t){let[e,r]=await this.req(t),i=async()=>{let d=t.length>>1;await this.one(t.slice(0,d)),await this.one(t.slice(d))};if(r.body.length>Ce&&t.length>1)return i();let o=await this.f(e,{...r,signal:this.signal}),s=o.status;if(s===200||s===201||s===400)return;if(s===413&&t.length>1)return i();let a=be(s);if(a)throw a;if(s===429){let[d]=await E(o).catch(()=>[{}]),l=Number(d.retryAfter);throw new u(o.headers.get("Retry-After")?H(o):l>0?Math.min(l,3600):10)}throw new Error(`HTTP ${s}`)}};var He=new Set(["reply","addressed","react","unreact"]),we=()=>(S("data-pipeup-agents")??"").split(/[\s,]+/).filter(Boolean),q=class{constructor(t,e,r=()=>[]){this.me=t;this.agents=r;this.held=new Set(e?.held),this.kept=new Set(e?.kept),this.recv=new Set(e?.recv),this.chosen=new Set(e?.chosen),this.follow=new Set(e?.follow),this.legacy=new Set(e?.legacy??(e?.init&&e.chosen===void 0?e.kept:void 0)),this.send=e?.send??!0,this.noted=e?.noted??!1,this.gen=e?.gen??null,this.init=e?.init??!1}held;kept;recv;chosen;follow;legacy;send;noted;gen;init;saved(){let{init:t,held:e,kept:r,recv:i,chosen:o,follow:s,legacy:a,send:d,noted:l,gen:f}=this;return{init:t,held:e,kept:r,recv:i,chosen:o,follow:s,legacy:a,send:d,noted:l,gen:f}}hold(t){if(!this.init){this.init=!0;for(let{body:e}of t)e.author===this.me&&this.held.add(e.id)}}sendable(t){let e=t.body;if(this.recv.has(e.id))return!0;if(He.has(e.kind)&&this.agents().includes(e.author))return this.send&&![e.target,e.thread].some(i=>i&&(this.held.has(i)||this.kept.has(i)));if(e.author!==this.me||this.held.has(e.id)||this.kept.has(e.id))return!1;let r=[e.target,e.thread].find(i=>i&&(this.held.has(i)||this.kept.has(i)));if(r)(this.held.has(r)?this.held:this.kept).add(e.id),this.follow.add(e.id);else if(!this.send)this.kept.add(e.id);else return(e.kind==="create"||e.kind==="reply")&&(this.noted=!0),!0;return!1}local(t){let e=t.body;return e.author!==this.me||this.recv.has(e.id)||!(this.held.has(e.id)||this.kept.has(e.id))?null:this.follow.has(e.id)?"thread":this.held.has(e.id)?"held":this.chosen.has(e.id)?"chosen":this.legacy.has(e.id)?"unknown":"kept"}decide(t){if(!t)for(let e of this.held)this.kept.add(e),this.chosen.add(e);this.held.clear()}};var je=256e3,qe=16,Fe=/^[A-Za-z0-9_-]{8,64}$/,ve=()=>[P(T(16)),P(T(8)),1e5,256,128,"aes","gcm","none"],Ke=(n,t)=>({v:2,adata:ve(),ct:P(t),pasteid:n,parentid:n});async function F(n){if(n.status===429)throw new u(H(n));if(!n.ok)throw new Error(`HTTP ${n.status}`);let[t]=await E(n);if(t.status===0)return t;let e=Number(/\d+/.exec(String(t.message??""))?.[0]);throw new u(e>=1&&e<=3600?e:30,!0)}async function Ge(n,t,e,r="never",i){let o=P(await A(e,"share",[])),s=await F(await n(t,{...B({v:2,adata:[ve(),"plaintext",1,0],ct:o,meta:{expire:r}}),signal:i}));if(typeof s.id!="string"||typeof s.deletetoken!="string")throw new Error("the answer had no paste id");return{id:s.id,deleteToken:s.deletetoken}}async function Je(n,t,e,r){return n(t,B({pasteid:e,deletetoken:r}))}async function Ye(n,t,e,r){let i=await z(n,`${t}?${e}`,r);if(i.status===404)return null;if(!i.ok)throw new Error(`HTTP ${i.status}`);let[o,s]=await E(i);if(o.status!==0)return null;let a=o.meta?.time_to_live;return{j:o,size:s,ttl:typeof a=="number"&&a>0?a*1e3:null}}var K=class{constructor(t,e,r,i,o,s=()=>{}){this.a=t;this.l=e;this.f=r;this.signal=i;this.onGen=s;this.head=o||t.paste,this.trail.push(t.paste)}ids=new Set;expires=null;moved=!1;big=!1;head;trail=[];seen=new Map;async scan(t,e){let r;for(let i of Array.isArray(t)?t:[]){if(typeof i?.id!="string"||typeof i.ct!="string")continue;let o=this.seen.get(i.id);if(o===void 0){o=null;let s=null;try{i.ct.length<14e5&&(s=Q(i.ct))}catch{}let a=s&&await C(this.l,"share",s),d=a?.next,l=typeof d=="string"?null:N(this.l,a);if(typeof d=="string"&&Fe.test(d))o=d;else if(l){e.push(l);for(let f of _(l.ops))this.ids.add(f)}this.seen.set(i.id,o)}r??=o??void 0}return r}async read(){let t=[],e=this.head,r=!1,i=!1;for(let o=0;;){let s=await Ye(this.f,this.a.base,e,this.signal);if(!s){if(i)break;if(!r&&e!==this.a.paste){r=!0,e=this.a.paste;continue}throw new b("gone")}i=!0,e!==this.head&&(this.head=e,this.trail.push(e),this.ids.clear(),this.moved=!0,this.onGen(e));let a=s.j.comments,d=await this.scan(a,t);this.expires=s.ttl&&Date.now()+s.ttl;let l=(Array.isArray(a)?a[0]?.ct?.length:0)??0;if(this.big=!d&&s.size>je&&s.size>2*l,!d||++o>=qe)break;e=d}return t}ask(t,e){return[this.a.base,B(Ke(t,e))]}async req(t){return this.ask(this.head,await A(this.l,"share",t))}go(t,e){return this.f(t,{...e,signal:this.signal})}async post(t){for(let e of $(t))await F(await this.go(...await this.req(e)))}async roll(t){let e=async s=>{for(let a=0;;a++)try{return await s()}catch(d){if(!(d instanceof u)||a>5||this.signal?.aborted)throw d;await U(d.seconds*1e3*(1+Math.random()*.2),this.signal)}},r=this.head,i=await e(()=>Ge(this.f,this.a.base,this.l,"never",this.signal));for(let s of $(t))await e(async()=>F(await this.go(...this.ask(i.id,await A(this.l,"share",s)))));await e(async()=>F(await this.go(...this.ask(r,await L(this.l,"share",{next:i.id})))));let o=await this.read();return this.head!==i.id&&await Je(this.f,this.a.base,i.id,i.deleteToken).catch(()=>{}),o}};function Ze(n,t,e,r=Math.random()){let i=t?3e5:n<6e5?25e3+r*1e4:n<18e5?12e4:3e5;return Math.min(3e5,i*2**e)}var G=class{constructor(t,e){this.b=t;this.o=e}id="share";problem=null;offline=!1;newer=0;deliver=()=>{};active=Date.now();errors=0;ok=!1;timer;busy=null;again=!1;limit=0;rolling=!1;ready=null;stopped=!1;off=[];async start(t){this.deliver=t;let e=(i,o,s)=>{i&&(i.addEventListener(o,s),this.off.push(()=>i.removeEventListener(o,s)))},r=globalThis;r.addEventListener&&(e(globalThis,"focus",()=>void this.poll()),e(globalThis,"online",()=>void this.poll()),e(globalThis,"pagehide",()=>this.flush()),e(r.document,"visibilitychange",()=>r.document?.visibilityState==="visible"&&void this.poll())),await this.poll()}stop(){this.stopped=!0,clearTimeout(this.timer);for(let t of this.off.splice(0))t()}async remoteIds(){return this.ok?this.b.ids:null}poll(){if(this.busy)return this.again=!0,this.busy;let t=async()=>{do this.again=!1,await this.read();while(this.again&&!this.stopped)};return this.busy=t().finally(()=>this.busy=null)}bump(){this.active=Date.now()}async read(){clearTimeout(this.timer);try{let t=await this.b.read();this.offline=!1,this.errors=0,this.ok=!0,this.newer+=t.reduce((r,i)=>r+i.newer,0);let e=t.flatMap(r=>r.ops);if(e.length&&(this.active=Date.now(),this.deliver(e)),this.b.moved){this.b.moved=!1;let r=this.o.allowed().filter(i=>!this.b.ids.has(i.body.id));r.length&&await this.b.post(r).catch(()=>{})}this.b.big&&!this.rolling&&this.roll()}catch(t){t instanceof b?this.problem=t.kind:(this.offline=!0,this.errors++)}if(this.o.changed(),!this.stopped&&this.problem!=="gone"){let t=globalThis.document?.visibilityState==="hidden";this.timer=setTimeout(()=>void this.poll(),Ze(Date.now()-this.active,t,this.errors))}}lock(t,e=0){let r=this.o.lock,i=globalThis.navigator?.locks;return r?i?new Promise((o,s)=>{i.request(r,{signal:this.o.signal},async()=>{try{o(await t())}catch(a){s(a)}await U(e,this.o.signal)}).catch(s)}):U(Math.random()*5e3,this.o.signal).then(t):t()}get gap(){return Math.max(this.o.gap,this.limit)}async guarded(t){try{await this.lock(t,this.gap),this.offline=!1}catch(e){throw e instanceof u?(e.limit&&(this.limit=Math.max(this.limit,e.seconds*1e3)),e):e instanceof b?(this.problem=e.kind,this.o.changed(),new u(3600)):(this.offline=!0,this.o.changed(),e)}}async send(t){if(this.problem)throw new u(3600);this.bump(),await this.guarded(async()=>{await this.poll();let e=t.filter(r=>!this.b.ids.has(r.body.id));if(e.length){await this.b.post(e);for(let r of e)this.b.ids.add(r.body.id)}})}async prepare(t){try{this.ready=t.length&&JSON.stringify(t).length<4e4?await this.b.req(t):null}catch{this.ready=null}}flush(){let t=this.ready;this.ready=null,!(!t||this.problem||typeof t[1].body!="string"||t[1].body.length>64e3)&&(this.o.fetch??fetch)(t[0],{...t[1],keepalive:!0}).catch(()=>{})}async roll(){this.rolling=!0;try{await U(Math.random()*3e4,this.o.signal),await this.guarded(async()=>{if(await this.poll(),!this.b.big||!this.b.roll)return;let e=(await this.b.roll(this.o.allowed())).flatMap(r=>r.ops);e.length&&this.deliver(e)})}catch{}finally{this.rolling=!1}}};var Xe="Comments here are shared with everyone who has this page.",We=7*864e5,ke=["M12 16V4M7 9l5-5 5 5M5 20h14"],Ve=["M4 7a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3z","M4 9.5h16M7.5 6.8h.01"],Qe="Only in this browser",et={held:"Written before this page was shared. Share it or keep it from the menu.",kept:"Written while Send my comments was off. It stays here.",chosen:"You chose to keep it here.",thread:"Part of a comment that stays in this browser.",unknown:"It stays in this browser and isn't sent to the shared copy."};function Se(n={}){return{id:"share",api:1,version:"0.4.1",needs:["sync","menu","notify","note","commentmark"],network:{when:"page-configured",to:["page"],says:"Sends the comments you choose to share, sealed so only people with this page can read them, to the sharing service the page names. That service sees when and how much is sent, and your network address."},async setup(t){let e=t.document,r=ge(n.share??S("data-pipeup-share"),!0);if(r.kind==="off")return t.off(r.reason);if(!ee(e.id))return t.off("needs a document identity");let i=te(e.id,r.key),o=await oe("share",{memory:t.ephemeral}),s=await o.get(`doc:${e.id}`),a=new q(e.me,s,we);a.hold(e.ops());let d=Promise.resolve(),l=()=>{d=d.then(()=>o.set(`doc:${e.id}`,a.saved())).catch(()=>{})};l();let f={gone:"The shared copy is gone",closed:"This page can't send to the shared copy; ask the author for the current file",full:"The shared copy is full; the author can start it again"},se=new Set,ae=(c,p)=>se.has(c)||(se.add(c),t.notify(p)),J=new Set,de=()=>{let c=new Set;for(let p of e.ops()){if(p.body.kind!=="create"&&p.body.kind!=="reply")continue;let g=h.problem==="gone"?null:a.local(p);g&&(c.add(p.body.id),t.setCommentMark(p.body.id,{text:Qe,tip:et[g]}))}for(let p of J)c.has(p)||t.setCommentMark(p,null);J.clear();for(let p of c)J.add(p)},x=()=>{de();for(let c of I)c.update();t.setStatus({text:pe()}),t.setComposerNote(a.send&&!a.noted&&h.problem!=="gone"?Xe:null),h.newer&&ae("newer",`${h.newer} comments come from a newer version of Pipeup; this page can't show them yet.`),h.problem&&ae(h.problem,f[h.problem]+(h.problem==="gone"?". Your comments stay in this browser.":"."))},le=(c,p)=>fetch(c,p),ce=r.kind==="mailbox"?new j(r,i,le,t.signal):new K(r,i,le,t.signal,a.gen,c=>{a.gen=c,l()}),h=new G(ce,{signal:t.signal,gap:1e4,lock:`pipeup-share:${e.id}`,allowed:()=>e.ops().filter(c=>a.sendable(c)),changed:x}),v=new M({id:"share",document:e,merge:async c=>{let p=await t.merge(c),g=new Set(e.ops().map(y=>y.body.id));for(let y of _(c))g.has(y)&&a.recv.add(y);return l(),p},transport:h,sendable:c=>{let p=a.sendable(c);return a.noted!==s?.noted&&l(),p},signal:t.signal,delay:2e3,gap:()=>h.gap}),Y;v.onState(()=>{x(),clearTimeout(Y),Y=setTimeout(()=>void h.prepare(v.pending()),1e3)});let Ae=new URL(r.kind==="mailbox"?r.mailbox:r.base).host,pe=()=>{let c=v.waiting;return h.problem?f[h.problem]:a.send?h.offline||v.state==="offline"?"Shared \xB7 offline, sends when back":c?`Shared \xB7 ${c} change${c===1?"":"s"} waiting`:"Shared \xB7 up to date":"Shared \xB7 not sending your comments"},xe=()=>{let c=ce.expires;return h.problem==="gone"?"Your comments stay in this browser. The author can start sharing again.":[`Your comments are encrypted and sent to ${Ae} so others with this page see them. The service can't read them. People who already have your comments, by file or live, could pass them on.`,t.ephemeral&&"This browser can't keep comments; the shared copy keeps the ones you send.",!o.lasting&&"This browser won't remember this choice.",c&&c-Date.now()<We&&`The shared copy expires on ${new Date(c).toLocaleDateString("en-GB",{day:"numeric",month:"long"})}.`].filter(Boolean).join(" ")},I=[],Z=(c,p,g,y,Pe,Te)=>I[I.push(t.addMenuItem({id:c,icon:p,label:g,hint:y,select:Pe,checked:Te}))-1],X=e.ops().filter(c=>a.held.has(c.body.id)&&(c.body.kind==="create"||c.body.kind==="reply")).length;if(Z("send",ke,()=>a.send||h.problem?pe():"Send my comments",xe,()=>{a.send=!a.send,l(),v.rescan(),x()},()=>a.send),X){let c=o.lasting?"":" This browser won't remember this choice.",p=g=>()=>{a.decide(g),l();for(let y of I.splice(1))y.remove();v.rescan(),x()};Z("share-earlier",ke,()=>`Share my ${X} earlier comment${X===1?"":"s"}`,()=>`Send the comments you wrote before this page was shared.${c}`,p(!0)),Z("keep-earlier",Ve,()=>"Keep them in this browser",()=>`Never send the comments you wrote before this page was shared.${c}`,p(!1))}return x(),v.start().then(x,c=>globalThis.reportError?.(c)),e.onChange((c,p,g)=>{if(g==="local"&&p.length){for(let y of p)a.sendable(y);h.bump(),de(),l()}}),()=>{clearTimeout(Y),v.stop()}}}}W(Se());})();
|
|
1
|
+
"use strict";(()=>{function V(n){let t=globalThis;(t.pipeupAddons||=[]).push(n),typeof addEventListener=="function"&&addEventListener("load",()=>{t.Pipeup||console.error(`pipeup: the "${n.id}" add-on needs Pipeup on the page too; load pipeup.min.js.`)},{once:!0})}var A=(n,t)=>t?t.getAttribute(n):globalThis.Pipeup?.setting?.(n)??globalThis.document?.documentElement.getAttribute(n)??null;var m=n=>new TextEncoder().encode(n),Q=n=>new TextDecoder().decode(n);function k(n){let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function w(n){let t=n.replace(/-/g,"+").replace(/_/g,"/"),e=atob(t+"=".repeat((4-t.length%4)%4));return Uint8Array.from(e,r=>r.charCodeAt(0))}function T(n){let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t)}function ee(n){return Uint8Array.from(atob(n),t=>t.charCodeAt(0))}var O=n=>crypto.getRandomValues(new Uint8Array(n));async function L(n){return new Uint8Array(await crypto.subtle.digest("SHA-256",n))}var Oe=/^([A-Za-z0-9_-]{16}):([A-Za-z0-9_-]{43})$/;function te(n,t=document){let e=Oe.exec((t===globalThis.document?A("data-pipeup-doc"):t.documentElement.getAttribute("data-pipeup-doc"))?.trim()??"");return e&&e[1]===n?w(e[2]):null}var Be=n=>m(`pipeup/v1/doc:${n}`);async function he(n,t,e,r){let i=await crypto.subtle.importKey("raw",n,"HKDF",!1,["deriveBits"]);return crypto.subtle.deriveBits({name:"HKDF",hash:"SHA-256",salt:Be(t),info:m(e)},i,r)}function ne(n,t){return{docId:n,key:async e=>crypto.subtle.importKey("raw",await he(t,n,`pipeup/v1/${e}/key`,256),"AES-GCM",!1,["encrypt","decrypt"]),addr:async e=>k(new Uint8Array(await he(t,n,`pipeup/v1/${e}/address`,128))),aad:e=>`pipeup/v1/${e}:${n}`}}var Ee=1e6,Ue=1e3;async function ue(n,t,e=1/0){let r=new Blob([n]).stream().pipeThrough(t).getReader(),i=[],o=0;for(;;){let{done:d,value:c}=await r.read();if(d)break;if(o+=c.length,o>e)throw r.cancel(),new Error("too large");i.push(c)}let s=new Uint8Array(o),a=0;for(let d of i)s.set(d,a),a+=d.length;return s}var x=(n,t,e)=>C(n,t,{pipeup:1,doc:n.docId,sealed:!1,ops:e});async function C(n,t,e){let r=m(JSON.stringify(e)),i=O(12),o=await crypto.subtle.encrypt({name:"AES-GCM",iv:i,additionalData:m(n.aad(t))},await n.key(t),await ue(r,new CompressionStream("deflate-raw"))),s=new Uint8Array(13+o.byteLength);return s[0]=1,s.set(i,1),s.set(new Uint8Array(o),13),s}async function N(n,t,e){try{if(e.length<29||e[0]!==1)return null;let r=await crypto.subtle.decrypt({name:"AES-GCM",iv:e.slice(1,13),additionalData:m(n.aad(t))},await n.key(t),e.slice(13));return JSON.parse(Q(await ue(new Uint8Array(r),new DecompressionStream("deflate-raw"),Ee)))}catch{return null}}function z(n,t){let e=t;if(e?.pipeup!==1||e.doc!==n.docId||!Array.isArray(e.ops)||e.ops.length>Ue)return null;let r=0;for(let i of e.ops){let o=i?.body?.v;typeof o=="number"&&o!==1&&r++}return{ops:e.ops,newer:r}}async function re(n,t,e){return z(n,await N(n,t,e))}var ie=n=>`pu1.${k(n)}`,oe=n=>{if(!/^pu1\.[A-Za-z0-9_-]+$/.test(n))return null;try{return w(n.slice(4))}catch{return null}};async function se(n,t={}){let e=t.keep;if(e)return{get:async o=>await e.get(o),set:(o,s)=>e.set(o,s),lasting:!0};let r=new Map,i={get:async o=>r.get(o),set:async(o,s)=>void r.set(o,s),lasting:!1};if(t.memory||typeof indexedDB>"u")return i;try{let o=await new Promise((a,d)=>{let c=setTimeout(()=>d(new Error("timed out")),4e3),h=indexedDB.open(`pipeup-addon-${n}`,1);h.onupgradeneeded=()=>h.result.createObjectStore("kv"),h.onsuccess=()=>{clearTimeout(c),a(h.result)},h.onerror=h.onblocked=()=>{clearTimeout(c),d(h.error??new Error("blocked"))}}),s=(a,d)=>new Promise((c,h)=>{let S=d(o.transaction("kv",a).objectStore("kv"));S.onsuccess=()=>c(S.result),S.onerror=()=>h(S.error)});return{get:a=>s("readonly",d=>d.get(a)),set:async(a,d)=>void await s("readwrite",c=>c.put(d,a)),lasting:!0}}catch{return i}}var f=class extends Error{constructor(e,r=!1){super(`try again in ${e} s`);this.seconds=e;this.limit=r;this.name="RetryAfter"}};var _e=100,fe=5e3,Me=5e3,Re=3e5,$e=3600,R=class{constructor(t){this.o=t}state="idle";lastOk=0;known=new Set;outbox=new Map;rejected=new Set;inbound=[];inboundTimer=0;sendTimer=0;lastSend=0;backoff=0;off=null;listeners=new Set;stopped=!1;get waiting(){return this.outbox.size}pending(){return[...this.outbox.values()]}onState(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(t){t&&(this.state=t);for(let e of[...this.listeners])e()}async start(){let{document:t,transport:e,signal:r}=this.o;this.off=t.onChange((o,s,a)=>{if(a===this.o.id)return;let d=!1;for(let c of s)this.want(c)&&(d=this.outbox.set(c.body.id,c).size>0||d);d&&this.schedule(),this.emit()}),r.addEventListener("abort",()=>this.stop(),{once:!0}),await e.start(o=>this.deliver(o));let i=await e.remoteIds?.();if(i)for(let o of i)this.known.add(o);this.rescan()}stop(){this.stopped||(this.stopped=!0,this.off?.(),clearTimeout(this.inboundTimer),clearTimeout(this.sendTimer),this.o.transport.stop())}want(t){return!this.known.has(t.body.id)&&!this.rejected.has(t.body.id)&&this.o.sendable(t)}rescan(){this.outbox.clear();for(let t of this.o.document.ops())this.want(t)&&this.outbox.set(t.body.id,t);this.schedule(),this.emit()}deliver(t){this.inbound.push(...t),this.inboundTimer||(this.inboundTimer=setTimeout(()=>void this.drain(),_e))}async drain(){this.inboundTimer=0;let t=this.inbound.splice(0),e=t.map(i=>i?.body?.id).filter(i=>typeof i=="string");for(let i=0;i<t.length;i+=fe)try{await this.o.merge(t.slice(i,i+fe))}catch(o){o?.name!=="UnsavedChangeError"&&globalThis.reportError?.(o)}let r=new Set(this.o.document.ops().map(i=>i.body.id));for(let i of e)r.has(i)?(this.known.add(i),this.outbox.delete(i)):this.rejected.add(i);this.lastOk=Date.now(),this.emit()}schedule(t=this.o.delay??0){if(this.stopped||this.sendTimer||this.outbox.size===0)return;let e=typeof this.o.gap=="function"?this.o.gap():this.o.gap??0,r=Math.max(t,this.lastSend+e-Date.now(),this.backoff);this.sendTimer=setTimeout(()=>void this.send(),r)}async send(){if(this.sendTimer=0,this.stopped||this.o.signal.aborted||this.outbox.size===0)return;let t=[...this.outbox.values()].slice(0,this.o.batch??500);this.emit("sending");try{await this.o.transport.send(t);for(let e of t)this.known.add(e.body.id),this.outbox.delete(e.body.id);this.lastSend=Date.now(),this.lastOk=this.lastSend,this.backoff=0,this.emit("idle")}catch(e){e instanceof f?(this.backoff=Math.min(e.seconds,$e)*1e3*(1+Math.random()*.2),this.emit("limited")):(this.backoff=Math.min(Re,this.backoff?this.backoff*2:Me),this.emit("offline"))}this.schedule()}};var yt=2n**256n-2n**32n-977n;var De="sharing isn't set up for this page",$="data-pipeup-share isn't a sharing address",Le=/^(localhost|127\.0\.0\.1|\[::1\])$/,me=/^[A-Za-z0-9_-]{43}$/,B=n=>({kind:"off",reason:n});function ge(n,t=!1){let e=n?.trim();if(!e)return B(De);let r;try{r=new URL(e)}catch{return B($)}if(r.protocol!=="https:"&&!(t&&r.protocol==="http:"&&Le.test(r.hostname)))return B($);let i=r.origin+r.pathname,o=r.hash.slice(1);if(o.startsWith("pm1.")){let[a,d,...c]=o.slice(4).split(".");return!me.test(a??"")||c.length||d!==void 0&&!/^[A-Za-z0-9_-]+$/.test(d)||r.search||!/\/m\/[A-Za-z0-9_-]{22,64}$/.test(i)?B($):{kind:"mailbox",mailbox:i,key:w(a),...d?{token:d}:{}}}let s=/^\?([A-Za-z0-9_-]{8,64})$/.exec(r.search)?.[1];return!s||!me.test(o)?B($):{kind:"privatebin",base:i,paste:s,key:w(o)}}var b=class extends Error{constructor(e){super(e);this.kind=e;this.name="Problem"}},ye={Accept:"application/json"},H=(n,t,e)=>n(t,{headers:ye,credentials:"omit",signal:e}),E=n=>({method:"POST",headers:ye,credentials:"omit",body:JSON.stringify(n)}),be=n=>{let t={403:"closed",404:"gone",507:"full"}[n];return t&&new b(t)};async function U(n){let t=await n.text(),e=t.length<1e7&&JSON.parse(t);if(!e||typeof e!="object")throw new Error("the answer was not usable");return[e,t.length]}function I(n,t=9e5){let e=[],r=0;for(let i of n)r+=JSON.stringify(i).length,(!e.length||r>t||e.at(-1).length>=500)&&(e.push([]),r=JSON.stringify(i).length),e.at(-1).push(i);return e}var _=(n,t)=>new Promise(e=>{let r=setTimeout(e,n);t?.addEventListener("abort",()=>{clearTimeout(r),e()},{once:!0})}),j=(n,t=10)=>{let e=Number(n.headers.get("Retry-After"));return e>0?Math.min(e,3600):t},M=n=>n.map(t=>t?.body?.id).filter(t=>typeof t=="string");var Ce=524288,Ne=1e3,ze=async n=>k(await L(m(n))).slice(0,22),K=class{constructor(t,e,r,i){this.a=t;this.l=e;this.f=r;this.signal=i}ids=new Set;expires=null;moved=!1;big=!1;cursor="";seen=new Set;memo=null;async read(){let t=[],e=0;for(let r=0;r<Ne;){let i=`${this.a.mailbox}/ops${this.cursor?`?since=${encodeURIComponent(this.cursor)}`:""}`,o=await H(this.f,i,this.signal);if(o.status===404)throw new b("gone");if(o.status===409&&e++<2){this.cursor="";continue}if(!o.ok)throw new Error(`HTTP ${o.status}`);r++;let[s]=await U(o);for(let a of Array.isArray(s.batches)?s.batches:[]){if(typeof a?.id!="string"||typeof a.ct!="string"||this.seen.has(a.id))continue;this.seen.add(a.id);let d=oe(a.ct),c=d&&await re(this.l,"share",d);if(c){t.push(c);for(let h of M(c.ops))this.ids.add(h)}}if(typeof s.next=="string"&&(this.cursor=s.next),this.expires=typeof s.expires=="string"&&Date.parse(s.expires)||null,s.more!==!0)break}return t}async req(t){let e=t.map(o=>o.body.id).join();if(this.memo?.key!==e){let o=ie(await x(this.l,"share",t));this.memo={key:e,ct:o,id:await ze(o)}}let{ct:r,id:i}=this.memo;return[`${this.a.mailbox}/ops`,E({id:i,ct:r,token:this.a.token})]}async post(t){for(let e of I(t))await this.one(e)}async one(t){let[e,r]=await this.req(t),i=async()=>{let d=t.length>>1;await this.one(t.slice(0,d)),await this.one(t.slice(d))};if(r.body.length>Ce&&t.length>1)return i();let o=await this.f(e,{...r,signal:this.signal}),s=o.status;if(s===200||s===201||s===400)return;if(s===413&&t.length>1)return i();let a=be(s);if(a)throw a;if(s===429){let[d]=await U(o).catch(()=>[{}]),c=Number(d.retryAfter);throw new f(o.headers.get("Retry-After")?j(o):c>0?Math.min(c,3600):10)}throw new Error(`HTTP ${s}`)}};var He=new Set(["reply","addressed","react","unreact"]),we=()=>(A("data-pipeup-agents")??"").split(/[\s,]+/).filter(Boolean),q=class{constructor(t,e,r=()=>[]){this.me=t;this.agents=r;this.held=new Set(e?.held),this.kept=new Set(e?.kept),this.recv=new Set(e?.recv),this.chosen=new Set(e?.chosen),this.follow=new Set(e?.follow),this.legacy=new Set(e?.legacy??(e?.init&&e.chosen===void 0?e.kept:void 0)),this.send=e?.send??!0,this.noted=e?.noted??!1,this.gen=e?.gen??null,this.init=e?.init??!1}held;kept;recv;chosen;follow;legacy;send;noted;gen;init;saved(){let{init:t,held:e,kept:r,recv:i,chosen:o,follow:s,legacy:a,send:d,noted:c,gen:h}=this;return{init:t,held:e,kept:r,recv:i,chosen:o,follow:s,legacy:a,send:d,noted:c,gen:h}}hold(t){if(!this.init){this.init=!0;for(let{body:e}of t)e.author===this.me&&this.held.add(e.id)}}sendable(t){let e=t.body;if(this.recv.has(e.id))return!0;if(He.has(e.kind)&&this.agents().includes(e.author))return this.send&&![e.target,e.thread].some(i=>i&&(this.held.has(i)||this.kept.has(i)));if(e.author!==this.me||this.held.has(e.id)||this.kept.has(e.id))return!1;let r=[e.target,e.thread].find(i=>i&&(this.held.has(i)||this.kept.has(i)));if(r)(this.held.has(r)?this.held:this.kept).add(e.id),this.follow.add(e.id);else if(!this.send)this.kept.add(e.id);else return(e.kind==="create"||e.kind==="reply")&&(this.noted=!0),!0;return!1}local(t){let e=t.body;return e.author!==this.me||this.recv.has(e.id)||!(this.held.has(e.id)||this.kept.has(e.id))?null:this.follow.has(e.id)?"thread":this.held.has(e.id)?"held":this.chosen.has(e.id)?"chosen":this.legacy.has(e.id)?"unknown":"kept"}decide(t){if(!t)for(let e of this.held)this.kept.add(e),this.chosen.add(e);this.held.clear()}};var je=256e3,Ke=16,qe=/^[A-Za-z0-9_-]{8,64}$/,ve=()=>[T(O(16)),T(O(8)),1e5,256,128,"aes","gcm","none"],Fe=(n,t)=>({v:2,adata:ve(),ct:T(t),pasteid:n,parentid:n});async function F(n){if(n.status===429)throw new f(j(n));if(!n.ok)throw new Error(`HTTP ${n.status}`);let[t]=await U(n);if(t.status===0)return t;let e=Number(/\d+/.exec(String(t.message??""))?.[0]);throw new f(e>=1&&e<=3600?e:30,!0)}async function Ge(n,t,e,r="never",i){let o=T(await x(e,"share",[])),s=await F(await n(t,{...E({v:2,adata:[ve(),"plaintext",1,0],ct:o,meta:{expire:r}}),signal:i}));if(typeof s.id!="string"||typeof s.deletetoken!="string")throw new Error("the answer had no paste id");return{id:s.id,deleteToken:s.deletetoken}}async function Je(n,t,e,r){return n(t,E({pasteid:e,deletetoken:r}))}async function Ye(n,t,e,r){let i=await H(n,`${t}?${e}`,r);if(i.status===404)return null;if(!i.ok)throw new Error(`HTTP ${i.status}`);let[o,s]=await U(i);if(o.status!==0)return null;let a=o.meta?.time_to_live;return{j:o,size:s,ttl:typeof a=="number"&&a>0?a*1e3:null}}var G=class{constructor(t,e,r,i,o,s=()=>{}){this.a=t;this.l=e;this.f=r;this.signal=i;this.onGen=s;this.head=o||t.paste,this.trail.push(t.paste)}ids=new Set;expires=null;moved=!1;big=!1;head;trail=[];seen=new Map;async scan(t,e){let r;for(let i of Array.isArray(t)?t:[]){if(typeof i?.id!="string"||typeof i.ct!="string")continue;let o=this.seen.get(i.id);if(o===void 0){o=null;let s=null;try{i.ct.length<14e5&&(s=ee(i.ct))}catch{}let a=s&&await N(this.l,"share",s),d=a?.next,c=typeof d=="string"?null:z(this.l,a);if(typeof d=="string"&&qe.test(d))o=d;else if(c){e.push(c);for(let h of M(c.ops))this.ids.add(h)}this.seen.set(i.id,o)}r??=o??void 0}return r}async read(){let t=[],e=this.head,r=!1,i=!1;for(let o=0;;){let s=await Ye(this.f,this.a.base,e,this.signal);if(!s){if(i)break;if(!r&&e!==this.a.paste){r=!0,e=this.a.paste;continue}throw new b("gone")}i=!0,e!==this.head&&(this.head=e,this.trail.push(e),this.ids.clear(),this.moved=!0,this.onGen(e));let a=s.j.comments,d=await this.scan(a,t);this.expires=s.ttl&&Date.now()+s.ttl;let c=(Array.isArray(a)?a[0]?.ct?.length:0)??0;if(this.big=!d&&s.size>je&&s.size>2*c,!d||++o>=Ke)break;e=d}return t}ask(t,e){return[this.a.base,E(Fe(t,e))]}async req(t){return this.ask(this.head,await x(this.l,"share",t))}go(t,e){return this.f(t,{...e,signal:this.signal})}async post(t){for(let e of I(t))await F(await this.go(...await this.req(e)))}async roll(t){let e=async s=>{for(let a=0;;a++)try{return await s()}catch(d){if(!(d instanceof f)||a>5||this.signal?.aborted)throw d;await _(d.seconds*1e3*(1+Math.random()*.2),this.signal)}},r=this.head,i=await e(()=>Ge(this.f,this.a.base,this.l,"never",this.signal));for(let s of I(t))await e(async()=>F(await this.go(...this.ask(i.id,await x(this.l,"share",s)))));await e(async()=>F(await this.go(...this.ask(r,await C(this.l,"share",{next:i.id})))));let o=await this.read();return this.head!==i.id&&await Je(this.f,this.a.base,i.id,i.deleteToken).catch(()=>{}),o}};function Ze(n,t,e,r=Math.random()){let i=t?3e5:n<6e5?25e3+r*1e4:n<18e5?12e4:3e5;return Math.min(3e5,i*2**e)}var J=class{constructor(t,e){this.b=t;this.o=e}id="share";problem=null;offline=!1;newer=0;deliver=()=>{};active=Date.now();errors=0;ok=!1;timer;busy=null;again=!1;limit=0;rolling=!1;ready=null;stopped=!1;off=[];async start(t){this.deliver=t;let e=(i,o,s)=>{i&&(i.addEventListener(o,s),this.off.push(()=>i.removeEventListener(o,s)))},r=globalThis;r.addEventListener&&(e(globalThis,"focus",()=>void this.poll()),e(globalThis,"online",()=>void this.poll()),e(globalThis,"pagehide",()=>this.flush()),e(r.document,"visibilitychange",()=>r.document?.visibilityState==="visible"&&void this.poll())),await this.poll()}stop(){this.stopped=!0,clearTimeout(this.timer);for(let t of this.off.splice(0))t()}async remoteIds(){return this.ok?this.b.ids:null}poll(){if(this.busy)return this.again=!0,this.busy;let t=async()=>{do this.again=!1,await this.read();while(this.again&&!this.stopped)};return this.busy=t().finally(()=>this.busy=null)}bump(){this.active=Date.now()}async read(){clearTimeout(this.timer);try{let t=await this.b.read();this.offline=!1,this.errors=0,this.ok=!0,this.newer+=t.reduce((r,i)=>r+i.newer,0);let e=t.flatMap(r=>r.ops);if(e.length&&(this.active=Date.now(),this.deliver(e)),this.b.moved){this.b.moved=!1;let r=this.o.allowed().filter(i=>!this.b.ids.has(i.body.id));r.length&&await this.b.post(r).catch(()=>{})}this.b.big&&!this.rolling&&this.roll()}catch(t){t instanceof b?this.problem=t.kind:(this.offline=!0,this.errors++)}if(this.o.changed(),!this.stopped&&this.problem!=="gone"){let t=globalThis.document?.visibilityState==="hidden";this.timer=setTimeout(()=>void this.poll(),Ze(Date.now()-this.active,t,this.errors))}}lock(t,e=0){let r=this.o.lock,i=globalThis.navigator?.locks;return r?i?new Promise((o,s)=>{i.request(r,{signal:this.o.signal},async()=>{try{o(await t())}catch(a){s(a)}await _(e,this.o.signal)}).catch(s)}):_(Math.random()*5e3,this.o.signal).then(t):t()}get gap(){return Math.max(this.o.gap,this.limit)}async guarded(t){try{await this.lock(t,this.gap),this.offline=!1}catch(e){throw e instanceof f?(e.limit&&(this.limit=Math.max(this.limit,e.seconds*1e3)),e):e instanceof b?(this.problem=e.kind,this.o.changed(),new f(3600)):(this.offline=!0,this.o.changed(),e)}}async send(t){if(this.problem)throw new f(3600);this.bump(),await this.guarded(async()=>{await this.poll();let e=t.filter(r=>!this.b.ids.has(r.body.id));if(e.length){await this.b.post(e);for(let r of e)this.b.ids.add(r.body.id)}})}async prepare(t){try{this.ready=t.length&&JSON.stringify(t).length<4e4?await this.b.req(t):null}catch{this.ready=null}}flush(){let t=this.ready;this.ready=null,!(!t||this.problem||typeof t[1].body!="string"||t[1].body.length>64e3)&&(this.o.fetch??fetch)(t[0],{...t[1],keepalive:!0}).catch(()=>{})}async roll(){this.rolling=!0;try{await _(Math.random()*3e4,this.o.signal),await this.guarded(async()=>{if(await this.poll(),!this.b.big||!this.b.roll)return;let e=(await this.b.roll(this.o.allowed())).flatMap(r=>r.ops);e.length&&this.deliver(e)})}catch{}finally{this.rolling=!1}}};var Xe="Comments here are shared with everyone who has this page.",We=7*864e5,ke=["M12 16V4M7 9l5-5 5 5M5 20h14"],Ve=["M4 7a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3z","M4 9.5h16M7.5 6.8h.01"],Qe="Only in this browser",et={held:"Written before this page was shared. Share it or keep it from the menu.",kept:"Written while Send my comments was off. It stays here.",chosen:"You chose to keep it here.",thread:"Part of a comment that stays in this browser.",unknown:"It stays in this browser and isn't sent to the shared copy."};function Se(n={}){return{id:"share",api:1,version:"0.4.1",needs:["sync","menu","notify","note","commentmark"],network:{when:"page-configured",to:["page"],says:"Sends the comments you choose to share, sealed so only people with this page can read them, to the sharing service the page names. That service sees when and how much is sent, and your network address."},async setup(t){let e=t.document,r=ge(n.share??A("data-pipeup-share"),!0);if(r.kind==="off")return t.off(r.reason);if(!te(e.id))return t.off("needs a document identity");let i=ne(e.id,r.key),o=await se("share",{memory:t.ephemeral,keep:t.keep}),s=await o.get(`doc:${e.id}`),a=new q(e.me,s,we);a.hold(e.ops());let d=Promise.resolve(),c=()=>{d=d.then(()=>o.set(`doc:${e.id}`,a.saved())).catch(()=>{})};c();let h={gone:"The shared copy is gone",closed:"This page can't send to the shared copy; ask the author for the current file",full:"The shared copy is full; the author can start it again"},S=new Set,ae=(l,p)=>S.has(l)||(S.add(l),t.notify(p)),Y=new Set,de=()=>{let l=new Set;for(let p of e.ops()){if(p.body.kind!=="create"&&p.body.kind!=="reply")continue;let g=u.problem==="gone"?null:a.local(p);g&&(l.add(p.body.id),t.setCommentMark(p.body.id,{text:Qe,tip:et[g]}))}for(let p of Y)l.has(p)||t.setCommentMark(p,null);Y.clear();for(let p of l)Y.add(p)},P=()=>{de();for(let l of D)l.update();t.setStatus({text:pe()}),t.setComposerNote(a.send&&!a.noted&&u.problem!=="gone"?Xe:null),u.newer&&ae("newer",`${u.newer} comments come from a newer version of Pipeup; this page can't show them yet.`),u.problem&&ae(u.problem,h[u.problem]+(u.problem==="gone"?". Your comments stay in this browser.":"."))},le=(l,p)=>fetch(l,p),ce=r.kind==="mailbox"?new K(r,i,le,t.signal):new G(r,i,le,t.signal,a.gen,l=>{a.gen=l,c()}),u=new J(ce,{signal:t.signal,gap:1e4,lock:`pipeup-share:${e.id}`,allowed:()=>e.ops().filter(l=>a.sendable(l)),changed:P}),v=new R({id:"share",document:e,merge:async l=>{let p=await t.merge(l),g=new Set(e.ops().map(y=>y.body.id));for(let y of M(l))g.has(y)&&a.recv.add(y);return c(),p},transport:u,sendable:l=>{let p=a.sendable(l);return a.noted!==s?.noted&&c(),p},signal:t.signal,delay:2e3,gap:()=>u.gap}),Z;v.onState(()=>{P(),clearTimeout(Z),Z=setTimeout(()=>void u.prepare(v.pending()),1e3)});let Ae=new URL(r.kind==="mailbox"?r.mailbox:r.base).host,pe=()=>{let l=v.waiting;return u.problem?h[u.problem]:a.send?u.offline||v.state==="offline"?"Shared \xB7 offline, sends when back":l?`Shared \xB7 ${l} change${l===1?"":"s"} waiting`:"Shared \xB7 up to date":"Shared \xB7 not sending your comments"},xe=()=>{let l=ce.expires;return u.problem==="gone"?"Your comments stay in this browser. The author can start sharing again.":[`Your comments are encrypted and sent to ${Ae} so others with this page see them. The service can't read them. People who already have your comments, by file or live, could pass them on.`,t.ephemeral&&"This browser can't keep comments; the shared copy keeps the ones you send.",!o.lasting&&"This browser won't remember this choice.",l&&l-Date.now()<We&&`The shared copy expires on ${new Date(l).toLocaleDateString("en-GB",{day:"numeric",month:"long"})}.`].filter(Boolean).join(" ")},D=[],X=(l,p,g,y,Pe,Te)=>D[D.push(t.addMenuItem({id:l,icon:p,label:g,hint:y,select:Pe,checked:Te}))-1],W=e.ops().filter(l=>a.held.has(l.body.id)&&(l.body.kind==="create"||l.body.kind==="reply")).length;if(X("send",ke,()=>a.send||u.problem?pe():"Send my comments",xe,()=>{a.send=!a.send,c(),v.rescan(),P()},()=>a.send),W){let l=o.lasting?"":" This browser won't remember this choice.",p=g=>()=>{a.decide(g),c();for(let y of D.splice(1))y.remove();v.rescan(),P()};X("share-earlier",ke,()=>`Share my ${W} earlier comment${W===1?"":"s"}`,()=>`Send the comments you wrote before this page was shared.${l}`,p(!0)),X("keep-earlier",Ve,()=>"Keep them in this browser",()=>`Never send the comments you wrote before this page was shared.${l}`,p(!1))}return P(),v.start().then(P,l=>globalThis.reportError?.(l)),e.onChange((l,p,g)=>{if(g==="local"&&p.length){for(let y of p)a.sendable(y);u.bump(),de(),c()}}),()=>{clearTimeout(Z),v.stop()}}}}V(Se());})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipeup/share",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Share Pipeup comments through an encrypted service (PrivateBin or your own HTTP mailbox).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Pipeup contributors",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"LICENSE"
|
|
31
31
|
],
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"pipeup": "0.
|
|
33
|
+
"pipeup": "1.0.0"
|
|
34
34
|
},
|
|
35
35
|
"pipeup": {
|
|
36
36
|
"network": {
|