@graffiti-garden/implementation-local 1.0.0 → 1.0.2

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.
@@ -1,4 +1,4 @@
1
- import{a as L,b as Rt,c as d,e as l,f as m,g as u}from"./chunk-GE6AZATH.js";var lt=L((Be,H)=>{"use strict";d();u();l();H.exports=dt;H.exports.preferredCharsets=dt;var Ft=/^\s*([^\s;]+)\s*(?:;(.*))?$/;function Nt(i){for(var t=i.split(","),e=0,r=0;e<t.length;e++){var o=kt(t[e].trim(),e);o&&(t[r++]=o)}return t.length=r,t}function kt(i,t){var e=Ft.exec(i);if(!e)return null;var r=e[1],o=1;if(e[2])for(var n=e[2].split(";"),a=0;a<n.length;a++){var s=n[a].trim().split("=");if(s[0]==="q"){o=parseFloat(s[1]);break}}return{charset:r,q:o,i:t}}function $t(i,t,e){for(var r={o:-1,q:0,s:0},o=0;o<t.length;o++){var n=Jt(i,t[o],e);n&&(r.s-n.s||r.q-n.q||r.o-n.o)<0&&(r=n)}return r}function Jt(i,t,e){var r=0;if(t.charset.toLowerCase()===i.toLowerCase())r|=1;else if(t.charset!=="*")return null;return{i:e,o:t.i,q:t.q,s:r}}function dt(i,t){var e=Nt(i===void 0?"*":i||"");if(!t)return e.filter(ft).sort(ct).map(zt);var r=t.map(function(n,a){return $t(n,e,a)});return r.filter(ft).sort(ct).map(function(n){return t[r.indexOf(n)]})}function ct(i,t){return t.q-i.q||t.s-i.s||i.o-t.o||i.i-t.i||0}function zt(i){return i.charset}function ft(i){return i.q>0}});var gt=L((Fe,_)=>{"use strict";d();u();l();_.exports=ht;_.exports.preferredEncodings=ht;var Qt=/^\s*([^\s;]+)\s*(?:;(.*))?$/;function Kt(i){for(var t=i.split(","),e=!1,r=1,o=0,n=0;o<t.length;o++){var a=Wt(t[o].trim(),o);a&&(t[n++]=a,e=e||pt("identity",a),r=Math.min(r,a.q||1))}return e||(t[n++]={encoding:"identity",q:r,i:o}),t.length=n,t}function Wt(i,t){var e=Qt.exec(i);if(!e)return null;var r=e[1],o=1;if(e[2])for(var n=e[2].split(";"),a=0;a<n.length;a++){var s=n[a].trim().split("=");if(s[0]==="q"){o=parseFloat(s[1]);break}}return{encoding:r,q:o,i:t}}function Xt(i,t,e){for(var r={encoding:i,o:-1,q:0,s:0},o=0;o<t.length;o++){var n=pt(i,t[o],e);n&&(r.s-n.s||r.q-n.q||r.o-n.o)<0&&(r=n)}return r}function pt(i,t,e){var r=0;if(t.encoding.toLowerCase()===i.toLowerCase())r|=1;else if(t.encoding!=="*")return null;return{encoding:i,i:e,o:t.i,q:t.q,s:r}}function ht(i,t,e){var r=Kt(i||""),o=e?function(s,c){if(s.q!==c.q)return c.q-s.q;var p=e.indexOf(s.encoding),g=e.indexOf(c.encoding);return p===-1&&g===-1?c.s-s.s||s.o-c.o||s.i-c.i:p!==-1&&g!==-1?p-g:p===-1?1:-1}:Vt;if(!t)return r.filter(ut).sort(o).map(Yt);var n=t.map(function(s,c){return Xt(s,r,c)});return n.filter(ut).sort(o).map(function(s){return t[n.indexOf(s)]})}function Vt(i,t){return t.q-i.q||t.s-i.s||i.o-t.o||i.i-t.i}function Yt(i){return i.encoding}function ut(i){return i.q>0}});var bt=L((Je,F)=>{"use strict";d();u();l();F.exports=wt;F.exports.preferredLanguages=wt;var Zt=/^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;function te(i){for(var t=i.split(","),e=0,r=0;e<t.length;e++){var o=vt(t[e].trim(),e);o&&(t[r++]=o)}return t.length=r,t}function vt(i,t){var e=Zt.exec(i);if(!e)return null;var r=e[1],o=e[2],n=r;o&&(n+="-"+o);var a=1;if(e[3])for(var s=e[3].split(";"),c=0;c<s.length;c++){var p=s[c].split("=");p[0]==="q"&&(a=parseFloat(p[1]))}return{prefix:r,suffix:o,q:a,i:t,full:n}}function ee(i,t,e){for(var r={o:-1,q:0,s:0},o=0;o<t.length;o++){var n=re(i,t[o],e);n&&(r.s-n.s||r.q-n.q||r.o-n.o)<0&&(r=n)}return r}function re(i,t,e){var r=vt(i);if(!r)return null;var o=0;if(t.full.toLowerCase()===r.full.toLowerCase())o|=4;else if(t.prefix.toLowerCase()===r.full.toLowerCase())o|=2;else if(t.full.toLowerCase()===r.prefix.toLowerCase())o|=1;else if(t.full!=="*")return null;return{i:e,o:t.i,q:t.q,s:o}}function wt(i,t){var e=te(i===void 0?"*":i||"");if(!t)return e.filter(mt).sort(yt).map(ie);var r=t.map(function(n,a){return ee(n,e,a)});return r.filter(mt).sort(yt).map(function(n){return t[r.indexOf(n)]})}function yt(i,t){return t.q-i.q||t.s-i.s||i.o-t.o||i.i-t.i||0}function ie(i){return i.full}function mt(i){return i.q>0}});var xt=L((We,N)=>{"use strict";d();u();l();N.exports=Lt;N.exports.preferredMediaTypes=Lt;var oe=/^\s*([^\s\/;]+)\/([^;\s]+)\s*(?:;(.*))?$/;function ne(i){for(var t=de(i),e=0,r=0;e<t.length;e++){var o=Et(t[e].trim(),e);o&&(t[r++]=o)}return t.length=r,t}function Et(i,t){var e=oe.exec(i);if(!e)return null;var r=Object.create(null),o=1,n=e[2],a=e[1];if(e[3])for(var s=le(e[3]).map(fe),c=0;c<s.length;c++){var p=s[c],g=p[0].toLowerCase(),h=p[1],y=h&&h[0]==='"'&&h[h.length-1]==='"'?h.slice(1,-1):h;if(g==="q"){o=parseFloat(y);break}r[g]=y}return{type:a,subtype:n,params:r,q:o,i:t}}function ae(i,t,e){for(var r={o:-1,q:0,s:0},o=0;o<t.length;o++){var n=se(i,t[o],e);n&&(r.s-n.s||r.q-n.q||r.o-n.o)<0&&(r=n)}return r}function se(i,t,e){var r=Et(i),o=0;if(!r)return null;if(t.type.toLowerCase()==r.type.toLowerCase())o|=4;else if(t.type!="*")return null;if(t.subtype.toLowerCase()==r.subtype.toLowerCase())o|=2;else if(t.subtype!="*")return null;var n=Object.keys(t.params);if(n.length>0)if(n.every(function(a){return t.params[a]=="*"||(t.params[a]||"").toLowerCase()==(r.params[a]||"").toLowerCase()}))o|=1;else return null;return{i:e,o:t.i,q:t.q,s:o}}function Lt(i,t){var e=ne(i===void 0?"*/*":i||"");if(!t)return e.filter(Ot).sort(Gt).map(ce);var r=t.map(function(n,a){return ae(n,e,a)});return r.filter(Ot).sort(Gt).map(function(n){return t[r.indexOf(n)]})}function Gt(i,t){return t.q-i.q||t.s-i.s||i.o-t.o||i.i-t.i||0}function ce(i){return i.type+"/"+i.subtype}function Ot(i){return i.q>0}function Ct(i){for(var t=0,e=0;(e=i.indexOf('"',e))!==-1;)t++,e++;return t}function fe(i){var t=i.indexOf("="),e,r;return t===-1?e=i:(e=i.slice(0,t),r=i.slice(t+1)),[e,r]}function de(i){for(var t=i.split(","),e=1,r=0;e<t.length;e++)Ct(t[r])%2==0?t[++r]=t[e]:t[r]+=","+t[e];return t.length=r+1,t}function le(i){for(var t=i.split(";"),e=1,r=0;e<t.length;e++)Ct(t[r])%2==0?t[++r]=t[e]:t[r]+=";"+t[e];t.length=r+1;for(var e=0;e<t.length;e++)t[e]=t[e].trim();return t}});var jt=L((Ze,k)=>{"use strict";d();u();l();var ue=lt(),pe=gt(),he=bt(),ge=xt();k.exports=f;k.exports.Negotiator=f;function f(i){if(!(this instanceof f))return new f(i);this.request=i}f.prototype.charset=function(t){var e=this.charsets(t);return e&&e[0]};f.prototype.charsets=function(t){return ue(this.request.headers["accept-charset"],t)};f.prototype.encoding=function(t,e){var r=this.encodings(t,e);return r&&r[0]};f.prototype.encodings=function(t,e){var r=e||{};return pe(this.request.headers["accept-encoding"],t,r.preferred)};f.prototype.language=function(t){var e=this.languages(t);return e&&e[0]};f.prototype.languages=function(t){return he(this.request.headers["accept-language"],t)};f.prototype.mediaType=function(t){var e=this.mediaTypes(t);return e&&e[0]};f.prototype.mediaTypes=function(t){return ge(this.request.headers.accept,t)};f.prototype.preferredCharset=f.prototype.charset;f.prototype.preferredCharsets=f.prototype.charsets;f.prototype.preferredEncoding=f.prototype.encoding;f.prototype.preferredEncodings=f.prototype.encodings;f.prototype.preferredLanguage=f.prototype.language;f.prototype.preferredLanguages=f.prototype.languages;f.prototype.preferredMediaType=f.prototype.mediaType;f.prototype.preferredMediaTypes=f.prototype.mediaTypes});d();u();l();d();u();l();var Ht={type:"object",properties:{value:{type:"object"},channels:{type:"array",items:{type:"string"}},allowed:{type:"array",items:{type:"string"},nullable:!0},url:{type:"string"},actor:{type:"string"}},additionalProperties:!1,required:["value","channels","actor","url"]},me={...Ht,required:["value","channels"]};var M=class $ extends Error{constructor(t){super(t),this.name="GraffitiErrorForbidden",Object.setPrototypeOf(this,$.prototype)}},b=class J extends Error{constructor(t){super(t),this.name="GraffitiErrorNotFound",Object.setPrototypeOf(this,J.prototype)}},_t=class z extends Error{constructor(t){super(t),this.name="GraffitiErrorInvalidSchema",Object.setPrototypeOf(this,z.prototype)}},Q=class K extends Error{constructor(t){super(t),this.name="GraffitiErrorSchemaMismatch",Object.setPrototypeOf(this,K.prototype)}},W=class X extends Error{constructor(t){super(t),this.name="GraffitiErrorTooLarge",Object.setPrototypeOf(this,X.prototype)}},V=class Y extends Error{constructor(t){super(t),this.name="GraffitiErrorNotAcceptable",Object.setPrototypeOf(this,Y.prototype)}};function D(i){return typeof i=="string"?i:i.url}function I(i,t){try{return i.compile(t)}catch(e){throw new _t(e instanceof Error?e.message:void 0)}}function U(i,t){return!Array.isArray(i.allowed)||typeof t?.actor=="string"&&(i.actor===t.actor||i.allowed.includes(t.actor))}function A(i,t,e){i.actor!==e?.actor&&(i.allowed=i.allowed&&e?[e.actor]:void 0,i.channels=i.channels.filter(r=>t.includes(r)))}d();u();l();d();u();l();function B(i){return btoa(String.fromCodePoint(...i)).replace(/\+/g,"-").replace(/\//g,"_").replace(/\=+$/,"")}function Z(i){let t=i.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4!==0;)t+="=";return Uint8Array.from(atob(t),e=>e.charCodeAt(0))}function tt(i=32){let t=new Uint8Array(i);return crypto.getRandomValues(t),B(t)}var et="graffiti:object:",rt="graffiti:media:";function it(i,t,e){return`${e}${encodeURIComponent(i)}:${encodeURIComponent(t)}`}function C(i,t){return it(i,t,et)}function ot(i,t){return it(i,t,rt)}function nt(i,t){if(!i.startsWith(t))throw new Error(`URL does not start with ${t}`);let e=i.slice(t.length).split(":");if(e.length!==2)throw new Error("URL has too many colon-seperated parts");let[r,o]=e.map(decodeURIComponent);return{actor:r,id:o}}function G(i){return nt(i,et)}function R(i){return nt(i,rt)}async function at(i){if(typeof FileReader<"u")return new Promise((t,e)=>{let r=new FileReader;r.onload=()=>{typeof r.result=="string"?t(r.result):e(new Error("Unexpected result type"))},r.onerror=e,r.readAsDataURL(i)});if(typeof m<"u"){let t=await i.arrayBuffer();return`data:${i.type};base64,${m.from(t).toString("base64")}`}throw new Error("Unsupported environment")}async function st(i){return await(await fetch(i)).blob()}var S="did:local:",q=class{sessionEvents=new EventTarget;handleToActor=async t=>{let e=new TextEncoder().encode(t),r=B(e);return`${S}${r}`};actorToHandle=async t=>{if(!t.startsWith(S))throw new Error(`actor must start with ${S}`);let e=t.slice(S.length),r=Z(e);return new TextDecoder().decode(r)};constructor(){(async()=>{await Promise.resolve();for(let r of this.getLoggedInHandles()){let o=new CustomEvent("login",{detail:{session:{actor:await this.handleToActor(r)}}});this.sessionEvents.dispatchEvent(o)}let e=new CustomEvent("initialized",{detail:{}});this.sessionEvents.dispatchEvent(e)})()}loggedInHandles=[];getLoggedInHandles(){if(typeof window<"u"){let t=window.localStorage.getItem("graffiti-handles");return t?t.split(",").map(decodeURIComponent):[]}else return this.loggedInHandles}setLoggedInHandles(t){typeof window<"u"?window.localStorage.setItem("graffiti-handles",t.join(",")):this.loggedInHandles=t}login=async t=>{await new Promise(r=>setTimeout(r,0));let e=t?await this.actorToHandle(t):void 0;if(typeof window<"u"&&(e=window.prompt("Choose a username to log in.",e)??void 0),e){let r=this.getLoggedInHandles();r.includes(e)||this.setLoggedInHandles([...r,e]),window.location.reload()}else{let r={error:new Error("No handle provided to login")},o=new CustomEvent("login",{detail:r});this.sessionEvents.dispatchEvent(o)}};logout=async t=>{let e=await this.actorToHandle(t.actor),r=this.getLoggedInHandles(),o=r.includes(e);o&&this.setLoggedInHandles(r.filter(s=>s!==e));let n=o?{actor:t.actor}:{actor:t.actor,error:new Error("Not logged in with that actor")},a=new CustomEvent("logout",{detail:n});this.sessionEvents.dispatchEvent(a)}};d();u();l();var T=class{db_;ajv_;options;operationClock=0;get db(){return this.db_||(this.db_=(async()=>{let{default:t}=await import("./index-browser.es-UXYPGJ2M.js"),e={name:"graffitiDb",...this.options.pouchDBOptions},r=new t(e.name,e);return await r.put({_id:"_design/indexes",views:{objectsPerChannelAndLastModified:{map:function(o){let n=o.lastModified.toString().padStart(15,"0");o.channels.forEach(function(a){let s=encodeURIComponent(a)+"/"+n;emit(s)})}.toString()}}}).catch(o=>{if(!(o&&typeof o=="object"&&"name"in o&&o.name==="conflict"))throw o}),r})()),this.db_}get ajv(){return this.ajv_||(this.ajv_=(async()=>{let{default:t}=await import("./ajv-IY2ZY7VT.js");return new t({strict:!1})})()),this.ajv_}constructor(t){this.options=t??{}}get=async(...t)=>{let[e,r,o]=t,n=D(e),a;try{a=await(await this.db).get(n)}catch{throw new b("The object you are trying to get either does not exist or you are not allowed to see it")}if(a.tombstone)throw new b("The object you are trying to get either does not exist or you are not allowed to see it");let{actor:s}=G(n),{value:c,channels:p,allowed:g}=a,h={value:c,channels:p,allowed:g,url:n,actor:s};if(!U(h,o))throw new b("The object you are trying to get either does not exist or you are not allowed to see it");if(A(h,[],o),!I(await this.ajv,r)(h))throw new Q;return h};delete=async(...t)=>{let[e,r]=t,o=D(e),{actor:n}=G(o);if(n!==r.actor)throw new M("You cannot delete an object that you did not create.");let a;try{a=await(await this.db).get(o)}catch{throw new b("Object not found.")}if(a.tombstone)throw new b("Object not found.");a.tombstone=!0,a.lastModified=this.operationClock;try{await(await this.db).put(a)}catch{throw new b("Object not found.")}this.operationClock++};post=async(...t)=>{let[e,r]=t,o=r.actor,n=tt(),a=C(o,n),{value:s,channels:c,allowed:p}=e,g={value:s,channels:c,allowed:p,lastModified:this.operationClock,tombstone:!1};return await(await this.db).put({_id:a,...g}),this.operationClock++,{...e,actor:o,url:a}};async*discoverMeta(t,e){if(e){let h=this.options.continueBuffer??2e3,y=Date.now()-e.lastDiscovered;y<h&&await new Promise(O=>setTimeout(O,h-y))}let[r,o,n]=t,a=I(await this.ajv,o),s=e?e.ifModifiedSince.toString().padStart(15,"0"):"",c="\uFFFF",p=new Set,g=this.operationClock;for(let h of r){let y=encodeURIComponent(h)+"/",O=y+s,qt=y+c,Tt=await(await this.db).query("indexes/objectsPerChannelAndLastModified",{startkey:O,endkey:qt,include_docs:!0});for(let Pt of Tt.rows){let x=Pt.doc;if(!x)continue;let E=x._id;if(p.has(E)||(p.add(E),!e&&x.tombstone))continue;let{tombstone:Dt,value:It,channels:Ut,allowed:At}=x,{actor:Bt}=G(E),j={url:E,value:It,allowed:At,channels:Ut,actor:Bt};U(j,n)&&(A(j,r,n),a(j)&&(yield Dt?{tombstone:!0,object:{url:E}}:{object:j}))}}return{lastDiscovered:Date.now(),ifModifiedSince:g}}discoverCursor(t,e){let[r,o,n]=t;return"discover:"+JSON.stringify({channels:r,schema:o,continueParams:e,actor:n?.actor})}async*discoverContinue(t,e,r){if(r?.actor!==t[2]?.actor)throw new M("Cannot continue a cursor started by another actor");let o=this.discoverMeta(t,e);for(;;){let n=await o.next();if(n.done)return{continue:a=>this.discoverContinue(t,n.value,a),cursor:this.discoverCursor(t,n.value)};yield n.value}}discover=(...t)=>{let[e,r,o]=t,n=this.discoverMeta([e,r,o]),a=this;return(async function*(){for(;;){let s=await n.next();if(s.done)return{continue:c=>a.discoverContinue(t,s.value,c),cursor:a.discoverCursor(t,s.value)};s.value.tombstone||(yield s.value)}})()};continueDiscover=(...t)=>{let[e,r]=t;if(e.startsWith("discover:")){let{channels:o,schema:n,actor:a,continueParams:s}=JSON.parse(e.slice(9));if(a&&a!==r?.actor)throw new M("Cannot continue a cursor started by another actor");return this.discoverContinue([o,n,r],s)}else throw new b("Cursor not found")}};d();u();l();var Mt=Rt(jt()),ye={properties:{value:{properties:{dataBase64:{type:"string"},type:{type:"string"},size:{type:"number"}},required:["dataBase64","type","size"]}}},P=class{db;constructor(t){this.db=t}postMedia=async(...t)=>{let[e,r]=t,o=await at(e.data),n=e.data.type,{url:a}=await this.db.post({value:{dataBase64:o,type:n,size:e.data.size},channels:[],allowed:e.allowed},r),{actor:s,id:c}=G(a);return ot(s,c)};getMedia=async(...t)=>{let[e,r,o]=t,{actor:n,id:a}=R(e),s=C(n,a),c=await this.db.get(s,ye,o),{dataBase64:p,type:g,size:h}=c.value;if(r?.maxBytes&&h>r.maxBytes)throw new W("File size exceeds limit");if(r?.accept&&new Mt.default({headers:{accept:r.accept}}).mediaType([g])!==g)throw new V(`Unsupported media type, ${g}`);let y=await st(p);if(y.size!==h||y.type!==g)throw new Error("Invalid data");return{data:y,actor:c.actor,allowed:c.allowed}};deleteMedia=async(...t)=>{let[e,r]=t,{actor:o,id:n}=R(e),a=C(o,n);await this.db.delete(a,r)}};var St=class{graffitiLocalIdentity=new q;login=this.graffitiLocalIdentity.login.bind(this.graffitiLocalIdentity);logout=this.graffitiLocalIdentity.logout.bind(this.graffitiLocalIdentity);handleToActor=this.graffitiLocalIdentity.handleToActor.bind(this.graffitiLocalIdentity);actorToHandle=this.graffitiLocalIdentity.actorToHandle.bind(this.graffitiLocalIdentity);sessionEvents=this.graffitiLocalIdentity.sessionEvents;graffitiLocalObjects;post;get;delete;discover;continueDiscover;graffitiLocalMedia;postMedia;getMedia;deleteMedia;constructor(t){this.graffitiLocalObjects=new T(t),this.post=this.graffitiLocalObjects.post.bind(this.graffitiLocalObjects),this.get=this.graffitiLocalObjects.get.bind(this.graffitiLocalObjects),this.delete=this.graffitiLocalObjects.delete.bind(this.graffitiLocalObjects),this.discover=this.graffitiLocalObjects.discover.bind(this.graffitiLocalObjects),this.continueDiscover=this.graffitiLocalObjects.continueDiscover.bind(this.graffitiLocalObjects),this.graffitiLocalMedia=new P(this.graffitiLocalObjects),this.postMedia=this.graffitiLocalMedia.postMedia.bind(this.graffitiLocalMedia),this.getMedia=this.graffitiLocalMedia.getMedia.bind(this.graffitiLocalMedia),this.deleteMedia=this.graffitiLocalMedia.deleteMedia.bind(this.graffitiLocalMedia)}};export{St as GraffitiLocal};
1
+ import{a as E,b as Rt,c as d,e as l,f as m,g as u}from"./chunk-GE6AZATH.js";var lt=E((Be,_)=>{"use strict";d();u();l();_.exports=dt;_.exports.preferredCharsets=dt;var Nt=/^\s*([^\s;]+)\s*(?:;(.*))?$/;function Ft(i){for(var t=i.split(","),e=0,r=0;e<t.length;e++){var o=kt(t[e].trim(),e);o&&(t[r++]=o)}return t.length=r,t}function kt(i,t){var e=Nt.exec(i);if(!e)return null;var r=e[1],o=1;if(e[2])for(var n=e[2].split(";"),a=0;a<n.length;a++){var s=n[a].trim().split("=");if(s[0]==="q"){o=parseFloat(s[1]);break}}return{charset:r,q:o,i:t}}function $t(i,t,e){for(var r={o:-1,q:0,s:0},o=0;o<t.length;o++){var n=Jt(i,t[o],e);n&&(r.s-n.s||r.q-n.q||r.o-n.o)<0&&(r=n)}return r}function Jt(i,t,e){var r=0;if(t.charset.toLowerCase()===i.toLowerCase())r|=1;else if(t.charset!=="*")return null;return{i:e,o:t.i,q:t.q,s:r}}function dt(i,t){var e=Ft(i===void 0?"*":i||"");if(!t)return e.filter(ft).sort(ct).map(zt);var r=t.map(function(n,a){return $t(n,e,a)});return r.filter(ft).sort(ct).map(function(n){return t[r.indexOf(n)]})}function ct(i,t){return t.q-i.q||t.s-i.s||i.o-t.o||i.i-t.i||0}function zt(i){return i.charset}function ft(i){return i.q>0}});var gt=E((Ne,H)=>{"use strict";d();u();l();H.exports=ht;H.exports.preferredEncodings=ht;var Qt=/^\s*([^\s;]+)\s*(?:;(.*))?$/;function Kt(i){for(var t=i.split(","),e=!1,r=1,o=0,n=0;o<t.length;o++){var a=Wt(t[o].trim(),o);a&&(t[n++]=a,e=e||pt("identity",a),r=Math.min(r,a.q||1))}return e||(t[n++]={encoding:"identity",q:r,i:o}),t.length=n,t}function Wt(i,t){var e=Qt.exec(i);if(!e)return null;var r=e[1],o=1;if(e[2])for(var n=e[2].split(";"),a=0;a<n.length;a++){var s=n[a].trim().split("=");if(s[0]==="q"){o=parseFloat(s[1]);break}}return{encoding:r,q:o,i:t}}function Xt(i,t,e){for(var r={encoding:i,o:-1,q:0,s:0},o=0;o<t.length;o++){var n=pt(i,t[o],e);n&&(r.s-n.s||r.q-n.q||r.o-n.o)<0&&(r=n)}return r}function pt(i,t,e){var r=0;if(t.encoding.toLowerCase()===i.toLowerCase())r|=1;else if(t.encoding!=="*")return null;return{encoding:i,i:e,o:t.i,q:t.q,s:r}}function ht(i,t,e){var r=Kt(i||""),o=e?function(s,c){if(s.q!==c.q)return c.q-s.q;var p=e.indexOf(s.encoding),g=e.indexOf(c.encoding);return p===-1&&g===-1?c.s-s.s||s.o-c.o||s.i-c.i:p!==-1&&g!==-1?p-g:p===-1?1:-1}:Vt;if(!t)return r.filter(ut).sort(o).map(Yt);var n=t.map(function(s,c){return Xt(s,r,c)});return n.filter(ut).sort(o).map(function(s){return t[n.indexOf(s)]})}function Vt(i,t){return t.q-i.q||t.s-i.s||i.o-t.o||i.i-t.i}function Yt(i){return i.encoding}function ut(i){return i.q>0}});var bt=E((Je,N)=>{"use strict";d();u();l();N.exports=wt;N.exports.preferredLanguages=wt;var Zt=/^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;function te(i){for(var t=i.split(","),e=0,r=0;e<t.length;e++){var o=vt(t[e].trim(),e);o&&(t[r++]=o)}return t.length=r,t}function vt(i,t){var e=Zt.exec(i);if(!e)return null;var r=e[1],o=e[2],n=r;o&&(n+="-"+o);var a=1;if(e[3])for(var s=e[3].split(";"),c=0;c<s.length;c++){var p=s[c].split("=");p[0]==="q"&&(a=parseFloat(p[1]))}return{prefix:r,suffix:o,q:a,i:t,full:n}}function ee(i,t,e){for(var r={o:-1,q:0,s:0},o=0;o<t.length;o++){var n=re(i,t[o],e);n&&(r.s-n.s||r.q-n.q||r.o-n.o)<0&&(r=n)}return r}function re(i,t,e){var r=vt(i);if(!r)return null;var o=0;if(t.full.toLowerCase()===r.full.toLowerCase())o|=4;else if(t.prefix.toLowerCase()===r.full.toLowerCase())o|=2;else if(t.full.toLowerCase()===r.prefix.toLowerCase())o|=1;else if(t.full!=="*")return null;return{i:e,o:t.i,q:t.q,s:o}}function wt(i,t){var e=te(i===void 0?"*":i||"");if(!t)return e.filter(mt).sort(yt).map(ie);var r=t.map(function(n,a){return ee(n,e,a)});return r.filter(mt).sort(yt).map(function(n){return t[r.indexOf(n)]})}function yt(i,t){return t.q-i.q||t.s-i.s||i.o-t.o||i.i-t.i||0}function ie(i){return i.full}function mt(i){return i.q>0}});var Ct=E((We,F)=>{"use strict";d();u();l();F.exports=Et;F.exports.preferredMediaTypes=Et;var oe=/^\s*([^\s\/;]+)\/([^;\s]+)\s*(?:;(.*))?$/;function ne(i){for(var t=de(i),e=0,r=0;e<t.length;e++){var o=Lt(t[e].trim(),e);o&&(t[r++]=o)}return t.length=r,t}function Lt(i,t){var e=oe.exec(i);if(!e)return null;var r=Object.create(null),o=1,n=e[2],a=e[1];if(e[3])for(var s=le(e[3]).map(fe),c=0;c<s.length;c++){var p=s[c],g=p[0].toLowerCase(),h=p[1],y=h&&h[0]==='"'&&h[h.length-1]==='"'?h.slice(1,-1):h;if(g==="q"){o=parseFloat(y);break}r[g]=y}return{type:a,subtype:n,params:r,q:o,i:t}}function ae(i,t,e){for(var r={o:-1,q:0,s:0},o=0;o<t.length;o++){var n=se(i,t[o],e);n&&(r.s-n.s||r.q-n.q||r.o-n.o)<0&&(r=n)}return r}function se(i,t,e){var r=Lt(i),o=0;if(!r)return null;if(t.type.toLowerCase()==r.type.toLowerCase())o|=4;else if(t.type!="*")return null;if(t.subtype.toLowerCase()==r.subtype.toLowerCase())o|=2;else if(t.subtype!="*")return null;var n=Object.keys(t.params);if(n.length>0)if(n.every(function(a){return t.params[a]=="*"||(t.params[a]||"").toLowerCase()==(r.params[a]||"").toLowerCase()}))o|=1;else return null;return{i:e,o:t.i,q:t.q,s:o}}function Et(i,t){var e=ne(i===void 0?"*/*":i||"");if(!t)return e.filter(Gt).sort(Ot).map(ce);var r=t.map(function(n,a){return ae(n,e,a)});return r.filter(Gt).sort(Ot).map(function(n){return t[r.indexOf(n)]})}function Ot(i,t){return t.q-i.q||t.s-i.s||i.o-t.o||i.i-t.i||0}function ce(i){return i.type+"/"+i.subtype}function Gt(i){return i.q>0}function xt(i){for(var t=0,e=0;(e=i.indexOf('"',e))!==-1;)t++,e++;return t}function fe(i){var t=i.indexOf("="),e,r;return t===-1?e=i:(e=i.slice(0,t),r=i.slice(t+1)),[e,r]}function de(i){for(var t=i.split(","),e=1,r=0;e<t.length;e++)xt(t[r])%2==0?t[++r]=t[e]:t[r]+=","+t[e];return t.length=r+1,t}function le(i){for(var t=i.split(";"),e=1,r=0;e<t.length;e++)xt(t[r])%2==0?t[++r]=t[e]:t[r]+=";"+t[e];t.length=r+1;for(var e=0;e<t.length;e++)t[e]=t[e].trim();return t}});var Mt=E((Ze,k)=>{"use strict";d();u();l();var ue=lt(),pe=gt(),he=bt(),ge=Ct();k.exports=f;k.exports.Negotiator=f;function f(i){if(!(this instanceof f))return new f(i);this.request=i}f.prototype.charset=function(t){var e=this.charsets(t);return e&&e[0]};f.prototype.charsets=function(t){return ue(this.request.headers["accept-charset"],t)};f.prototype.encoding=function(t,e){var r=this.encodings(t,e);return r&&r[0]};f.prototype.encodings=function(t,e){var r=e||{};return pe(this.request.headers["accept-encoding"],t,r.preferred)};f.prototype.language=function(t){var e=this.languages(t);return e&&e[0]};f.prototype.languages=function(t){return he(this.request.headers["accept-language"],t)};f.prototype.mediaType=function(t){var e=this.mediaTypes(t);return e&&e[0]};f.prototype.mediaTypes=function(t){return ge(this.request.headers.accept,t)};f.prototype.preferredCharset=f.prototype.charset;f.prototype.preferredCharsets=f.prototype.charsets;f.prototype.preferredEncoding=f.prototype.encoding;f.prototype.preferredEncodings=f.prototype.encodings;f.prototype.preferredLanguage=f.prototype.language;f.prototype.preferredLanguages=f.prototype.languages;f.prototype.preferredMediaType=f.prototype.mediaType;f.prototype.preferredMediaTypes=f.prototype.mediaTypes});d();u();l();d();u();l();var _t={type:"object",properties:{value:{type:"object"},channels:{type:"array",items:{type:"string"}},allowed:{type:"array",items:{type:"string"},nullable:!0},url:{type:"string"},actor:{type:"string"}},additionalProperties:!1,required:["value","channels","actor","url"]},me={..._t,required:["value","channels"]},j=class $ extends Error{constructor(t){super(t),this.name="GraffitiErrorForbidden",Object.setPrototypeOf(this,$.prototype)}},b=class J extends Error{constructor(t){super(t),this.name="GraffitiErrorNotFound",Object.setPrototypeOf(this,J.prototype)}},Ht=class z extends Error{constructor(t){super(t),this.name="GraffitiErrorInvalidSchema",Object.setPrototypeOf(this,z.prototype)}},Q=class K extends Error{constructor(t){super(t),this.name="GraffitiErrorSchemaMismatch",Object.setPrototypeOf(this,K.prototype)}},W=class X extends Error{constructor(t){super(t),this.name="GraffitiErrorTooLarge",Object.setPrototypeOf(this,X.prototype)}},V=class Y extends Error{constructor(t){super(t),this.name="GraffitiErrorNotAcceptable",Object.setPrototypeOf(this,Y.prototype)}};function P(i){return typeof i=="string"?i:i.url}function I(i,t){try{return i.compile(t)}catch(e){throw new Ht(e instanceof Error?e.message:void 0)}}function A(i,t){return!Array.isArray(i.allowed)||typeof t?.actor=="string"&&(i.actor===t.actor||i.allowed.includes(t.actor))}function U(i,t,e){i.actor!==e?.actor&&(i.allowed=i.allowed&&e?[e.actor]:void 0,i.channels=i.channels.filter(r=>t.includes(r)))}d();u();l();d();u();l();function B(i){return btoa(String.fromCodePoint(...i)).replace(/\+/g,"-").replace(/\//g,"_").replace(/\=+$/,"")}function Z(i){let t=i.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4!==0;)t+="=";return Uint8Array.from(atob(t),e=>e.charCodeAt(0))}function tt(i=32){let t=new Uint8Array(i);return crypto.getRandomValues(t),B(t)}var et="graffiti:object:",rt="graffiti:media:";function it(i,t,e){return`${e}${encodeURIComponent(i)}:${encodeURIComponent(t)}`}function x(i,t){return it(i,t,et)}function ot(i,t){return it(i,t,rt)}function nt(i,t){if(!i.startsWith(t))throw new Error(`URL does not start with ${t}`);let e=i.slice(t.length).split(":");if(e.length!==2)throw new Error("URL has too many colon-seperated parts");let[r,o]=e.map(decodeURIComponent);return{actor:r,id:o}}function O(i){return nt(i,et)}function R(i){return nt(i,rt)}async function at(i){if(typeof FileReader<"u")return new Promise((t,e)=>{let r=new FileReader;r.onload=()=>{typeof r.result=="string"?t(r.result):e(new Error("Unexpected result type"))},r.onerror=e,r.readAsDataURL(i)});if(typeof m<"u"){let t=await i.arrayBuffer();return`data:${i.type};base64,${m.from(t).toString("base64")}`}throw new Error("Unsupported environment")}async function st(i){return await(await fetch(i)).blob()}var S="did:local:",q=class{sessionEvents=new EventTarget;handleToActor=async t=>{let e=new TextEncoder().encode(t),r=B(e);return`${S}${r}`};actorToHandle=async t=>{if(!t.startsWith(S))throw new Error(`actor must start with ${S}`);let e=t.slice(S.length),r=Z(e);return new TextDecoder().decode(r)};constructor(){(async()=>{await Promise.resolve();for(let r of this.getLoggedInHandles()){let o=new CustomEvent("login",{detail:{session:{actor:await this.handleToActor(r)}}});this.sessionEvents.dispatchEvent(o)}let e=new CustomEvent("initialized",{detail:{}});this.sessionEvents.dispatchEvent(e)})()}loggedInHandles=[];getLoggedInHandles(){if(typeof window<"u"){let t=window.localStorage.getItem("graffiti-handles");return t?t.split(",").map(decodeURIComponent):[]}else return this.loggedInHandles}setLoggedInHandles(t){typeof window<"u"?window.localStorage.setItem("graffiti-handles",t.join(",")):this.loggedInHandles=t}login=async t=>{await new Promise(r=>setTimeout(r,0));let e=t?await this.actorToHandle(t):void 0;if(typeof window<"u"&&(e=window.prompt("Choose a username to log in.",e)??void 0),e){let r=this.getLoggedInHandles();r.includes(e)||this.setLoggedInHandles([...r,e]),window.location.reload()}else{let r={error:new Error("No handle provided to login")},o=new CustomEvent("login",{detail:r});this.sessionEvents.dispatchEvent(o)}};logout=async t=>{let e=await this.actorToHandle(t.actor),r=this.getLoggedInHandles(),o=r.includes(e);o&&this.setLoggedInHandles(r.filter(s=>s!==e));let n=o?{actor:t.actor}:{actor:t.actor,error:new Error("Not logged in with that actor")},a=new CustomEvent("logout",{detail:n});this.sessionEvents.dispatchEvent(a)}};d();u();l();var T=class{db_;ajv_;options;get db(){return this.db_||(this.db_=(async()=>{let{default:t}=await import("./index-browser.es-UXYPGJ2M.js"),e={name:"graffitiDb",...this.options.pouchDBOptions},r=new t(e.name,e);return await r.put({_id:"_design/indexes",views:{objectsPerChannelAndLastModified:{map:function(o){let n=o.lastModified.toString().padStart(15,"0");o.channels.forEach(function(a){let s=encodeURIComponent(a)+"/"+n;emit(s)})}.toString()}}}).catch(o=>{if(!(o&&typeof o=="object"&&"name"in o&&o.name==="conflict"))throw o}),r})()),this.db_}get ajv(){return this.ajv_||(this.ajv_=(async()=>{let{default:t}=await import("./ajv-IY2ZY7VT.js");return new t({strict:!1})})()),this.ajv_}async getOperationClock(){return Number((await(await this.db).info()).update_seq)}constructor(t){this.options=t??{}}get=async(...t)=>{let[e,r,o]=t,n=P(e),a;try{a=await(await this.db).get(n)}catch{throw new b("The object you are trying to get either does not exist or you are not allowed to see it")}if(a.tombstone)throw new b("The object you are trying to get either does not exist or you are not allowed to see it");let{actor:s}=O(n),{value:c,channels:p,allowed:g}=a,h={value:c,channels:p,allowed:g,url:n,actor:s};if(!A(h,o))throw new b("The object you are trying to get either does not exist or you are not allowed to see it");if(U(h,[],o),!I(await this.ajv,r)(h))throw new Q;return h};delete=async(...t)=>{let[e,r]=t,o=P(e),{actor:n}=O(o);if(n!==r.actor)throw new j("You cannot delete an object that you did not create.");let a;try{a=await(await this.db).get(o)}catch{throw new b("Object not found.")}if(a.tombstone)throw new b("Object not found.");a.tombstone=!0,a.lastModified=await this.getOperationClock();try{await(await this.db).put(a)}catch{throw new b("Object not found.")}};post=async(...t)=>{let[e,r]=t,o=r.actor,n=tt(),a=x(o,n),{value:s,channels:c,allowed:p}=e,g={value:s,channels:c,allowed:p,lastModified:await this.getOperationClock(),tombstone:!1};return await(await this.db).put({_id:a,...g}),{...e,actor:o,url:a}};async*discoverMeta(t,e){if(e){let h=this.options.continueBuffer??2e3,y=Date.now()-e.lastDiscovered;y<h&&await new Promise(G=>setTimeout(G,h-y))}let[r,o,n]=t,a=I(await this.ajv,o),s=e?e.ifModifiedSince.toString().padStart(15,"0"):"",c="\uFFFF",p=new Set,g=await this.getOperationClock();for(let h of r){let y=encodeURIComponent(h)+"/",G=y+s,qt=y+c,Tt=await(await this.db).query("indexes/objectsPerChannelAndLastModified",{startkey:G,endkey:qt,include_docs:!0});for(let Dt of Tt.rows){let C=Dt.doc;if(!C)continue;let L=C._id;if(p.has(L)||(p.add(L),!e&&C.tombstone))continue;let{tombstone:Pt,value:It,channels:At,allowed:Ut}=C,{actor:Bt}=O(L),M={url:L,value:It,allowed:Ut,channels:At,actor:Bt};A(M,n)&&(U(M,r,n),a(M)&&(yield Pt?{tombstone:!0,object:{url:L}}:{object:M}))}}return{lastDiscovered:Date.now(),ifModifiedSince:g}}discoverCursor(t,e){let[r,o,n]=t;return"discover:"+JSON.stringify({channels:r,schema:o,continueParams:e,actor:n?.actor})}async*discoverContinue(t,e,r){if(r?.actor!==t[2]?.actor)throw new j("Cannot continue a cursor started by another actor");let o=this.discoverMeta(t,e);for(;;){let n=await o.next();if(n.done)return{continue:a=>this.discoverContinue(t,n.value,a),cursor:this.discoverCursor(t,n.value)};yield n.value}}discover=(...t)=>{let[e,r,o]=t,n=this.discoverMeta([e,r,o]),a=this;return(async function*(){for(;;){let s=await n.next();if(s.done)return{continue:c=>a.discoverContinue(t,s.value,c),cursor:a.discoverCursor(t,s.value)};s.value.tombstone||(yield s.value)}})()};continueDiscover=(...t)=>{let[e,r]=t;if(e.startsWith("discover:")){let{channels:o,schema:n,actor:a,continueParams:s}=JSON.parse(e.slice(9));if(a&&a!==r?.actor)throw new j("Cannot continue a cursor started by another actor");return this.discoverContinue([o,n,r],s)}else throw new b("Cursor not found")}};d();u();l();var jt=Rt(Mt()),ye={properties:{value:{properties:{dataBase64:{type:"string"},type:{type:"string"},size:{type:"number"}},required:["dataBase64","type","size"]}}},D=class{db;constructor(t){this.db=t}postMedia=async(...t)=>{let[e,r]=t,o=await at(e.data),n=e.data.type,{url:a}=await this.db.post({value:{dataBase64:o,type:n,size:e.data.size},channels:[],allowed:e.allowed},r),{actor:s,id:c}=O(a);return ot(s,c)};getMedia=async(...t)=>{let[e,r,o]=t,{actor:n,id:a}=R(e),s=x(n,a),c=await this.db.get(s,ye,o),{dataBase64:p,type:g,size:h}=c.value;if(r?.maxBytes&&h>r.maxBytes)throw new W("File size exceeds limit");if(r?.accept&&new jt.default({headers:{accept:r.accept}}).mediaType([g])!==g)throw new V(`Unsupported media type, ${g}`);let y=await st(p);if(y.size!==h||y.type!==g)throw new Error("Invalid data");return{data:y,actor:c.actor,allowed:c.allowed}};deleteMedia=async(...t)=>{let[e,r]=t,{actor:o,id:n}=R(e),a=x(o,n);await this.db.delete(a,r)}};var St=class{graffitiLocalIdentity=new q;login=this.graffitiLocalIdentity.login.bind(this.graffitiLocalIdentity);logout=this.graffitiLocalIdentity.logout.bind(this.graffitiLocalIdentity);handleToActor=this.graffitiLocalIdentity.handleToActor.bind(this.graffitiLocalIdentity);actorToHandle=this.graffitiLocalIdentity.actorToHandle.bind(this.graffitiLocalIdentity);sessionEvents=this.graffitiLocalIdentity.sessionEvents;graffitiLocalObjects;post;get;delete;discover;continueDiscover;graffitiLocalMedia;postMedia;getMedia;deleteMedia;constructor(t){this.graffitiLocalObjects=new T(t),this.post=this.graffitiLocalObjects.post.bind(this.graffitiLocalObjects),this.get=this.graffitiLocalObjects.get.bind(this.graffitiLocalObjects),this.delete=this.graffitiLocalObjects.delete.bind(this.graffitiLocalObjects),this.discover=this.graffitiLocalObjects.discover.bind(this.graffitiLocalObjects),this.continueDiscover=this.graffitiLocalObjects.continueDiscover.bind(this.graffitiLocalObjects),this.graffitiLocalMedia=new D(this.graffitiLocalObjects),this.postMedia=this.graffitiLocalMedia.postMedia.bind(this.graffitiLocalMedia),this.getMedia=this.graffitiLocalMedia.getMedia.bind(this.graffitiLocalMedia),this.deleteMedia=this.graffitiLocalMedia.deleteMedia.bind(this.graffitiLocalMedia)}};export{St as GraffitiLocal};
2
2
  /*! Bundled license information:
3
3
 
4
4
  negotiator/index.js:
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../node_modules/negotiator/lib/charset.js", "../../node_modules/negotiator/lib/encoding.js", "../../node_modules/negotiator/lib/language.js", "../../node_modules/negotiator/lib/mediaType.js", "../../node_modules/negotiator/index.js", "../../src/index.ts", "../../node_modules/@graffiti-garden/api/src/1-api.ts", "../../node_modules/@graffiti-garden/api/src/2-types.ts", "../../node_modules/@graffiti-garden/api/src/3-errors.ts", "../../node_modules/@graffiti-garden/api/src/4-utilities.ts", "../../src/identity.ts", "../../src/utilities.ts", "../../src/objects.ts", "../../src/media.ts"],
4
- "sourcesContent": ["/**\n * negotiator\n * Copyright(c) 2012 Isaac Z. Schlueter\n * Copyright(c) 2014 Federico Romero\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = preferredCharsets;\nmodule.exports.preferredCharsets = preferredCharsets;\n\n/**\n * Module variables.\n * @private\n */\n\nvar simpleCharsetRegExp = /^\\s*([^\\s;]+)\\s*(?:;(.*))?$/;\n\n/**\n * Parse the Accept-Charset header.\n * @private\n */\n\nfunction parseAcceptCharset(accept) {\n var accepts = accept.split(',');\n\n for (var i = 0, j = 0; i < accepts.length; i++) {\n var charset = parseCharset(accepts[i].trim(), i);\n\n if (charset) {\n accepts[j++] = charset;\n }\n }\n\n // trim accepts\n accepts.length = j;\n\n return accepts;\n}\n\n/**\n * Parse a charset from the Accept-Charset header.\n * @private\n */\n\nfunction parseCharset(str, i) {\n var match = simpleCharsetRegExp.exec(str);\n if (!match) return null;\n\n var charset = match[1];\n var q = 1;\n if (match[2]) {\n var params = match[2].split(';')\n for (var j = 0; j < params.length; j++) {\n var p = params[j].trim().split('=');\n if (p[0] === 'q') {\n q = parseFloat(p[1]);\n break;\n }\n }\n }\n\n return {\n charset: charset,\n q: q,\n i: i\n };\n}\n\n/**\n * Get the priority of a charset.\n * @private\n */\n\nfunction getCharsetPriority(charset, accepted, index) {\n var priority = {o: -1, q: 0, s: 0};\n\n for (var i = 0; i < accepted.length; i++) {\n var spec = specify(charset, accepted[i], index);\n\n if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {\n priority = spec;\n }\n }\n\n return priority;\n}\n\n/**\n * Get the specificity of the charset.\n * @private\n */\n\nfunction specify(charset, spec, index) {\n var s = 0;\n if(spec.charset.toLowerCase() === charset.toLowerCase()){\n s |= 1;\n } else if (spec.charset !== '*' ) {\n return null\n }\n\n return {\n i: index,\n o: spec.i,\n q: spec.q,\n s: s\n }\n}\n\n/**\n * Get the preferred charsets from an Accept-Charset header.\n * @public\n */\n\nfunction preferredCharsets(accept, provided) {\n // RFC 2616 sec 14.2: no header = *\n var accepts = parseAcceptCharset(accept === undefined ? '*' : accept || '');\n\n if (!provided) {\n // sorted list of all charsets\n return accepts\n .filter(isQuality)\n .sort(compareSpecs)\n .map(getFullCharset);\n }\n\n var priorities = provided.map(function getPriority(type, index) {\n return getCharsetPriority(type, accepts, index);\n });\n\n // sorted list of accepted charsets\n return priorities.filter(isQuality).sort(compareSpecs).map(function getCharset(priority) {\n return provided[priorities.indexOf(priority)];\n });\n}\n\n/**\n * Compare two specs.\n * @private\n */\n\nfunction compareSpecs(a, b) {\n return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;\n}\n\n/**\n * Get full charset string.\n * @private\n */\n\nfunction getFullCharset(spec) {\n return spec.charset;\n}\n\n/**\n * Check if a spec has any quality.\n * @private\n */\n\nfunction isQuality(spec) {\n return spec.q > 0;\n}\n", "/**\n * negotiator\n * Copyright(c) 2012 Isaac Z. Schlueter\n * Copyright(c) 2014 Federico Romero\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = preferredEncodings;\nmodule.exports.preferredEncodings = preferredEncodings;\n\n/**\n * Module variables.\n * @private\n */\n\nvar simpleEncodingRegExp = /^\\s*([^\\s;]+)\\s*(?:;(.*))?$/;\n\n/**\n * Parse the Accept-Encoding header.\n * @private\n */\n\nfunction parseAcceptEncoding(accept) {\n var accepts = accept.split(',');\n var hasIdentity = false;\n var minQuality = 1;\n\n for (var i = 0, j = 0; i < accepts.length; i++) {\n var encoding = parseEncoding(accepts[i].trim(), i);\n\n if (encoding) {\n accepts[j++] = encoding;\n hasIdentity = hasIdentity || specify('identity', encoding);\n minQuality = Math.min(minQuality, encoding.q || 1);\n }\n }\n\n if (!hasIdentity) {\n /*\n * If identity doesn't explicitly appear in the accept-encoding header,\n * it's added to the list of acceptable encoding with the lowest q\n */\n accepts[j++] = {\n encoding: 'identity',\n q: minQuality,\n i: i\n };\n }\n\n // trim accepts\n accepts.length = j;\n\n return accepts;\n}\n\n/**\n * Parse an encoding from the Accept-Encoding header.\n * @private\n */\n\nfunction parseEncoding(str, i) {\n var match = simpleEncodingRegExp.exec(str);\n if (!match) return null;\n\n var encoding = match[1];\n var q = 1;\n if (match[2]) {\n var params = match[2].split(';');\n for (var j = 0; j < params.length; j++) {\n var p = params[j].trim().split('=');\n if (p[0] === 'q') {\n q = parseFloat(p[1]);\n break;\n }\n }\n }\n\n return {\n encoding: encoding,\n q: q,\n i: i\n };\n}\n\n/**\n * Get the priority of an encoding.\n * @private\n */\n\nfunction getEncodingPriority(encoding, accepted, index) {\n var priority = {encoding: encoding, o: -1, q: 0, s: 0};\n\n for (var i = 0; i < accepted.length; i++) {\n var spec = specify(encoding, accepted[i], index);\n\n if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {\n priority = spec;\n }\n }\n\n return priority;\n}\n\n/**\n * Get the specificity of the encoding.\n * @private\n */\n\nfunction specify(encoding, spec, index) {\n var s = 0;\n if(spec.encoding.toLowerCase() === encoding.toLowerCase()){\n s |= 1;\n } else if (spec.encoding !== '*' ) {\n return null\n }\n\n return {\n encoding: encoding,\n i: index,\n o: spec.i,\n q: spec.q,\n s: s\n }\n};\n\n/**\n * Get the preferred encodings from an Accept-Encoding header.\n * @public\n */\n\nfunction preferredEncodings(accept, provided, preferred) {\n var accepts = parseAcceptEncoding(accept || '');\n\n var comparator = preferred ? function comparator (a, b) {\n if (a.q !== b.q) {\n return b.q - a.q // higher quality first\n }\n\n var aPreferred = preferred.indexOf(a.encoding)\n var bPreferred = preferred.indexOf(b.encoding)\n\n if (aPreferred === -1 && bPreferred === -1) {\n // consider the original specifity/order\n return (b.s - a.s) || (a.o - b.o) || (a.i - b.i)\n }\n\n if (aPreferred !== -1 && bPreferred !== -1) {\n return aPreferred - bPreferred // consider the preferred order\n }\n\n return aPreferred === -1 ? 1 : -1 // preferred first\n } : compareSpecs;\n\n if (!provided) {\n // sorted list of all encodings\n return accepts\n .filter(isQuality)\n .sort(comparator)\n .map(getFullEncoding);\n }\n\n var priorities = provided.map(function getPriority(type, index) {\n return getEncodingPriority(type, accepts, index);\n });\n\n // sorted list of accepted encodings\n return priorities.filter(isQuality).sort(comparator).map(function getEncoding(priority) {\n return provided[priorities.indexOf(priority)];\n });\n}\n\n/**\n * Compare two specs.\n * @private\n */\n\nfunction compareSpecs(a, b) {\n return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i);\n}\n\n/**\n * Get full encoding string.\n * @private\n */\n\nfunction getFullEncoding(spec) {\n return spec.encoding;\n}\n\n/**\n * Check if a spec has any quality.\n * @private\n */\n\nfunction isQuality(spec) {\n return spec.q > 0;\n}\n", "/**\n * negotiator\n * Copyright(c) 2012 Isaac Z. Schlueter\n * Copyright(c) 2014 Federico Romero\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = preferredLanguages;\nmodule.exports.preferredLanguages = preferredLanguages;\n\n/**\n * Module variables.\n * @private\n */\n\nvar simpleLanguageRegExp = /^\\s*([^\\s\\-;]+)(?:-([^\\s;]+))?\\s*(?:;(.*))?$/;\n\n/**\n * Parse the Accept-Language header.\n * @private\n */\n\nfunction parseAcceptLanguage(accept) {\n var accepts = accept.split(',');\n\n for (var i = 0, j = 0; i < accepts.length; i++) {\n var language = parseLanguage(accepts[i].trim(), i);\n\n if (language) {\n accepts[j++] = language;\n }\n }\n\n // trim accepts\n accepts.length = j;\n\n return accepts;\n}\n\n/**\n * Parse a language from the Accept-Language header.\n * @private\n */\n\nfunction parseLanguage(str, i) {\n var match = simpleLanguageRegExp.exec(str);\n if (!match) return null;\n\n var prefix = match[1]\n var suffix = match[2]\n var full = prefix\n\n if (suffix) full += \"-\" + suffix;\n\n var q = 1;\n if (match[3]) {\n var params = match[3].split(';')\n for (var j = 0; j < params.length; j++) {\n var p = params[j].split('=');\n if (p[0] === 'q') q = parseFloat(p[1]);\n }\n }\n\n return {\n prefix: prefix,\n suffix: suffix,\n q: q,\n i: i,\n full: full\n };\n}\n\n/**\n * Get the priority of a language.\n * @private\n */\n\nfunction getLanguagePriority(language, accepted, index) {\n var priority = {o: -1, q: 0, s: 0};\n\n for (var i = 0; i < accepted.length; i++) {\n var spec = specify(language, accepted[i], index);\n\n if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {\n priority = spec;\n }\n }\n\n return priority;\n}\n\n/**\n * Get the specificity of the language.\n * @private\n */\n\nfunction specify(language, spec, index) {\n var p = parseLanguage(language)\n if (!p) return null;\n var s = 0;\n if(spec.full.toLowerCase() === p.full.toLowerCase()){\n s |= 4;\n } else if (spec.prefix.toLowerCase() === p.full.toLowerCase()) {\n s |= 2;\n } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {\n s |= 1;\n } else if (spec.full !== '*' ) {\n return null\n }\n\n return {\n i: index,\n o: spec.i,\n q: spec.q,\n s: s\n }\n};\n\n/**\n * Get the preferred languages from an Accept-Language header.\n * @public\n */\n\nfunction preferredLanguages(accept, provided) {\n // RFC 2616 sec 14.4: no header = *\n var accepts = parseAcceptLanguage(accept === undefined ? '*' : accept || '');\n\n if (!provided) {\n // sorted list of all languages\n return accepts\n .filter(isQuality)\n .sort(compareSpecs)\n .map(getFullLanguage);\n }\n\n var priorities = provided.map(function getPriority(type, index) {\n return getLanguagePriority(type, accepts, index);\n });\n\n // sorted list of accepted languages\n return priorities.filter(isQuality).sort(compareSpecs).map(function getLanguage(priority) {\n return provided[priorities.indexOf(priority)];\n });\n}\n\n/**\n * Compare two specs.\n * @private\n */\n\nfunction compareSpecs(a, b) {\n return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;\n}\n\n/**\n * Get full language string.\n * @private\n */\n\nfunction getFullLanguage(spec) {\n return spec.full;\n}\n\n/**\n * Check if a spec has any quality.\n * @private\n */\n\nfunction isQuality(spec) {\n return spec.q > 0;\n}\n", "/**\n * negotiator\n * Copyright(c) 2012 Isaac Z. Schlueter\n * Copyright(c) 2014 Federico Romero\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = preferredMediaTypes;\nmodule.exports.preferredMediaTypes = preferredMediaTypes;\n\n/**\n * Module variables.\n * @private\n */\n\nvar simpleMediaTypeRegExp = /^\\s*([^\\s\\/;]+)\\/([^;\\s]+)\\s*(?:;(.*))?$/;\n\n/**\n * Parse the Accept header.\n * @private\n */\n\nfunction parseAccept(accept) {\n var accepts = splitMediaTypes(accept);\n\n for (var i = 0, j = 0; i < accepts.length; i++) {\n var mediaType = parseMediaType(accepts[i].trim(), i);\n\n if (mediaType) {\n accepts[j++] = mediaType;\n }\n }\n\n // trim accepts\n accepts.length = j;\n\n return accepts;\n}\n\n/**\n * Parse a media type from the Accept header.\n * @private\n */\n\nfunction parseMediaType(str, i) {\n var match = simpleMediaTypeRegExp.exec(str);\n if (!match) return null;\n\n var params = Object.create(null);\n var q = 1;\n var subtype = match[2];\n var type = match[1];\n\n if (match[3]) {\n var kvps = splitParameters(match[3]).map(splitKeyValuePair);\n\n for (var j = 0; j < kvps.length; j++) {\n var pair = kvps[j];\n var key = pair[0].toLowerCase();\n var val = pair[1];\n\n // get the value, unwrapping quotes\n var value = val && val[0] === '\"' && val[val.length - 1] === '\"'\n ? val.slice(1, -1)\n : val;\n\n if (key === 'q') {\n q = parseFloat(value);\n break;\n }\n\n // store parameter\n params[key] = value;\n }\n }\n\n return {\n type: type,\n subtype: subtype,\n params: params,\n q: q,\n i: i\n };\n}\n\n/**\n * Get the priority of a media type.\n * @private\n */\n\nfunction getMediaTypePriority(type, accepted, index) {\n var priority = {o: -1, q: 0, s: 0};\n\n for (var i = 0; i < accepted.length; i++) {\n var spec = specify(type, accepted[i], index);\n\n if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {\n priority = spec;\n }\n }\n\n return priority;\n}\n\n/**\n * Get the specificity of the media type.\n * @private\n */\n\nfunction specify(type, spec, index) {\n var p = parseMediaType(type);\n var s = 0;\n\n if (!p) {\n return null;\n }\n\n if(spec.type.toLowerCase() == p.type.toLowerCase()) {\n s |= 4\n } else if(spec.type != '*') {\n return null;\n }\n\n if(spec.subtype.toLowerCase() == p.subtype.toLowerCase()) {\n s |= 2\n } else if(spec.subtype != '*') {\n return null;\n }\n\n var keys = Object.keys(spec.params);\n if (keys.length > 0) {\n if (keys.every(function (k) {\n return spec.params[k] == '*' || (spec.params[k] || '').toLowerCase() == (p.params[k] || '').toLowerCase();\n })) {\n s |= 1\n } else {\n return null\n }\n }\n\n return {\n i: index,\n o: spec.i,\n q: spec.q,\n s: s,\n }\n}\n\n/**\n * Get the preferred media types from an Accept header.\n * @public\n */\n\nfunction preferredMediaTypes(accept, provided) {\n // RFC 2616 sec 14.2: no header = */*\n var accepts = parseAccept(accept === undefined ? '*/*' : accept || '');\n\n if (!provided) {\n // sorted list of all types\n return accepts\n .filter(isQuality)\n .sort(compareSpecs)\n .map(getFullType);\n }\n\n var priorities = provided.map(function getPriority(type, index) {\n return getMediaTypePriority(type, accepts, index);\n });\n\n // sorted list of accepted types\n return priorities.filter(isQuality).sort(compareSpecs).map(function getType(priority) {\n return provided[priorities.indexOf(priority)];\n });\n}\n\n/**\n * Compare two specs.\n * @private\n */\n\nfunction compareSpecs(a, b) {\n return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;\n}\n\n/**\n * Get full type string.\n * @private\n */\n\nfunction getFullType(spec) {\n return spec.type + '/' + spec.subtype;\n}\n\n/**\n * Check if a spec has any quality.\n * @private\n */\n\nfunction isQuality(spec) {\n return spec.q > 0;\n}\n\n/**\n * Count the number of quotes in a string.\n * @private\n */\n\nfunction quoteCount(string) {\n var count = 0;\n var index = 0;\n\n while ((index = string.indexOf('\"', index)) !== -1) {\n count++;\n index++;\n }\n\n return count;\n}\n\n/**\n * Split a key value pair.\n * @private\n */\n\nfunction splitKeyValuePair(str) {\n var index = str.indexOf('=');\n var key;\n var val;\n\n if (index === -1) {\n key = str;\n } else {\n key = str.slice(0, index);\n val = str.slice(index + 1);\n }\n\n return [key, val];\n}\n\n/**\n * Split an Accept header into media types.\n * @private\n */\n\nfunction splitMediaTypes(accept) {\n var accepts = accept.split(',');\n\n for (var i = 1, j = 0; i < accepts.length; i++) {\n if (quoteCount(accepts[j]) % 2 == 0) {\n accepts[++j] = accepts[i];\n } else {\n accepts[j] += ',' + accepts[i];\n }\n }\n\n // trim accepts\n accepts.length = j + 1;\n\n return accepts;\n}\n\n/**\n * Split a string of parameters.\n * @private\n */\n\nfunction splitParameters(str) {\n var parameters = str.split(';');\n\n for (var i = 1, j = 0; i < parameters.length; i++) {\n if (quoteCount(parameters[j]) % 2 == 0) {\n parameters[++j] = parameters[i];\n } else {\n parameters[j] += ';' + parameters[i];\n }\n }\n\n // trim parameters\n parameters.length = j + 1;\n\n for (var i = 0; i < parameters.length; i++) {\n parameters[i] = parameters[i].trim();\n }\n\n return parameters;\n}\n", "/*!\n * negotiator\n * Copyright(c) 2012 Federico Romero\n * Copyright(c) 2012-2014 Isaac Z. Schlueter\n * Copyright(c) 2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\nvar preferredCharsets = require('./lib/charset')\nvar preferredEncodings = require('./lib/encoding')\nvar preferredLanguages = require('./lib/language')\nvar preferredMediaTypes = require('./lib/mediaType')\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = Negotiator;\nmodule.exports.Negotiator = Negotiator;\n\n/**\n * Create a Negotiator instance from a request.\n * @param {object} request\n * @public\n */\n\nfunction Negotiator(request) {\n if (!(this instanceof Negotiator)) {\n return new Negotiator(request);\n }\n\n this.request = request;\n}\n\nNegotiator.prototype.charset = function charset(available) {\n var set = this.charsets(available);\n return set && set[0];\n};\n\nNegotiator.prototype.charsets = function charsets(available) {\n return preferredCharsets(this.request.headers['accept-charset'], available);\n};\n\nNegotiator.prototype.encoding = function encoding(available, opts) {\n var set = this.encodings(available, opts);\n return set && set[0];\n};\n\nNegotiator.prototype.encodings = function encodings(available, options) {\n var opts = options || {};\n return preferredEncodings(this.request.headers['accept-encoding'], available, opts.preferred);\n};\n\nNegotiator.prototype.language = function language(available) {\n var set = this.languages(available);\n return set && set[0];\n};\n\nNegotiator.prototype.languages = function languages(available) {\n return preferredLanguages(this.request.headers['accept-language'], available);\n};\n\nNegotiator.prototype.mediaType = function mediaType(available) {\n var set = this.mediaTypes(available);\n return set && set[0];\n};\n\nNegotiator.prototype.mediaTypes = function mediaTypes(available) {\n return preferredMediaTypes(this.request.headers.accept, available);\n};\n\n// Backwards compatibility\nNegotiator.prototype.preferredCharset = Negotiator.prototype.charset;\nNegotiator.prototype.preferredCharsets = Negotiator.prototype.charsets;\nNegotiator.prototype.preferredEncoding = Negotiator.prototype.encoding;\nNegotiator.prototype.preferredEncodings = Negotiator.prototype.encodings;\nNegotiator.prototype.preferredLanguage = Negotiator.prototype.language;\nNegotiator.prototype.preferredLanguages = Negotiator.prototype.languages;\nNegotiator.prototype.preferredMediaType = Negotiator.prototype.mediaType;\nNegotiator.prototype.preferredMediaTypes = Negotiator.prototype.mediaTypes;\n", "import { Graffiti, type GraffitiSession } from \"@graffiti-garden/api\";\nimport { GraffitiLocalIdentity } from \"./identity\";\nimport { GraffitiLocalObjects, type GraffitiLocalOptions } from \"./objects\";\nimport { GraffitiLocalMedia } from \"./media\";\n\nexport type { GraffitiLocalOptions };\n\n/**\n * A local implementation of the [Graffiti API](https://api.graffiti.garden/classes/Graffiti.html)\n * based on [PouchDB](https://pouchdb.com/). PouchDb will automatically persist data in a local\n * database, either in the browser or in Node.js.\n * It can also be configured to work with an external [CouchDB](https://couchdb.apache.org/) server,\n * although using it with a remote server will not be secure.\n */\nexport class GraffitiLocal implements Graffiti {\n protected graffitiLocalIdentity = new GraffitiLocalIdentity();\n login = this.graffitiLocalIdentity.login.bind(this.graffitiLocalIdentity);\n logout = this.graffitiLocalIdentity.logout.bind(this.graffitiLocalIdentity);\n handleToActor = this.graffitiLocalIdentity.handleToActor.bind(\n this.graffitiLocalIdentity,\n );\n actorToHandle = this.graffitiLocalIdentity.actorToHandle.bind(\n this.graffitiLocalIdentity,\n );\n sessionEvents = this.graffitiLocalIdentity.sessionEvents;\n\n protected graffitiLocalObjects: GraffitiLocalObjects;\n post: Graffiti[\"post\"];\n get: Graffiti[\"get\"];\n delete: Graffiti[\"delete\"];\n discover: Graffiti[\"discover\"];\n continueDiscover: Graffiti[\"continueDiscover\"];\n\n protected graffitiLocalMedia: GraffitiLocalMedia;\n postMedia: Graffiti[\"postMedia\"];\n getMedia: Graffiti[\"getMedia\"];\n deleteMedia: Graffiti[\"deleteMedia\"];\n\n constructor(options?: GraffitiLocalOptions) {\n this.graffitiLocalObjects = new GraffitiLocalObjects(options);\n this.post = this.graffitiLocalObjects.post.bind(this.graffitiLocalObjects);\n this.get = this.graffitiLocalObjects.get.bind(this.graffitiLocalObjects);\n this.delete = this.graffitiLocalObjects.delete.bind(\n this.graffitiLocalObjects,\n );\n this.discover = this.graffitiLocalObjects.discover.bind(\n this.graffitiLocalObjects,\n );\n this.continueDiscover = this.graffitiLocalObjects.continueDiscover.bind(\n this.graffitiLocalObjects,\n );\n\n this.graffitiLocalMedia = new GraffitiLocalMedia(this.graffitiLocalObjects);\n this.postMedia = this.graffitiLocalMedia.postMedia.bind(\n this.graffitiLocalMedia,\n );\n this.getMedia = this.graffitiLocalMedia.getMedia.bind(\n this.graffitiLocalMedia,\n );\n this.deleteMedia = this.graffitiLocalMedia.deleteMedia.bind(\n this.graffitiLocalMedia,\n );\n }\n}\n", "import type {\n GraffitiObjectUrl,\n GraffitiObject,\n GraffitiObjectBase,\n GraffitiSession,\n GraffitiPostObject,\n GraffitiObjectStream,\n GraffitiObjectStreamContinue,\n} from \"./2-types\";\nimport type { JSONSchema } from \"json-schema-to-ts\";\n\n/**\n * This API describes a small but powerful set of methods that\n * can be used to create many different kinds of social applications,\n * from applications like Twitter, to Messenger, to Wikipedia, to many more new designs.\n * See the [Graffiti project website](https://graffiti.garden)\n * for links to example applications. Additionally, apps built on top\n * of the API interoperate with each other so you can seamlessly switch\n * between apps without losing your friends or data.\n *\n * These API methods should satisfy all of an application's needs for\n * the communication, storage, and access management of social data.\n * The rest of the application can be built with standard client-side\n * user interface tools to present and interact with that data\u2014no server code necessary!\n *\n * The Typescript code for this API is [open source on Github](https://github.com/graffiti-garden/api).\n *\n * There are several different implementations of this Graffiti API available,\n * including a [federated implementation](https://github.com/graffiti-garden/implementation-remote),\n * that lets people choose where their data is stored (you do not need to host your own server)\n * and a [local implementation](https://github.com/graffiti-garden/implementation-local)\n * that can be used for testing and development. Different implementations can\n * be swapped-in in the future without changing the API or any of the apps built on\n * top of it. In fact, we're working on an end-to-end encrypted version now!\n * [Follow Theia on BlueSky for updates](https://bsky.app/profile/theias.place).\n *\n * On the other side of the stack, there is [Vue plugin](https://vue.graffiti.garden/variables/GraffitiPlugin.html)\n * that wraps around this API to provide reactivity. Other plugin frameworks\n * and high-level libraries will be available in the future.\n *\n * ## API Overview\n *\n * The Graffiti API provides applications with methods for {@link login} and {@link logout},\n * methods to interact with data objects using standard database operations ({@link post}, {@link get}, and {@link delete}),\n * and a method to {@link discover} data objects created by others.\n * These data objects have a couple structured properties:\n * - {@link GraffitiObjectBase.url | `url`} (string): A globally unique identifier and locator for the object.\n * - {@link GraffitiObjectBase.actor | `actor`} (string): An unforgeable identifier for the creator of the object.\n * - {@link GraffitiObjectBase.allowed | `allowed`} (string[] | undefined): An array of the actors who are allowed to access the object (undefined for public objects).\n * - {@link GraffitiObjectBase.channels | `channels`} (string[]): An array of the *contexts* in which the object should appear.\n *\n * All other data is stored in the object's unstructured {@link GraffitiObjectBase.value | `value`} property.\n * This data can be used to represent social artifacts (e.g. posts, profiles) and activities (e.g. likes, follows).\n * For example, a post might have the value:\n\n * ```js\n * {\n * title: \"My First Post\",\n * content: \"Hello, world!\",\n * published: 1630483200000\n * }\n * ```\n *\n * a profile might have the value:\n *\n * ```js\n * {\n * name: \"Theia Henderson\",\n * pronouns: \"she/her\",\n * describes: \"did:web:theias.place\" // Theia's actor ID\n * }\n * ```\n *\n * and a \"Like\" might have the value:\n *\n * ```js\n * {\n * activity: \"Like\",\n * target: \"graffiti:remote:pod.graffiti.garden/12345\" // The URL of the graffiti object being liked\n * }\n * ```\n *\n * New social artifacts and activities can be easily created, simply\n * by creating new objects with appropriate properties. Despite the lack of\n * structure, we expect Graffiti object properties to adhere to a \"[folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\",\n * similar to hashtags. Any string can be used as a hashtag on Twitter,\n * but there is social value in using the same hashtags at other people and\n * so a structure naturally emerges. Similarly, Graffiti objects\n * can have arbitrary properties but if people use the same properties as each other,\n * their apps will interoperate, which has social value.\n *\n * For a more complete and detailed overview of Graffiti's design, please\n * refer to [this section of the Graffiti paper](https://dl.acm.org/doi/10.1145/3746059.3747627#sec-3),\n * published in ACM UIST 2025. The paper also overviews {@link GraffitiObjectBase.channels | `channels`},\n * which are Graffiti's means of organizing data contextually, and a concept called \"total reification\",\n * which handles explains how moderation, collaboration, and other interactions are managed.\n *\n * @groupDescription 1 - Single-Object Methods\n * Methods for {@link post | creating}, {@link get | reading},\n * and {@link delete | deleting} {@link GraffitiObjectBase | Graffiti objects}.\n * @groupDescription 2 - Multi-Object Methods\n * Methods that retrieve or accumulate information about multiple {@link GraffitiObjectBase | Graffiti objects} at a time.\n * @groupDescription 3 - Media Methods\n * Methods for {@link postMedia | creating}, {@link getMedia | reading},\n * and {@link deleteMedia | deleting} media data.\n * @groupDescription 4 - Identity Methods\n * Methods and properties for logging in and out.\n */\nexport abstract class Graffiti {\n /**\n * Creates a new {@link GraffitiObjectBase | object}.\n *\n * @returns Returns the object that has been posted, complete with its\n * assigned {@link GraffitiObjectBase.url | `url`} and\n * {@link GraffitiObjectBase.actor | `actor`}.\n *\n * @group 1 - Single-Object Methods\n */\n abstract post<Schema extends JSONSchema>(\n /**\n * An object to post, minus its {@link GraffitiObjectBase.url | `url`} and\n * {@link GraffitiObjectBase.actor | `actor`}, which will be assigned once posted.\n * This object is statically type-checked against the [JSON schema](https://json-schema.org/) that can be optionally provided\n * as the generic type parameter. It is recommended to a schema to\n * ensure that the posted object matches subsequent {@link get} or {@link discover}\n * methods.\n */\n partialObject: GraffitiPostObject<Schema>,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<\n GraffitiPostObject<Schema> & {\n url: string;\n actor: string;\n }\n >;\n\n /**\n * Retrieves an object from a given {@link GraffitiObjectBase.url | `url`} matching\n * the provided `schema`.\n *\n * If the retreiving {@link GraffitiObjectBase.actor | `actor`} is not\n * the object's `actor`,\n * the object's {@link GraffitiObjectBase.allowed | `allowed`} and\n * {@link GraffitiObjectBase.channels | `channels`} properties are\n * not revealed, similar to a BCC email.\n *\n * @returns Returns the retrieved object.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has been deleted, or the actor is not\n * {@link GraffitiObjectBase.allowed | `allowed`} to access it.\n *\n * @throws {@link GraffitiErrorSchemaMismatch} if the retrieved object does not match the provided schema.\n *\n * @group 1 - Single-Object Methods\n */\n abstract get<Schema extends JSONSchema>(\n /**\n * The location of the object to get.\n */\n url: string | GraffitiObjectUrl,\n /**\n * The JSON schema to validate the retrieved object against.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}. If no `session` is provided,\n * the retrieved object's {@link GraffitiObjectBase.allowed | `allowed`}\n * property must be `undefined`.\n */\n session?: GraffitiSession | null,\n ): Promise<GraffitiObject<Schema>>;\n\n /**\n * Deletes an object from a given {@link GraffitiObjectBase.url | `url`}\n * that had previously been {@link post | `post`ed}.\n * The deleting {@link GraffitiObjectBase.actor | `actor`} must be the same as the\n * `actor` that created the object.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,\n * or the actor is not {@link GraffitiObjectBase.allowed | `allowed`} to access it.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * is not the same `actor` as the one who created the object.\n *\n * @group 1 - Single-Object Methods\n */\n abstract delete(\n /**\n * The location of the object to delete.\n */\n url: string | GraffitiObjectUrl,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * Discovers objects created by any actor that are contained\n * in at least one of the given {@link GraffitiObjectBase.channels | `channels`}\n * and match the given [JSON Schema](https://json-schema.org).\n *\n * Objects are returned asynchronously as they are discovered but the stream\n * will end once all leads have been exhausted.\n * The {@link GraffitiObjectStream} ends by returning a\n * {@link GraffitiObjectStreamReturn.continue | `continue`} method and a\n * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string,\n * each of which can be be used to poll for new objects.\n * The `continue` method preserves the type safety of the stream and the `cursor`\n * string can be serialized to continue the stream after an application is closed\n * and reopened.\n *\n * `discover` will not return objects that the querying {@link GraffitiObjectBase.actor | `actor`}\n * is not {@link GraffitiObjectBase.allowed | `allowed`} to access.\n * If the `actor` is not the creator of a discovered object,\n * the allowed list will be masked to only contain the querying actor if the\n * allowed list is not `undefined` (public). Additionally, if the actor is not the\n * creator of a discovered object, any {@link GraffitiObjectBase.channels | `channels`}\n * not specified by the `discover` method will not be revealed. This masking happens\n * before the object is validated against the supplied `schema`.\n *\n * Since different implementations may fetch data from multiple sources there is\n * no guarentee on the order that objects are returned in.\n *\n * @returns Returns a stream of objects that match the given {@link GraffitiObjectBase.channels | `channels`}\n * and [JSON Schema](https://json-schema.org).\n *\n * @group 2 - Multi-Object Methods\n */\n abstract discover<Schema extends JSONSchema>(\n /**\n * The {@link GraffitiObjectBase.channels | `channels`} that objects must be associated with.\n */\n channels: string[],\n /**\n * A [JSON Schema](https://json-schema.org) that objects must satisfy.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}. If no `session` is provided,\n * only objects that have no {@link GraffitiObjectBase.allowed | `allowed`}\n * property will be returned.\n */\n session?: GraffitiSession | null,\n ): GraffitiObjectStream<Schema>;\n\n /**\n * Continues a {@link GraffitiObjectStream} from a given\n * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string.\n * The continuation will return new objects that have been {@link post | `post`ed}\n * that match the original stream, and also returns the\n * {@link GraffitiObjectBase.url | `url`}s of objects that\n * have been {@link delete | `delete`d}, as marked by a `tombstone`.\n *\n * The `cursor` allows the client to\n * serialize the state of the stream and continue it later.\n * However this method loses any typing information that was\n * present in the original stream. For better type safety\n * and when serializing is not necessary, use the\n * {@link GraffitiObjectStreamReturn.continue | `continue`} method\n * instead, which is returned along with the `cursor` at the\n * end of the original stream.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * provided in the `session` is not the same as the `actor`\n * that initiated the original stream.\n *\n * @group 2 - Multi-Object Methods\n */\n abstract continueDiscover(\n cursor: string,\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<{}>;\n\n /**\n * Uploads media data, such as an image or video.\n *\n * Unlike structured {@link GraffitiObjectBase | objects},\n * media is not indexed for {@link discover | `discover`y} and\n * must be retrieved by its exact URL using {@link getMedia}\n *\n * @returns The URL that the media was posted to.\n *\n * @group 3 - Media Methods\n */\n abstract postMedia(\n media: {\n /**\n * The binary data of the media to be uploaded,\n * along with its [media type](https://www.iana.org/assignments/media-types/media-types.xhtml),\n * formatted as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob).\n */\n data: Blob;\n /**\n * An optional list, identical in function to an object's\n * {@link GraffitiObjectBase.allowed | `allowed`} property,\n * that specifies the {@link GraffitiObjectBase.actor | `actor`}s\n * who are allowed to access the media. If the list is `undefined`\n * or `null`, anyone with the URL can access the media. If the list\n * is empty, only the {@link GraffitiObjectBase.actor | `actor`}\n * who {@link postMedia | `post`ed} the media can access it.\n */\n allowed?: string[] | null;\n },\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<string>;\n\n /**\n * Deletes media previously {@link postMedia | `post`ed} to a given URL.\n *\n * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * provided in the `session` is not the same as the `actor` that {@link postMedia | `post`ed}\n * the media.\n *\n * @group 3 - Media Methods\n */\n abstract deleteMedia(\n /**\n * A globally unique identifier and locator for the media.\n */\n mediaUrl: string,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * Retrieves media from the given media URL, adhering to the given requirements.\n *\n * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.\n *\n * @throws {@link GraffitiErrorTooLarge} if the media exceeds the given `maxBytes`.\n *\n * @throws {@link GraffitiErrorNotAcceptable} if the media does not match the given\n * `accept` specification.\n *\n * @returns The URL of the retrieved media, as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob)\n * and the {@link GraffitiObjectBase.actor | `actor`} that posted it.\n *\n * @group 3 - Media Methods\n */\n abstract getMedia(\n /**\n * A globally unique identifier and locator for the media.\n */\n mediaUrl: string,\n /**\n * A set of requirements the retrieved media must meet.\n */\n requirements: {\n /**\n * A list of acceptable media types for the retrieved media,\n * formatted as like an [HTTP Accept header](https://httpwg.org/specs/rfc9110.html#field.accept)\n */\n accept?: string;\n /**\n * The maximum acceptable size, in bytes, of the media.\n */\n maxBytes?: number;\n },\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session?: GraffitiSession | null,\n ): Promise<{\n data: Blob;\n actor: string;\n allowed?: string[] | null;\n }>;\n\n /**\n * Begins the login process. Depending on the implementation, this may\n * involve redirecting to a login page or opening a popup,\n * so it should always be called in response to a gesture, such as clicking\n * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).\n *\n * The {@link GraffitiSession | session} object is returned\n * asynchronously via {@link Graffiti.sessionEvents | sessionEvents}\n * as a {@link GraffitiLoginEvent} with event type `login`.\n *\n * @group 4 - Identity Methods\n */\n abstract login(\n /**\n * A suggested actor to login as. For example, if a user tries to\n * edit a post but are not logged in, the interface can infer that\n * they might want to log in as the actor who created the post\n * they are attempting to edit.\n *\n * Even if provided, the implementation should allow the user\n * to log in as a different actor if they choose.\n */\n actor?: string,\n ): Promise<void>;\n\n /**\n * Begins the logout process for a particular {@link GraffitiSession | session}. Depending on the implementation, this may\n * involve redirecting the user to a logout page or opening a popup,\n * so it should always be called in response to a gesture, such as clicking\n * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).\n *\n * A confirmation will be returned asynchronously via\n * {@link Graffiti.sessionEvents | sessionEvents}\n * as a {@link GraffitiLogoutEvent} as event type `logout`.\n *\n * @group 4 - Identity Methods\n */\n abstract logout(\n /**\n * The {@link GraffitiSession | session} object to logout.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * An event target that can be used to listen for the following\n * events and their corresponding event types:\n * - `login` - {@link GraffitiLoginEvent}\n * - `logout` - {@link GraffitiLogoutEvent}\n * - `initialized` - {@link GraffitiSessionInitializedEvent}\n *\n * @group 4 - Identity Methods\n */\n abstract readonly sessionEvents: EventTarget;\n\n /**\n * Retrieves the human-readable handle associated\n * with the given actor. The handle may change over time\n * and so it should be used for display purposes only.\n *\n * The inverse of {@link handleToActor}.\n *\n * @throws {@link GraffitiErrorNotFound} if a handle cannot be\n * found for the given actor.\n *\n * @returns A human-readable handle for the given actor.\n *\n * @group 4 - Identity Methods\n */\n abstract actorToHandle(actor: string): Promise<string>;\n\n /**\n * Retrieves the actor ID associated with the given handle.\n *\n * The inverse of {@link actorToHandle}.\n *\n * @throws {@link GraffitiErrorNotFound} if there is no actor\n * with the given handle.\n *\n * @returns The actor ID for the given handle.\n *\n * @group 4 - Identity Methods\n */\n abstract handleToActor(handle: string): Promise<string>;\n}\n", "import type { JSONSchema, FromSchema } from \"json-schema-to-ts\";\n\n/**\n * Objects are the atomic unit in Graffiti that can represent both data (*e.g.* a social media post or profile)\n * and activities (*e.g.* a like or follow).\n *\n * Each object embeds the {@link actor | `actor`} that created it.\n * Object content and metadata are static but an object may be deleted by\n * its creating {@link actor | `actor`}.\n *\n * An object's content is stored in its {@link value | `value`} property, which can be any JSON\n * object. However, it is recommended to use properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)\n * or properties that emerge in the Graffiti [folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\n * to promote interoperability.\n *\n * Each object is globally addressable via its {@link url | `url`}.\n *\n * An object's {@link channels | `channels`} and {@link allowed | `allowed`} properties\n * are set by an objects creator to shape the visibility of and access to their object.\n */\nexport interface GraffitiObjectBase {\n /**\n * The object's content as freeform JSON. We recommend using properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)\n * or properties that emerge in the Graffiti [folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\n * to promote interoperability.\n */\n value: {};\n\n /**\n * An array of URIs the creator associates with the object. Objects can only be found by querying\n * one of the object's channels using the\n * {@link Graffiti.discover} method. This allows creators to express the intended audience of their object\n * which helps to prevent [context collapse](https://en.wikipedia.org/wiki/Context_collapse) even\n * in the highly interoperable ecosystem that Graffiti envisions. For example, channel URIs may be:\n * - A actor's own {@link actor | `actor`} URI. Posting an object to this channel is a way to broadcast\n * the object to the actor's followers, like posting a tweet.\n * - The URL of a Graffiti post. Posting an object to this channel is a way to broadcast to anyone viewing\n * the post, like commenting on a tweet.\n * - A URI representing a topic. Posting an object to this channel is a way to broadcast to anyone interested\n * in that topic, like posting in a subreddit.\n */\n channels: string[];\n\n /**\n * An optional array of {@link actor | `actor`} URIs that the creator allows to access the object.\n * If no `allowed` array is provided, the object can be accessed by anyone (so long as they\n * also know the right {@link channels | `channel` } to look in). An object can always be accessed by its creator, even if\n * the `allowed` array is empty.\n *\n * The `allowed` array is not revealed to actors other than the creator, like\n * a BCC email. An actor may choose to add a `to` property to the object's {@link value | `value`} to indicate\n * other recipients, however this is not enforced by Graffiti and may not accurately reflect the actual `allowed` array.\n *\n * `allowed` can be combined with {@link channels | `channels`}. For example, to send someone a direct message\n * the sender should post their object to the channel of the recipient's {@link actor | `actor`} URI to notify them of the message and also add\n * the recipient's {@link actor | `actor`} URI to the `allowed` array to prevent others from seeing the message.\n */\n allowed?: string[] | null;\n\n /**\n * The URI of the `actor` that {@link Graffiti.post | created } the object.\n * This `actor` has the unique permission to\n * {@link Graffiti.delete | delete} the object.\n *\n * We borrow the term actor from the ActivityPub because\n * [like in ActivityPub](https://www.w3.org/TR/activitypub/#h-note-0)\n * there is not necessarily a one-to-one mapping between actors and people/users.\n * Multiple people can share the same actor or one person can have multiple actors.\n * Actors can also be bots.\n *\n * In Graffiti, actors are always globally unique URIs which\n * allows them to also function as {@link channels | `channels`}.\n */\n actor: string;\n\n /**\n * A globally unique identifier and locator for the object. It can be used to point to\n * an object or to retrieve the object directly with {@link Graffiti.get}.\n *\n * An object's URL is generated when the object is first created and\n * should include sufficient randomness to prevent collisions\n * and guessing. The URL starts with a \"scheme,\" just like web URLs start with `http` or `https`, to indicate\n * to indicate the particular Graffiti implementation. This allows for applications\n * to pull from multiple coexisting Graffiti implementations without collision.\n * Existing schemes include `graffiti:local:` for objects stored locally\n * (see the [local implementation](https://github.com/graffiti-garden/implementation-local))\n * and `graffiti:remote:` for objects stored on Graffiti-specific web servers (see the [remote implementation](https://github.com/graffiti-garden/implementation-remote))\n * Options available in the future might include `graffiti:solid:` for objects stored on Solid servers\n * or `graffiti:p2p:` for objects stored on a peer-to-peer network.\n */\n url: string;\n}\n\n/**\n * This type constrains the {@link GraffitiObjectBase} type to adhere to a\n * particular [JSON schema](https://json-schema.org/).\n * This allows for static type-checking of an object's {@link GraffitiObjectBase.value | `value`}\n * which is otherwise a freeform JSON object.\n *\n * Schema-aware objects are returned by {@link Graffiti.get} and {@link Graffiti.discover}.\n */\nexport type GraffitiObject<Schema extends JSONSchema> = GraffitiObjectBase &\n FromSchema<Schema & typeof GraffitiObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiObjectBase} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiObjectJSONSchema = {\n type: \"object\",\n properties: {\n value: { type: \"object\" },\n channels: { type: \"array\", items: { type: \"string\" } },\n allowed: { type: \"array\", items: { type: \"string\" }, nullable: true },\n url: { type: \"string\" },\n actor: { type: \"string\" },\n },\n additionalProperties: false,\n required: [\"value\", \"channels\", \"actor\", \"url\"],\n} as const satisfies JSONSchema;\n\n/**\n * This is an object containing only the {@link GraffitiObjectBase.url | `url`}\n * property of a {@link GraffitiObjectBase | GraffitiObject}.\n * It is used as a utility type so that applications can call\n * {@link Graffiti.delete} directly on an object\n * rather than on `object.url`.\n */\nexport type GraffitiObjectUrl = Pick<GraffitiObjectBase, \"url\">;\n\n/**\n * This object is a subset of {@link GraffitiObjectBase} that must be constructed locally before calling {@link Graffiti.post}.\n * This local copy ignores system-generated properties\n * ({@link GraffitiObjectBase.url | `url`} and {@link GraffitiObjectBase.actor | `actor`}),\n * and may be statically typed with\n * a [JSON schema](https://json-schema.org/) to prevent the accidental creation of erroneous objects.\n *\n * This local object must have a {@link GraffitiObjectBase.value | `value`} and {@link GraffitiObjectBase.channels | `channels`}\n * and may optionally have an {@link GraffitiObjectBase.allowed | `allowed`} property.\n */\nexport type GraffitiPostObject<Schema extends JSONSchema> = Pick<\n GraffitiObjectBase,\n \"value\" | \"channels\" | \"allowed\"\n> &\n Partial<GraffitiObjectBase> &\n FromSchema<Schema & typeof GraffitiPostObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiPostObject} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiPostObjectJSONSchema = {\n ...GraffitiObjectJSONSchema,\n required: [\"value\", \"channels\"],\n} as const satisfies JSONSchema;\n\n/**\n * This object contains information that the underlying implementation can\n * use to authenticate a particular {@link GraffitiObjectBase.actor | `actor`}.\n * This object is required of all {@link Graffiti} methods\n * that modify objects or media and is optional for methods that read objects.\n *\n * At a minimum the `session` object must contain the\n * {@link GraffitiSession.actor | `actor`} URI to authenticate with.\n * However it is likely that the `session` object must contain other\n * implementation-specific properties.\n * For example, a Solid implementation might include a\n * [`fetch`](https://docs.inrupt.com/developer-tools/api/javascript/solid-client-authn-browser/functions.html#fetch)\n * function. A distributed implementation may include\n * a cryptographic signature.\n *\n * As to why the `session` object is passed as an argument to every method\n * rather than being an internal property of the {@link Graffiti} instance,\n * this is primarily for type-checking to catch bugs related to login state.\n * Graffiti applications can expose some functionality to people who are not logged in\n * with {@link Graffiti.get} and {@link Graffiti.discover} but without type-checking\n * the `session` it can be easy to forget to hide buttons that trigger\n * other methods that require login.\n *\n * Passing the `session` object per-method also allows for multiple sessions\n * to be used within the same application, like an Email client fetching from\n * multiple accounts.\n */\nexport interface GraffitiSession {\n /**\n * The {@link GraffitiObjectBase.actor | `actor`} to authenticate with.\n */\n actor: string;\n}\n\n/**\n * A stream of data that are returned by {@link Graffiti.discover}.\n *\n * Errors are returned within the stream rather than as\n * exceptions that would halt the entire stream. This is because\n * some implementations may pull data from multiple sources\n * including some that may be unreliable. In many cases,\n * these errors can be safely ignored.\n * See {@link GraffitiObjectStreamError}.\n *\n * The stream is an [`AsyncGenerator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)\n * that can be iterated over using `for await` loops or calling `next` on the generator.\n * The stream can be terminated by breaking out of a loop calling `return` on the generator.\n *\n * The stream ends by returning a {@link GraffitiObjectStreamReturn.continue | `continue`}\n * function and a {@link GraffitiObjectStreamReturn.cursor | `cursor`} string,\n * each of which can be used to resume the stream from where it left off.\n */\nexport type GraffitiObjectStream<Schema extends JSONSchema> = AsyncGenerator<\n GraffitiObjectStreamError | GraffitiObjectStreamEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n>;\n\n/**\n * An error that can occur in a {@link GraffitiObjectStream}.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamError {\n /**\n * The error that occurred while streaming data.\n */\n error: Error;\n /**\n * The origin that the error occurred. It will include\n * the scheme of the Graffiti implementation used and other\n * implementation-specific information like a hostname.\n */\n origin: string;\n}\n\n/**\n * A successful result from a {@link GraffitiObjectStream} or\n * {@link GraffitiObjectStreamContinue} that includes an object.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamEntry<Schema extends JSONSchema> {\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamError}\n */\n error?: undefined;\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamContinueTombstone}\n */\n tombstone?: undefined;\n /**\n * The object returned by the stream.\n */\n object: GraffitiObject<Schema>;\n}\n\n/**\n * A result from a {@link GraffitiObjectStreamContinue} that indicated\n * an object has been deleted since the original stream was run.\n * Only sparse metadata about the deleted object is returned to respect\n * the deleting actor's privacy.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamContinueTombstone {\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamError}\n */\n error?: undefined;\n /**\n * Use this property to differentiate a tombstone from a\n * {@link GraffitiObjectStreamEntry}.\n */\n tombstone: true;\n /**\n * Sparse metadata about the deleted object. The full object is not returned\n * to respect an actor's privacy.\n */\n object: {\n /**\n * The {@link GraffitiObjectBase.url | `url`} of the deleted object.\n */\n url: string;\n };\n}\n\n/**\n * A continuation of the {@link GraffitiObjectStream} type can include\n * both objects and tombstones of deleted objects.\n *\n * @internal\n */\nexport type GraffitiObjectStreamContinueEntry<Schema extends JSONSchema> =\n | GraffitiObjectStreamEntry<Schema>\n | GraffitiObjectStreamContinueTombstone;\n\n/**\n * The output of a {@link GraffitiObjectStream} or a {@link GraffitiObjectStreamContinue}\n * that allows the stream to be continued from where it left off.\n *\n * The {@link continue} function preserves the typing of the original stream,\n * where as the {@link cursor} string can be serialized for use after a person\n * has closed and reopened an application.\n *\n * The continued stream may include `tombstone`s of objects that have been\n * deleted since the original stream was run. See {@link GraffitiObjectStreamContinueTombstone}.\n * The continued stream may also return some objects that were already\n * returned by the original stream, depending on how much state the\n * underlying implementation is able to preserve.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamReturn<Schema extends JSONSchema> {\n /**\n * @returns A function that creates new stream that continues from where the original stream left off.\n * It preserves the typing of the original stream.\n */\n continue: (\n session?: GraffitiSession | null,\n ) => GraffitiObjectStreamContinue<Schema>;\n /**\n * A string that can be serialized and stored to resume the stream later.\n * It must be passed to the {@link Graffiti.continueDiscover} method\n * to resume the stream.\n */\n cursor: string;\n}\n\n/**\n * A continutation of the {@link GraffitiObjectStream} type, as returned by\n * the {@link GraffitiObjectStreamReturn.continue} or by using\n * {@link GraffitiObjectStreamReturn.cursor} with {@link Graffiti.continueDiscover}.\n *\n * The continued stream may include `tombstone`s of objects that have been\n * deleted since the original stream was run. See {@link GraffitiObjectStreamContinueTombstone}.\n *\n * @internal\n */\nexport type GraffitiObjectStreamContinue<Schema extends JSONSchema> =\n AsyncGenerator<\n GraffitiObjectStreamError | GraffitiObjectStreamContinueEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n >;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a actor logs in manually from {@link Graffiti.login}\n * or when their session is restored from a previous login.\n * The event name to listen for is `login`.\n */\nexport type GraffitiLoginEvent = CustomEvent<\n | {\n error: Error;\n session?: undefined;\n }\n | {\n error?: undefined;\n session: GraffitiSession;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a actor logs out either manually with {@link Graffiti.logout}\n * or when their session times out or otherwise becomes invalid.\n * The event name to listen for is `logout`.\n */\nexport type GraffitiLogoutEvent = CustomEvent<\n | {\n error: Error;\n actor?: string;\n }\n | {\n error?: undefined;\n actor: string;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * after an application has attempted to complete any login redirects\n * and restore any previously active sessions.\n * Successful session restores will be returned in parallel as\n * their own {@link GraffitiLoginEvent} events.\n *\n * This event optionally returns an `href` property\n * representing the URL that originated a login request,\n * which may be useful for redirecting the user back to\n * the page they were on after login.\n * The event name to listen for is `initialized`.\n */\nexport type GraffitiSessionInitializedEvent = CustomEvent<\n | {\n error?: Error;\n href?: string;\n }\n | null\n | undefined\n>;\n", "export class GraffitiErrorUnauthorized extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorUnauthorized\";\n Object.setPrototypeOf(this, GraffitiErrorUnauthorized.prototype);\n }\n}\n\nexport class GraffitiErrorForbidden extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorForbidden\";\n Object.setPrototypeOf(this, GraffitiErrorForbidden.prototype);\n }\n}\n\nexport class GraffitiErrorNotFound extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorNotFound\";\n Object.setPrototypeOf(this, GraffitiErrorNotFound.prototype);\n }\n}\n\nexport class GraffitiErrorInvalidSchema extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorInvalidSchema\";\n Object.setPrototypeOf(this, GraffitiErrorInvalidSchema.prototype);\n }\n}\n\nexport class GraffitiErrorSchemaMismatch extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorSchemaMismatch\";\n Object.setPrototypeOf(this, GraffitiErrorSchemaMismatch.prototype);\n }\n}\n\nexport class GraffitiErrorTooLarge extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorTooLarge\";\n Object.setPrototypeOf(this, GraffitiErrorTooLarge.prototype);\n }\n}\n\nexport class GraffitiErrorNotAcceptable extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorNotAcceptable\";\n Object.setPrototypeOf(this, GraffitiErrorNotAcceptable.prototype);\n }\n}\n", "import type { JSONSchema } from \"json-schema-to-ts\";\nimport type { Ajv } from \"ajv\";\nimport { GraffitiErrorInvalidSchema } from \"./3-errors\";\nimport type {\n GraffitiObjectBase,\n GraffitiObject,\n GraffitiObjectUrl,\n GraffitiSession,\n} from \"./2-types\";\n\nexport function unpackObjectUrl(url: string | GraffitiObjectUrl) {\n return typeof url === \"string\" ? url : url.url;\n}\n\nexport function compileGraffitiObjectSchema<Schema extends JSONSchema>(\n ajv: Ajv,\n schema: Schema,\n) {\n try {\n // Force the validation guard because\n // it is too big for the type checker.\n // Fortunately json-schema-to-ts is\n // well tested against ajv.\n return ajv.compile(schema) as (\n data: GraffitiObjectBase,\n ) => data is GraffitiObject<Schema>;\n } catch (error) {\n throw new GraffitiErrorInvalidSchema(\n error instanceof Error ? error.message : undefined,\n );\n }\n}\n\nexport function isActorAllowedGraffitiObject(\n object: GraffitiObjectBase,\n session?: GraffitiSession | null,\n) {\n return (\n // If there is no allowed list, the actor is allowed.\n !Array.isArray(object.allowed) ||\n // Otherwise...\n (typeof session?.actor === \"string\" &&\n // The actor must be the creator of the object\n (object.actor === session.actor ||\n // Or be on the allowed list\n object.allowed.includes(session.actor)))\n );\n}\n\nexport function maskGraffitiObject(\n object: GraffitiObjectBase,\n channels: string[],\n session?: GraffitiSession | null,\n): void {\n // If the actor is not the creator, mask the object.\n if (object.actor !== session?.actor) {\n // If there is an allowed list, mask it to only include the actor\n // (This assumes the actor is already allowed to access the object)\n object.allowed = object.allowed && session ? [session.actor] : undefined;\n // Mask the channels to only include the channels that are being queried\n object.channels = object.channels.filter((channel) =>\n channels.includes(channel),\n );\n }\n}\n", "import type {\n Graffiti,\n GraffitiLoginEvent,\n GraffitiLogoutEvent,\n GraffitiSessionInitializedEvent,\n} from \"@graffiti-garden/api\";\nimport { decodeBase64, encodeBase64 } from \"./utilities\";\n\nconst DID_LOCAL_PREFIX = \"did:local:\";\n\n/**\n * A class that implements the login methods\n * of the [Graffiti API]() for use in the browser.\n * It is completely insecure and should only be used\n * for testing and demonstrations.\n *\n * It uses `localStorage` to store login state and\n * window prompts rather than an oauth flow for log in.\n * It can be used in node.js but will not persist\n * login state and a proposed username must be provided.\n */\nexport class GraffitiLocalIdentity {\n sessionEvents: Graffiti[\"sessionEvents\"] = new EventTarget();\n\n handleToActor: Graffiti[\"handleToActor\"] = async (handle: string) => {\n const bytes = new TextEncoder().encode(handle);\n const base64 = encodeBase64(bytes);\n return `${DID_LOCAL_PREFIX}${base64}`;\n };\n\n actorToHandle: Graffiti[\"actorToHandle\"] = async (actor: string) => {\n if (!actor.startsWith(DID_LOCAL_PREFIX)) {\n throw new Error(`actor must start with ${DID_LOCAL_PREFIX}`);\n }\n const base64 = actor.slice(DID_LOCAL_PREFIX.length);\n const bytes = decodeBase64(base64);\n return new TextDecoder().decode(bytes);\n };\n\n constructor() {\n // Look for any existing sessions\n const sessionRestorer = async () => {\n // Allow listeners to be added first\n await Promise.resolve();\n\n // Restore previous sessions\n for (const handle of this.getLoggedInHandles()) {\n const event: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: { session: { actor: await this.handleToActor(handle) } },\n });\n this.sessionEvents.dispatchEvent(event);\n }\n\n const event: GraffitiSessionInitializedEvent = new CustomEvent(\n \"initialized\",\n { detail: {} },\n );\n this.sessionEvents.dispatchEvent(event);\n };\n sessionRestorer();\n }\n\n loggedInHandles: string[] = [];\n\n protected getLoggedInHandles(): string[] {\n if (typeof window !== \"undefined\") {\n const handlesString = window.localStorage.getItem(\"graffiti-handles\");\n return handlesString\n ? handlesString.split(\",\").map(decodeURIComponent)\n : [];\n } else {\n return this.loggedInHandles;\n }\n }\n\n protected setLoggedInHandles(handles: string[]) {\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\"graffiti-handles\", handles.join(\",\"));\n } else {\n this.loggedInHandles = handles;\n }\n }\n\n login: Graffiti[\"login\"] = async (actor) => {\n // Wait a tick for the browser to update the UI\n await new Promise((resolve) => setTimeout(resolve, 0));\n\n let handle = actor ? await this.actorToHandle(actor) : undefined;\n\n if (typeof window !== \"undefined\") {\n const response = window.prompt(\"Choose a username to log in.\", handle);\n handle = response ?? undefined;\n }\n\n if (!handle) {\n const detail: GraffitiLoginEvent[\"detail\"] = {\n error: new Error(\"No handle provided to login\"),\n };\n const event: GraffitiLoginEvent = new CustomEvent(\"login\", { detail });\n this.sessionEvents.dispatchEvent(event);\n } else {\n const existingHandles = this.getLoggedInHandles();\n if (!existingHandles.includes(handle)) {\n this.setLoggedInHandles([...existingHandles, handle]);\n }\n // Refresh the page to simulate oauth\n window.location.reload();\n }\n };\n\n logout: Graffiti[\"logout\"] = async (session) => {\n const handle = await this.actorToHandle(session.actor);\n const existingHandles = this.getLoggedInHandles();\n const exists = existingHandles.includes(handle);\n if (exists) {\n this.setLoggedInHandles(existingHandles.filter((h) => h !== handle));\n }\n\n const detail: GraffitiLogoutEvent[\"detail\"] = exists\n ? {\n actor: session.actor,\n }\n : {\n actor: session.actor,\n error: new Error(\"Not logged in with that actor\"),\n };\n\n const event: GraffitiLogoutEvent = new CustomEvent(\"logout\", { detail });\n this.sessionEvents.dispatchEvent(event);\n };\n}\n", "export function encodeBase64(bytes: Uint8Array): string {\n // Convert it to base64\n const base64 = btoa(String.fromCodePoint(...bytes));\n // Make sure it is url safe\n return base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/\\=+$/, \"\");\n}\n\nexport function decodeBase64(base64Url: string): Uint8Array {\n // Undo url-safe base64\n let base64 = base64Url.replace(/-/g, \"+\").replace(/_/g, \"/\");\n // Add padding if necessary\n while (base64.length % 4 !== 0) base64 += \"=\";\n // Decode\n return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));\n}\n\nexport function randomBase64(numBytes: number = 32): string {\n // Generate random bytes\n const bytes = new Uint8Array(numBytes);\n crypto.getRandomValues(bytes);\n return encodeBase64(bytes);\n}\n\nconst OBJECT_URL_PREFIX = \"graffiti:object:\";\nconst MEDIA_URL_PREFIX = \"graffiti:media:\";\n\nexport function encodeGraffitiUrl(actor: string, id: string, prefix: string) {\n return `${prefix}${encodeURIComponent(actor)}:${encodeURIComponent(id)}`;\n}\nexport function encodeObjectUrl(actor: string, id: string) {\n return encodeGraffitiUrl(actor, id, OBJECT_URL_PREFIX);\n}\nexport function encodeMediaUrl(actor: string, id: string) {\n return encodeGraffitiUrl(actor, id, MEDIA_URL_PREFIX);\n}\n\nexport function decodeGraffitiUrl(url: string, prefix: string) {\n if (!url.startsWith(prefix)) {\n throw new Error(`URL does not start with ${prefix}`);\n }\n const slices = url.slice(prefix.length).split(\":\");\n if (slices.length !== 2) {\n throw new Error(\"URL has too many colon-seperated parts\");\n }\n const [actor, id] = slices.map(decodeURIComponent);\n return { actor, id };\n}\nexport function decodeObjectUrl(url: string) {\n return decodeGraffitiUrl(url, OBJECT_URL_PREFIX);\n}\nexport function decodeMediaUrl(url: string) {\n return decodeGraffitiUrl(url, MEDIA_URL_PREFIX);\n}\n\nexport async function blobToBase64(blob: Blob): Promise<string> {\n if (typeof FileReader !== \"undefined\") {\n return new Promise((resolve, reject) => {\n const r = new FileReader();\n r.onload = () => {\n if (typeof r.result === \"string\") {\n resolve(r.result);\n } else {\n reject(new Error(\"Unexpected result type\"));\n }\n };\n r.onerror = reject;\n r.readAsDataURL(blob);\n });\n }\n\n if (typeof Buffer !== \"undefined\") {\n const ab = await blob.arrayBuffer();\n return `data:${blob.type};base64,${Buffer.from(ab).toString(\"base64\")}`;\n }\n\n throw new Error(\"Unsupported environment\");\n}\n\nexport async function base64ToBlob(dataUrl: string) {\n const response = await fetch(dataUrl);\n return await response.blob();\n}\n", "import type {\n Graffiti,\n GraffitiObjectBase,\n JSONSchema,\n GraffitiSession,\n GraffitiObjectStreamContinue,\n GraffitiObjectStreamContinueEntry,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorNotFound,\n GraffitiErrorSchemaMismatch,\n GraffitiErrorForbidden,\n unpackObjectUrl,\n maskGraffitiObject,\n isActorAllowedGraffitiObject,\n compileGraffitiObjectSchema,\n} from \"@graffiti-garden/api\";\nimport { randomBase64, decodeObjectUrl, encodeObjectUrl } from \"./utilities.js\";\nimport type Ajv from \"ajv\";\n\n/**\n * Constructor options for the GraffitiPoubchDB class.\n */\nexport interface GraffitiLocalOptions {\n /**\n * Options to pass to the PouchDB constructor.\n * Defaults to `{ name: \"graffitiDb\" }`.\n *\n * See the [PouchDB documentation](https://pouchdb.com/api.html#create_database)\n * for available options.\n */\n pouchDBOptions?: PouchDB.Configuration.DatabaseConfiguration;\n /**\n * Wait at least this long (in milliseconds) before continuing a stream.\n * A basic form of rate limiting. Defaults to 2 seconds.\n */\n continueBuffer?: number;\n}\n\ntype GraffitiObjectData = {\n tombstone: boolean;\n value: {};\n channels: string[];\n allowed?: string[] | null;\n lastModified: number;\n};\n\ntype ContinueDiscoverParams = {\n lastDiscovered: number;\n ifModifiedSince: number;\n};\n\n/**\n * An implementation of only the database operations of the\n * GraffitiAPI without synchronization or session management.\n */\nexport class GraffitiLocalObjects {\n protected db_: Promise<PouchDB.Database<GraffitiObjectData>> | undefined;\n protected ajv_: Promise<Ajv> | undefined;\n protected readonly options: GraffitiLocalOptions;\n protected operationClock: number = 0;\n\n get db() {\n if (!this.db_) {\n this.db_ = (async () => {\n const { default: PouchDB } = await import(\"pouchdb\");\n const pouchDbOptions = {\n name: \"graffitiDb\",\n ...this.options.pouchDBOptions,\n };\n const db = new PouchDB<GraffitiObjectData>(\n pouchDbOptions.name,\n pouchDbOptions,\n );\n await db\n //@ts-ignore\n .put({\n _id: \"_design/indexes\",\n views: {\n objectsPerChannelAndLastModified: {\n map: function (object: GraffitiObjectData) {\n const paddedLastModified = object.lastModified\n .toString()\n .padStart(15, \"0\");\n object.channels.forEach(function (channel) {\n const id =\n encodeURIComponent(channel) + \"/\" + paddedLastModified;\n //@ts-ignore\n emit(id);\n });\n }.toString(),\n },\n },\n })\n //@ts-ignore\n .catch((error) => {\n if (\n error &&\n typeof error === \"object\" &&\n \"name\" in error &&\n error.name === \"conflict\"\n ) {\n // Design document already exists\n return;\n } else {\n throw error;\n }\n });\n return db;\n })();\n }\n return this.db_;\n }\n\n protected get ajv() {\n if (!this.ajv_) {\n this.ajv_ = (async () => {\n const { default: Ajv } = await import(\"ajv\");\n return new Ajv({ strict: false });\n })();\n }\n return this.ajv_;\n }\n\n constructor(options?: GraffitiLocalOptions) {\n this.options = options ?? {};\n }\n\n get: Graffiti[\"get\"] = async (...args) => {\n const [urlObject, schema, session] = args;\n const url = unpackObjectUrl(urlObject);\n\n let doc: GraffitiObjectData;\n try {\n doc = await (await this.db).get(url);\n } catch (error) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n const { actor } = decodeObjectUrl(url);\n const { value, channels, allowed } = doc;\n const object: GraffitiObjectBase = {\n value,\n channels,\n allowed,\n url,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n // Mask out the allowed list and channels\n // if the user is not the owner\n maskGraffitiObject(object, [], session);\n\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n if (!validate(object)) {\n throw new GraffitiErrorSchemaMismatch();\n }\n return object;\n };\n\n delete: Graffiti[\"delete\"] = async (...args) => {\n const [urlObject, session] = args;\n\n const url = unpackObjectUrl(urlObject);\n const { actor } = decodeObjectUrl(url);\n if (actor !== session.actor) {\n throw new GraffitiErrorForbidden(\n \"You cannot delete an object that you did not create.\",\n );\n }\n\n let doc: GraffitiObjectData & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta;\n try {\n doc = await (await this.db).get(url);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n // Set the tombstone and update lastModified\n doc.tombstone = true;\n doc.lastModified = this.operationClock;\n try {\n await (await this.db).put(doc);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n this.operationClock++;\n\n return;\n };\n\n post: Graffiti[\"post\"] = async (...args) => {\n const [objectPartial, session] = args;\n\n const actor = session.actor;\n const id = randomBase64();\n const url = encodeObjectUrl(actor, id);\n\n const { value, channels, allowed } = objectPartial;\n const object: GraffitiObjectData = {\n value,\n channels,\n allowed,\n lastModified: this.operationClock,\n tombstone: false,\n };\n\n await (\n await this.db\n ).put({\n _id: url,\n ...object,\n });\n this.operationClock++;\n\n return {\n ...objectPartial,\n actor,\n url,\n };\n };\n\n protected async *discoverMeta<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams?: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): AsyncGenerator<\n GraffitiObjectStreamContinueEntry<Schema>,\n ContinueDiscoverParams\n > {\n // If we are continuing a discover, make sure to wait at\n // least 2 seconds since the last poll to start a new one.\n if (continueParams) {\n const continueBuffer = this.options.continueBuffer ?? 2000;\n const timeElapsedSinceLastDiscover =\n Date.now() - continueParams.lastDiscovered;\n if (timeElapsedSinceLastDiscover < continueBuffer) {\n // Continue was called too soon,\n // wait a bit before continuing\n await new Promise((resolve) =>\n setTimeout(resolve, continueBuffer - timeElapsedSinceLastDiscover),\n );\n }\n }\n\n const [discoverChannels, schema, session] = args;\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n const startKeySuffix = continueParams\n ? continueParams.ifModifiedSince.toString().padStart(15, \"0\")\n : \"\";\n const endKeySuffix = \"\\uffff\";\n\n const processedUrls = new Set<string>();\n\n const startTime = this.operationClock;\n\n for (const channel of discoverChannels) {\n const keyPrefix = encodeURIComponent(channel) + \"/\";\n const startkey = keyPrefix + startKeySuffix;\n const endkey = keyPrefix + endKeySuffix;\n\n const result = await (\n await this.db\n ).query<GraffitiObjectData>(\"indexes/objectsPerChannelAndLastModified\", {\n startkey,\n endkey,\n include_docs: true,\n });\n\n for (const row of result.rows) {\n const doc = row.doc;\n if (!doc) continue;\n\n const url = doc._id;\n\n if (processedUrls.has(url)) continue;\n processedUrls.add(url);\n\n // If this is not a continuation, skip tombstones\n if (!continueParams && doc.tombstone) continue;\n\n const { tombstone, value, channels, allowed } = doc;\n const { actor } = decodeObjectUrl(url);\n\n const object: GraffitiObjectBase = {\n url,\n value,\n allowed,\n channels,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) continue;\n\n maskGraffitiObject(object, discoverChannels, session);\n\n if (!validate(object)) continue;\n\n yield tombstone\n ? {\n tombstone: true,\n object: { url },\n }\n : { object };\n }\n }\n\n return {\n lastDiscovered: Date.now(),\n ifModifiedSince: startTime,\n };\n }\n\n protected discoverCursor(\n args: Parameters<typeof Graffiti.prototype.discover<{}>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): string {\n const [channels, schema, session] = args;\n return (\n \"discover:\" +\n JSON.stringify({\n channels,\n schema,\n continueParams,\n actor: session?.actor,\n })\n );\n }\n\n protected async *discoverContinue<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<Schema> {\n if (session?.actor !== args[2]?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n const iterator = this.discoverMeta<Schema>(args, continueParams);\n\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this.discoverContinue<Schema>(args, result.value, session),\n cursor: this.discoverCursor(args, result.value),\n };\n }\n yield result.value;\n }\n }\n\n discover: Graffiti[\"discover\"] = (...args) => {\n const [channels, schema, session] = args;\n const iterator = this.discoverMeta<(typeof args)[1]>([\n channels,\n schema,\n session,\n ]);\n\n const this_ = this;\n return (async function* () {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this_.discoverContinue<(typeof args)[1]>(\n args,\n result.value,\n session,\n ),\n cursor: this_.discoverCursor(args, result.value),\n };\n }\n // Make sure to filter out tombstones\n if (result.value.tombstone) continue;\n yield result.value;\n }\n })();\n };\n\n continueDiscover: Graffiti[\"continueDiscover\"] = (...args) => {\n const [cursor, session] = args;\n if (cursor.startsWith(\"discover:\")) {\n // TODO: use AJV here\n const { channels, schema, actor, continueParams } = JSON.parse(\n cursor.slice(\"discover:\".length),\n );\n if (actor && actor !== session?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n return this.discoverContinue<{}>(\n [channels, schema, session],\n continueParams,\n );\n } else {\n throw new GraffitiErrorNotFound(\"Cursor not found\");\n }\n };\n}\n", "import {\n GraffitiErrorNotAcceptable,\n GraffitiErrorTooLarge,\n type Graffiti,\n type JSONSchema,\n} from \"@graffiti-garden/api\";\nimport {\n decodeObjectUrl,\n encodeObjectUrl,\n decodeMediaUrl,\n encodeMediaUrl,\n blobToBase64,\n base64ToBlob,\n} from \"./utilities\";\nimport Negotiator from \"negotiator\";\n\nconst MEDIA_OBJECT_SCHEMA = {\n properties: {\n value: {\n properties: {\n dataBase64: { type: \"string\" },\n type: { type: \"string\" },\n size: { type: \"number\" },\n },\n required: [\"dataBase64\", \"type\", \"size\"],\n },\n },\n} as const satisfies JSONSchema;\n\nexport class GraffitiLocalMedia {\n protected db: Pick<Graffiti, \"post\" | \"get\" | \"delete\">;\n\n constructor(db: Pick<Graffiti, \"post\" | \"get\" | \"delete\">) {\n this.db = db;\n }\n\n postMedia: Graffiti[\"postMedia\"] = async (...args) => {\n const [media, session] = args;\n\n const dataBase64 = await blobToBase64(media.data);\n const type = media.data.type;\n\n const { url } = await this.db.post<typeof MEDIA_OBJECT_SCHEMA>(\n {\n value: {\n dataBase64,\n type,\n size: media.data.size,\n },\n channels: [],\n allowed: media.allowed,\n },\n session,\n );\n\n const { actor, id } = decodeObjectUrl(url);\n return encodeMediaUrl(actor, id);\n };\n\n getMedia: Graffiti[\"getMedia\"] = async (...args) => {\n const [mediaUrl, requirements, session] = args;\n const { actor, id } = decodeMediaUrl(mediaUrl);\n const objectUrl = encodeObjectUrl(actor, id);\n\n const object = await this.db.get<typeof MEDIA_OBJECT_SCHEMA>(\n objectUrl,\n MEDIA_OBJECT_SCHEMA,\n session,\n );\n\n const { dataBase64, type, size } = object.value;\n\n if (requirements?.maxBytes && size > requirements.maxBytes) {\n throw new GraffitiErrorTooLarge(\"File size exceeds limit\");\n }\n\n // Make sure it adheres to requirements.accept\n if (requirements?.accept) {\n const negotiator = new Negotiator({\n headers: { accept: requirements.accept },\n });\n if (negotiator.mediaType([type]) !== type) {\n throw new GraffitiErrorNotAcceptable(`Unsupported media type, ${type}`);\n }\n }\n\n const data = await base64ToBlob(dataBase64);\n if (data.size !== size || data.type !== type) {\n throw new Error(\"Invalid data\");\n }\n\n return {\n data,\n actor: object.actor,\n allowed: object.allowed,\n };\n };\n\n deleteMedia: Graffiti[\"deleteMedia\"] = async (...args) => {\n const [mediaUrl, session] = args;\n const { actor, id } = decodeMediaUrl(mediaUrl);\n const objectUrl = encodeObjectUrl(actor, id);\n\n await this.db.delete(objectUrl, session);\n };\n}\n"],
5
- "mappings": "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",
6
- "names": ["require_charset", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredCharsets", "simpleCharsetRegExp", "parseAcceptCharset", "accept", "accepts", "i", "j", "charset", "parseCharset", "str", "match", "q", "params", "p", "getCharsetPriority", "accepted", "index", "priority", "spec", "specify", "s", "provided", "isQuality", "compareSpecs", "getFullCharset", "priorities", "type", "a", "b", "require_encoding", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredEncodings", "simpleEncodingRegExp", "parseAcceptEncoding", "accept", "accepts", "hasIdentity", "minQuality", "i", "j", "encoding", "parseEncoding", "specify", "str", "match", "q", "params", "p", "getEncodingPriority", "accepted", "index", "priority", "spec", "s", "provided", "preferred", "comparator", "a", "b", "aPreferred", "bPreferred", "compareSpecs", "isQuality", "getFullEncoding", "priorities", "type", "require_language", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredLanguages", "simpleLanguageRegExp", "parseAcceptLanguage", "accept", "accepts", "i", "j", "language", "parseLanguage", "str", "match", "prefix", "suffix", "full", "q", "params", "getLanguagePriority", "accepted", "index", "priority", "spec", "specify", "p", "s", "provided", "isQuality", "compareSpecs", "getFullLanguage", "priorities", "type", "a", "b", "require_mediaType", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredMediaTypes", "simpleMediaTypeRegExp", "parseAccept", "accept", "accepts", "splitMediaTypes", "i", "j", "mediaType", "parseMediaType", "str", "match", "params", "q", "subtype", "type", "kvps", "splitParameters", "splitKeyValuePair", "pair", "key", "val", "value", "getMediaTypePriority", "accepted", "index", "priority", "spec", "specify", "p", "s", "keys", "k", "provided", "isQuality", "compareSpecs", "getFullType", "priorities", "a", "b", "quoteCount", "string", "count", "parameters", "require_negotiator", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredCharsets", "preferredEncodings", "preferredLanguages", "preferredMediaTypes", "Negotiator", "request", "available", "set", "opts", "options", "init_dirname", "init_buffer", "init_process", "init_dirname", "init_buffer", "init_process", "GraffitiObjectJSONSchema", "GraffitiPostObjectJSONSchema", "GraffitiErrorForbidden", "_GraffitiErrorForbidden", "message", "GraffitiErrorNotFound", "_GraffitiErrorNotFound", "GraffitiErrorInvalidSchema", "_GraffitiErrorInvalidSchema", "GraffitiErrorSchemaMismatch", "_GraffitiErrorSchemaMismatch", "GraffitiErrorTooLarge", "_GraffitiErrorTooLarge", "GraffitiErrorNotAcceptable", "_GraffitiErrorNotAcceptable", "unpackObjectUrl", "url", "compileGraffitiObjectSchema", "ajv", "schema", "error", "isActorAllowedGraffitiObject", "object", "session", "maskGraffitiObject", "channels", "channel", "init_dirname", "init_buffer", "init_process", "init_dirname", "init_buffer", "init_process", "encodeBase64", "bytes", "decodeBase64", "base64Url", "base64", "c", "randomBase64", "numBytes", "OBJECT_URL_PREFIX", "MEDIA_URL_PREFIX", "encodeGraffitiUrl", "actor", "id", "prefix", "encodeObjectUrl", "encodeMediaUrl", "decodeGraffitiUrl", "url", "slices", "decodeObjectUrl", "decodeMediaUrl", "blobToBase64", "blob", "resolve", "reject", "Buffer", "ab", "base64ToBlob", "dataUrl", "DID_LOCAL_PREFIX", "GraffitiLocalIdentity", "handle", "bytes", "base64", "encodeBase64", "actor", "decodeBase64", "event", "handlesString", "handles", "resolve", "existingHandles", "detail", "session", "exists", "h", "init_dirname", "init_buffer", "init_process", "GraffitiLocalObjects", "PouchDB", "pouchDbOptions", "db", "object", "paddedLastModified", "channel", "id", "error", "Ajv", "options", "args", "urlObject", "schema", "session", "url", "d", "doc", "n", "actor", "decodeObjectUrl", "value", "channels", "allowed", "j", "y", "G", "c", "o", "objectPartial", "randomBase64", "encodeObjectUrl", "continueParams", "continueBuffer", "timeElapsedSinceLastDiscover", "resolve", "discoverChannels", "validate", "startKeySuffix", "endKeySuffix", "processedUrls", "startTime", "keyPrefix", "startkey", "endkey", "result", "row", "tombstone", "iterator", "this_", "cursor", "init_dirname", "init_buffer", "init_process", "import_negotiator", "MEDIA_OBJECT_SCHEMA", "GraffitiLocalMedia", "db", "args", "media", "session", "dataBase64", "blobToBase64", "type", "url", "actor", "id", "decodeObjectUrl", "encodeMediaUrl", "mediaUrl", "requirements", "decodeMediaUrl", "objectUrl", "encodeObjectUrl", "object", "size", "f", "Negotiator", "m", "data", "base64ToBlob", "GraffitiLocal", "GraffitiLocalIdentity", "options", "GraffitiLocalObjects", "GraffitiLocalMedia"]
4
+ "sourcesContent": ["/**\n * negotiator\n * Copyright(c) 2012 Isaac Z. Schlueter\n * Copyright(c) 2014 Federico Romero\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = preferredCharsets;\nmodule.exports.preferredCharsets = preferredCharsets;\n\n/**\n * Module variables.\n * @private\n */\n\nvar simpleCharsetRegExp = /^\\s*([^\\s;]+)\\s*(?:;(.*))?$/;\n\n/**\n * Parse the Accept-Charset header.\n * @private\n */\n\nfunction parseAcceptCharset(accept) {\n var accepts = accept.split(',');\n\n for (var i = 0, j = 0; i < accepts.length; i++) {\n var charset = parseCharset(accepts[i].trim(), i);\n\n if (charset) {\n accepts[j++] = charset;\n }\n }\n\n // trim accepts\n accepts.length = j;\n\n return accepts;\n}\n\n/**\n * Parse a charset from the Accept-Charset header.\n * @private\n */\n\nfunction parseCharset(str, i) {\n var match = simpleCharsetRegExp.exec(str);\n if (!match) return null;\n\n var charset = match[1];\n var q = 1;\n if (match[2]) {\n var params = match[2].split(';')\n for (var j = 0; j < params.length; j++) {\n var p = params[j].trim().split('=');\n if (p[0] === 'q') {\n q = parseFloat(p[1]);\n break;\n }\n }\n }\n\n return {\n charset: charset,\n q: q,\n i: i\n };\n}\n\n/**\n * Get the priority of a charset.\n * @private\n */\n\nfunction getCharsetPriority(charset, accepted, index) {\n var priority = {o: -1, q: 0, s: 0};\n\n for (var i = 0; i < accepted.length; i++) {\n var spec = specify(charset, accepted[i], index);\n\n if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {\n priority = spec;\n }\n }\n\n return priority;\n}\n\n/**\n * Get the specificity of the charset.\n * @private\n */\n\nfunction specify(charset, spec, index) {\n var s = 0;\n if(spec.charset.toLowerCase() === charset.toLowerCase()){\n s |= 1;\n } else if (spec.charset !== '*' ) {\n return null\n }\n\n return {\n i: index,\n o: spec.i,\n q: spec.q,\n s: s\n }\n}\n\n/**\n * Get the preferred charsets from an Accept-Charset header.\n * @public\n */\n\nfunction preferredCharsets(accept, provided) {\n // RFC 2616 sec 14.2: no header = *\n var accepts = parseAcceptCharset(accept === undefined ? '*' : accept || '');\n\n if (!provided) {\n // sorted list of all charsets\n return accepts\n .filter(isQuality)\n .sort(compareSpecs)\n .map(getFullCharset);\n }\n\n var priorities = provided.map(function getPriority(type, index) {\n return getCharsetPriority(type, accepts, index);\n });\n\n // sorted list of accepted charsets\n return priorities.filter(isQuality).sort(compareSpecs).map(function getCharset(priority) {\n return provided[priorities.indexOf(priority)];\n });\n}\n\n/**\n * Compare two specs.\n * @private\n */\n\nfunction compareSpecs(a, b) {\n return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;\n}\n\n/**\n * Get full charset string.\n * @private\n */\n\nfunction getFullCharset(spec) {\n return spec.charset;\n}\n\n/**\n * Check if a spec has any quality.\n * @private\n */\n\nfunction isQuality(spec) {\n return spec.q > 0;\n}\n", "/**\n * negotiator\n * Copyright(c) 2012 Isaac Z. Schlueter\n * Copyright(c) 2014 Federico Romero\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = preferredEncodings;\nmodule.exports.preferredEncodings = preferredEncodings;\n\n/**\n * Module variables.\n * @private\n */\n\nvar simpleEncodingRegExp = /^\\s*([^\\s;]+)\\s*(?:;(.*))?$/;\n\n/**\n * Parse the Accept-Encoding header.\n * @private\n */\n\nfunction parseAcceptEncoding(accept) {\n var accepts = accept.split(',');\n var hasIdentity = false;\n var minQuality = 1;\n\n for (var i = 0, j = 0; i < accepts.length; i++) {\n var encoding = parseEncoding(accepts[i].trim(), i);\n\n if (encoding) {\n accepts[j++] = encoding;\n hasIdentity = hasIdentity || specify('identity', encoding);\n minQuality = Math.min(minQuality, encoding.q || 1);\n }\n }\n\n if (!hasIdentity) {\n /*\n * If identity doesn't explicitly appear in the accept-encoding header,\n * it's added to the list of acceptable encoding with the lowest q\n */\n accepts[j++] = {\n encoding: 'identity',\n q: minQuality,\n i: i\n };\n }\n\n // trim accepts\n accepts.length = j;\n\n return accepts;\n}\n\n/**\n * Parse an encoding from the Accept-Encoding header.\n * @private\n */\n\nfunction parseEncoding(str, i) {\n var match = simpleEncodingRegExp.exec(str);\n if (!match) return null;\n\n var encoding = match[1];\n var q = 1;\n if (match[2]) {\n var params = match[2].split(';');\n for (var j = 0; j < params.length; j++) {\n var p = params[j].trim().split('=');\n if (p[0] === 'q') {\n q = parseFloat(p[1]);\n break;\n }\n }\n }\n\n return {\n encoding: encoding,\n q: q,\n i: i\n };\n}\n\n/**\n * Get the priority of an encoding.\n * @private\n */\n\nfunction getEncodingPriority(encoding, accepted, index) {\n var priority = {encoding: encoding, o: -1, q: 0, s: 0};\n\n for (var i = 0; i < accepted.length; i++) {\n var spec = specify(encoding, accepted[i], index);\n\n if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {\n priority = spec;\n }\n }\n\n return priority;\n}\n\n/**\n * Get the specificity of the encoding.\n * @private\n */\n\nfunction specify(encoding, spec, index) {\n var s = 0;\n if(spec.encoding.toLowerCase() === encoding.toLowerCase()){\n s |= 1;\n } else if (spec.encoding !== '*' ) {\n return null\n }\n\n return {\n encoding: encoding,\n i: index,\n o: spec.i,\n q: spec.q,\n s: s\n }\n};\n\n/**\n * Get the preferred encodings from an Accept-Encoding header.\n * @public\n */\n\nfunction preferredEncodings(accept, provided, preferred) {\n var accepts = parseAcceptEncoding(accept || '');\n\n var comparator = preferred ? function comparator (a, b) {\n if (a.q !== b.q) {\n return b.q - a.q // higher quality first\n }\n\n var aPreferred = preferred.indexOf(a.encoding)\n var bPreferred = preferred.indexOf(b.encoding)\n\n if (aPreferred === -1 && bPreferred === -1) {\n // consider the original specifity/order\n return (b.s - a.s) || (a.o - b.o) || (a.i - b.i)\n }\n\n if (aPreferred !== -1 && bPreferred !== -1) {\n return aPreferred - bPreferred // consider the preferred order\n }\n\n return aPreferred === -1 ? 1 : -1 // preferred first\n } : compareSpecs;\n\n if (!provided) {\n // sorted list of all encodings\n return accepts\n .filter(isQuality)\n .sort(comparator)\n .map(getFullEncoding);\n }\n\n var priorities = provided.map(function getPriority(type, index) {\n return getEncodingPriority(type, accepts, index);\n });\n\n // sorted list of accepted encodings\n return priorities.filter(isQuality).sort(comparator).map(function getEncoding(priority) {\n return provided[priorities.indexOf(priority)];\n });\n}\n\n/**\n * Compare two specs.\n * @private\n */\n\nfunction compareSpecs(a, b) {\n return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i);\n}\n\n/**\n * Get full encoding string.\n * @private\n */\n\nfunction getFullEncoding(spec) {\n return spec.encoding;\n}\n\n/**\n * Check if a spec has any quality.\n * @private\n */\n\nfunction isQuality(spec) {\n return spec.q > 0;\n}\n", "/**\n * negotiator\n * Copyright(c) 2012 Isaac Z. Schlueter\n * Copyright(c) 2014 Federico Romero\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = preferredLanguages;\nmodule.exports.preferredLanguages = preferredLanguages;\n\n/**\n * Module variables.\n * @private\n */\n\nvar simpleLanguageRegExp = /^\\s*([^\\s\\-;]+)(?:-([^\\s;]+))?\\s*(?:;(.*))?$/;\n\n/**\n * Parse the Accept-Language header.\n * @private\n */\n\nfunction parseAcceptLanguage(accept) {\n var accepts = accept.split(',');\n\n for (var i = 0, j = 0; i < accepts.length; i++) {\n var language = parseLanguage(accepts[i].trim(), i);\n\n if (language) {\n accepts[j++] = language;\n }\n }\n\n // trim accepts\n accepts.length = j;\n\n return accepts;\n}\n\n/**\n * Parse a language from the Accept-Language header.\n * @private\n */\n\nfunction parseLanguage(str, i) {\n var match = simpleLanguageRegExp.exec(str);\n if (!match) return null;\n\n var prefix = match[1]\n var suffix = match[2]\n var full = prefix\n\n if (suffix) full += \"-\" + suffix;\n\n var q = 1;\n if (match[3]) {\n var params = match[3].split(';')\n for (var j = 0; j < params.length; j++) {\n var p = params[j].split('=');\n if (p[0] === 'q') q = parseFloat(p[1]);\n }\n }\n\n return {\n prefix: prefix,\n suffix: suffix,\n q: q,\n i: i,\n full: full\n };\n}\n\n/**\n * Get the priority of a language.\n * @private\n */\n\nfunction getLanguagePriority(language, accepted, index) {\n var priority = {o: -1, q: 0, s: 0};\n\n for (var i = 0; i < accepted.length; i++) {\n var spec = specify(language, accepted[i], index);\n\n if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {\n priority = spec;\n }\n }\n\n return priority;\n}\n\n/**\n * Get the specificity of the language.\n * @private\n */\n\nfunction specify(language, spec, index) {\n var p = parseLanguage(language)\n if (!p) return null;\n var s = 0;\n if(spec.full.toLowerCase() === p.full.toLowerCase()){\n s |= 4;\n } else if (spec.prefix.toLowerCase() === p.full.toLowerCase()) {\n s |= 2;\n } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {\n s |= 1;\n } else if (spec.full !== '*' ) {\n return null\n }\n\n return {\n i: index,\n o: spec.i,\n q: spec.q,\n s: s\n }\n};\n\n/**\n * Get the preferred languages from an Accept-Language header.\n * @public\n */\n\nfunction preferredLanguages(accept, provided) {\n // RFC 2616 sec 14.4: no header = *\n var accepts = parseAcceptLanguage(accept === undefined ? '*' : accept || '');\n\n if (!provided) {\n // sorted list of all languages\n return accepts\n .filter(isQuality)\n .sort(compareSpecs)\n .map(getFullLanguage);\n }\n\n var priorities = provided.map(function getPriority(type, index) {\n return getLanguagePriority(type, accepts, index);\n });\n\n // sorted list of accepted languages\n return priorities.filter(isQuality).sort(compareSpecs).map(function getLanguage(priority) {\n return provided[priorities.indexOf(priority)];\n });\n}\n\n/**\n * Compare two specs.\n * @private\n */\n\nfunction compareSpecs(a, b) {\n return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;\n}\n\n/**\n * Get full language string.\n * @private\n */\n\nfunction getFullLanguage(spec) {\n return spec.full;\n}\n\n/**\n * Check if a spec has any quality.\n * @private\n */\n\nfunction isQuality(spec) {\n return spec.q > 0;\n}\n", "/**\n * negotiator\n * Copyright(c) 2012 Isaac Z. Schlueter\n * Copyright(c) 2014 Federico Romero\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = preferredMediaTypes;\nmodule.exports.preferredMediaTypes = preferredMediaTypes;\n\n/**\n * Module variables.\n * @private\n */\n\nvar simpleMediaTypeRegExp = /^\\s*([^\\s\\/;]+)\\/([^;\\s]+)\\s*(?:;(.*))?$/;\n\n/**\n * Parse the Accept header.\n * @private\n */\n\nfunction parseAccept(accept) {\n var accepts = splitMediaTypes(accept);\n\n for (var i = 0, j = 0; i < accepts.length; i++) {\n var mediaType = parseMediaType(accepts[i].trim(), i);\n\n if (mediaType) {\n accepts[j++] = mediaType;\n }\n }\n\n // trim accepts\n accepts.length = j;\n\n return accepts;\n}\n\n/**\n * Parse a media type from the Accept header.\n * @private\n */\n\nfunction parseMediaType(str, i) {\n var match = simpleMediaTypeRegExp.exec(str);\n if (!match) return null;\n\n var params = Object.create(null);\n var q = 1;\n var subtype = match[2];\n var type = match[1];\n\n if (match[3]) {\n var kvps = splitParameters(match[3]).map(splitKeyValuePair);\n\n for (var j = 0; j < kvps.length; j++) {\n var pair = kvps[j];\n var key = pair[0].toLowerCase();\n var val = pair[1];\n\n // get the value, unwrapping quotes\n var value = val && val[0] === '\"' && val[val.length - 1] === '\"'\n ? val.slice(1, -1)\n : val;\n\n if (key === 'q') {\n q = parseFloat(value);\n break;\n }\n\n // store parameter\n params[key] = value;\n }\n }\n\n return {\n type: type,\n subtype: subtype,\n params: params,\n q: q,\n i: i\n };\n}\n\n/**\n * Get the priority of a media type.\n * @private\n */\n\nfunction getMediaTypePriority(type, accepted, index) {\n var priority = {o: -1, q: 0, s: 0};\n\n for (var i = 0; i < accepted.length; i++) {\n var spec = specify(type, accepted[i], index);\n\n if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {\n priority = spec;\n }\n }\n\n return priority;\n}\n\n/**\n * Get the specificity of the media type.\n * @private\n */\n\nfunction specify(type, spec, index) {\n var p = parseMediaType(type);\n var s = 0;\n\n if (!p) {\n return null;\n }\n\n if(spec.type.toLowerCase() == p.type.toLowerCase()) {\n s |= 4\n } else if(spec.type != '*') {\n return null;\n }\n\n if(spec.subtype.toLowerCase() == p.subtype.toLowerCase()) {\n s |= 2\n } else if(spec.subtype != '*') {\n return null;\n }\n\n var keys = Object.keys(spec.params);\n if (keys.length > 0) {\n if (keys.every(function (k) {\n return spec.params[k] == '*' || (spec.params[k] || '').toLowerCase() == (p.params[k] || '').toLowerCase();\n })) {\n s |= 1\n } else {\n return null\n }\n }\n\n return {\n i: index,\n o: spec.i,\n q: spec.q,\n s: s,\n }\n}\n\n/**\n * Get the preferred media types from an Accept header.\n * @public\n */\n\nfunction preferredMediaTypes(accept, provided) {\n // RFC 2616 sec 14.2: no header = */*\n var accepts = parseAccept(accept === undefined ? '*/*' : accept || '');\n\n if (!provided) {\n // sorted list of all types\n return accepts\n .filter(isQuality)\n .sort(compareSpecs)\n .map(getFullType);\n }\n\n var priorities = provided.map(function getPriority(type, index) {\n return getMediaTypePriority(type, accepts, index);\n });\n\n // sorted list of accepted types\n return priorities.filter(isQuality).sort(compareSpecs).map(function getType(priority) {\n return provided[priorities.indexOf(priority)];\n });\n}\n\n/**\n * Compare two specs.\n * @private\n */\n\nfunction compareSpecs(a, b) {\n return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;\n}\n\n/**\n * Get full type string.\n * @private\n */\n\nfunction getFullType(spec) {\n return spec.type + '/' + spec.subtype;\n}\n\n/**\n * Check if a spec has any quality.\n * @private\n */\n\nfunction isQuality(spec) {\n return spec.q > 0;\n}\n\n/**\n * Count the number of quotes in a string.\n * @private\n */\n\nfunction quoteCount(string) {\n var count = 0;\n var index = 0;\n\n while ((index = string.indexOf('\"', index)) !== -1) {\n count++;\n index++;\n }\n\n return count;\n}\n\n/**\n * Split a key value pair.\n * @private\n */\n\nfunction splitKeyValuePair(str) {\n var index = str.indexOf('=');\n var key;\n var val;\n\n if (index === -1) {\n key = str;\n } else {\n key = str.slice(0, index);\n val = str.slice(index + 1);\n }\n\n return [key, val];\n}\n\n/**\n * Split an Accept header into media types.\n * @private\n */\n\nfunction splitMediaTypes(accept) {\n var accepts = accept.split(',');\n\n for (var i = 1, j = 0; i < accepts.length; i++) {\n if (quoteCount(accepts[j]) % 2 == 0) {\n accepts[++j] = accepts[i];\n } else {\n accepts[j] += ',' + accepts[i];\n }\n }\n\n // trim accepts\n accepts.length = j + 1;\n\n return accepts;\n}\n\n/**\n * Split a string of parameters.\n * @private\n */\n\nfunction splitParameters(str) {\n var parameters = str.split(';');\n\n for (var i = 1, j = 0; i < parameters.length; i++) {\n if (quoteCount(parameters[j]) % 2 == 0) {\n parameters[++j] = parameters[i];\n } else {\n parameters[j] += ';' + parameters[i];\n }\n }\n\n // trim parameters\n parameters.length = j + 1;\n\n for (var i = 0; i < parameters.length; i++) {\n parameters[i] = parameters[i].trim();\n }\n\n return parameters;\n}\n", "/*!\n * negotiator\n * Copyright(c) 2012 Federico Romero\n * Copyright(c) 2012-2014 Isaac Z. Schlueter\n * Copyright(c) 2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\nvar preferredCharsets = require('./lib/charset')\nvar preferredEncodings = require('./lib/encoding')\nvar preferredLanguages = require('./lib/language')\nvar preferredMediaTypes = require('./lib/mediaType')\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = Negotiator;\nmodule.exports.Negotiator = Negotiator;\n\n/**\n * Create a Negotiator instance from a request.\n * @param {object} request\n * @public\n */\n\nfunction Negotiator(request) {\n if (!(this instanceof Negotiator)) {\n return new Negotiator(request);\n }\n\n this.request = request;\n}\n\nNegotiator.prototype.charset = function charset(available) {\n var set = this.charsets(available);\n return set && set[0];\n};\n\nNegotiator.prototype.charsets = function charsets(available) {\n return preferredCharsets(this.request.headers['accept-charset'], available);\n};\n\nNegotiator.prototype.encoding = function encoding(available, opts) {\n var set = this.encodings(available, opts);\n return set && set[0];\n};\n\nNegotiator.prototype.encodings = function encodings(available, options) {\n var opts = options || {};\n return preferredEncodings(this.request.headers['accept-encoding'], available, opts.preferred);\n};\n\nNegotiator.prototype.language = function language(available) {\n var set = this.languages(available);\n return set && set[0];\n};\n\nNegotiator.prototype.languages = function languages(available) {\n return preferredLanguages(this.request.headers['accept-language'], available);\n};\n\nNegotiator.prototype.mediaType = function mediaType(available) {\n var set = this.mediaTypes(available);\n return set && set[0];\n};\n\nNegotiator.prototype.mediaTypes = function mediaTypes(available) {\n return preferredMediaTypes(this.request.headers.accept, available);\n};\n\n// Backwards compatibility\nNegotiator.prototype.preferredCharset = Negotiator.prototype.charset;\nNegotiator.prototype.preferredCharsets = Negotiator.prototype.charsets;\nNegotiator.prototype.preferredEncoding = Negotiator.prototype.encoding;\nNegotiator.prototype.preferredEncodings = Negotiator.prototype.encodings;\nNegotiator.prototype.preferredLanguage = Negotiator.prototype.language;\nNegotiator.prototype.preferredLanguages = Negotiator.prototype.languages;\nNegotiator.prototype.preferredMediaType = Negotiator.prototype.mediaType;\nNegotiator.prototype.preferredMediaTypes = Negotiator.prototype.mediaTypes;\n", "import { Graffiti, type GraffitiSession } from \"@graffiti-garden/api\";\nimport { GraffitiLocalIdentity } from \"./identity\";\nimport { GraffitiLocalObjects, type GraffitiLocalOptions } from \"./objects\";\nimport { GraffitiLocalMedia } from \"./media\";\n\nexport type { GraffitiLocalOptions };\n\n/**\n * A local implementation of the [Graffiti API](https://api.graffiti.garden/classes/Graffiti.html)\n * based on [PouchDB](https://pouchdb.com/). PouchDb will automatically persist data in a local\n * database, either in the browser or in Node.js.\n * It can also be configured to work with an external [CouchDB](https://couchdb.apache.org/) server,\n * although using it with a remote server will not be secure.\n */\nexport class GraffitiLocal implements Graffiti {\n protected graffitiLocalIdentity = new GraffitiLocalIdentity();\n login = this.graffitiLocalIdentity.login.bind(this.graffitiLocalIdentity);\n logout = this.graffitiLocalIdentity.logout.bind(this.graffitiLocalIdentity);\n handleToActor = this.graffitiLocalIdentity.handleToActor.bind(\n this.graffitiLocalIdentity,\n );\n actorToHandle = this.graffitiLocalIdentity.actorToHandle.bind(\n this.graffitiLocalIdentity,\n );\n sessionEvents = this.graffitiLocalIdentity.sessionEvents;\n\n protected graffitiLocalObjects: GraffitiLocalObjects;\n post: Graffiti[\"post\"];\n get: Graffiti[\"get\"];\n delete: Graffiti[\"delete\"];\n discover: Graffiti[\"discover\"];\n continueDiscover: Graffiti[\"continueDiscover\"];\n\n protected graffitiLocalMedia: GraffitiLocalMedia;\n postMedia: Graffiti[\"postMedia\"];\n getMedia: Graffiti[\"getMedia\"];\n deleteMedia: Graffiti[\"deleteMedia\"];\n\n constructor(options?: GraffitiLocalOptions) {\n this.graffitiLocalObjects = new GraffitiLocalObjects(options);\n this.post = this.graffitiLocalObjects.post.bind(this.graffitiLocalObjects);\n this.get = this.graffitiLocalObjects.get.bind(this.graffitiLocalObjects);\n this.delete = this.graffitiLocalObjects.delete.bind(\n this.graffitiLocalObjects,\n );\n this.discover = this.graffitiLocalObjects.discover.bind(\n this.graffitiLocalObjects,\n );\n this.continueDiscover = this.graffitiLocalObjects.continueDiscover.bind(\n this.graffitiLocalObjects,\n );\n\n this.graffitiLocalMedia = new GraffitiLocalMedia(this.graffitiLocalObjects);\n this.postMedia = this.graffitiLocalMedia.postMedia.bind(\n this.graffitiLocalMedia,\n );\n this.getMedia = this.graffitiLocalMedia.getMedia.bind(\n this.graffitiLocalMedia,\n );\n this.deleteMedia = this.graffitiLocalMedia.deleteMedia.bind(\n this.graffitiLocalMedia,\n );\n }\n}\n", "import type {\n GraffitiObjectUrl,\n GraffitiObject,\n GraffitiObjectBase,\n GraffitiSession,\n GraffitiPostObject,\n GraffitiObjectStream,\n GraffitiObjectStreamContinue,\n} from \"./2-types\";\nimport type { JSONSchema } from \"json-schema-to-ts\";\n\n/**\n * This API describes a small but powerful set of methods that\n * can be used to create many different kinds of social applications,\n * from applications like Twitter, to Messenger, to Wikipedia, to many more new designs.\n * See the [Graffiti project website](https://graffiti.garden)\n * for links to example applications. Additionally, apps built on top\n * of the API interoperate with each other so you can seamlessly switch\n * between apps without losing your friends or data.\n *\n * These API methods should satisfy all of an application's needs for\n * the communication, storage, and access management of social data.\n * The rest of the application can be built with standard client-side\n * user interface tools to present and interact with that data\u2014no server code necessary!\n *\n * The Typescript code for this API is [open source on Github](https://github.com/graffiti-garden/api).\n *\n * There are several different implementations of this Graffiti API available,\n * including a [federated implementation](https://github.com/graffiti-garden/implementation-remote),\n * that lets people choose where their data is stored (you do not need to host your own server)\n * and a [local implementation](https://github.com/graffiti-garden/implementation-local)\n * that can be used for testing and development. Different implementations can\n * be swapped-in in the future without changing the API or any of the apps built on\n * top of it. In fact, we're working on an end-to-end encrypted version now!\n * [Follow Theia on BlueSky for updates](https://bsky.app/profile/theias.place).\n *\n * On the other side of the stack, there is [Vue plugin](https://vue.graffiti.garden/variables/GraffitiPlugin.html)\n * that wraps around this API to provide reactivity. Other plugin frameworks\n * and high-level libraries will be available in the future.\n *\n * ## API Overview\n *\n * The Graffiti API provides applications with methods for {@link login} and {@link logout},\n * methods to interact with data objects using standard database operations ({@link post}, {@link get}, and {@link delete}),\n * and a method to {@link discover} data objects created by others.\n * These data objects have a couple structured properties:\n * - {@link GraffitiObjectBase.url | `url`} (string): A globally unique identifier and locator for the object.\n * - {@link GraffitiObjectBase.actor | `actor`} (string): An unforgeable identifier for the creator of the object.\n * - {@link GraffitiObjectBase.allowed | `allowed`} (string[] | undefined): An array of the actors who are allowed to access the object (undefined for public objects).\n * - {@link GraffitiObjectBase.channels | `channels`} (string[]): An array of the *contexts* in which the object should appear.\n *\n * All other data is stored in the object's unstructured {@link GraffitiObjectBase.value | `value`} property.\n * This data can be used to represent social artifacts (e.g. posts, profiles) and activities (e.g. likes, follows).\n * For example, a post might have the value:\n\n * ```js\n * {\n * title: \"My First Post\",\n * content: \"Hello, world!\",\n * published: 1630483200000\n * }\n * ```\n *\n * a profile might have the value:\n *\n * ```js\n * {\n * name: \"Theia Henderson\",\n * pronouns: \"she/her\",\n * describes: \"did:web:theias.place\" // Theia's actor ID\n * }\n * ```\n *\n * and a \"Like\" might have the value:\n *\n * ```js\n * {\n * activity: \"Like\",\n * target: \"graffiti:remote:pod.graffiti.garden/12345\" // The URL of the graffiti object being liked\n * }\n * ```\n *\n * New social artifacts and activities can be easily created, simply\n * by creating new objects with appropriate properties. Despite the lack of\n * structure, we expect Graffiti object properties to adhere to a \"[folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\",\n * similar to hashtags. Any string can be used as a hashtag on Twitter,\n * but there is social value in using the same hashtags at other people and\n * so a structure naturally emerges. Similarly, Graffiti objects\n * can have arbitrary properties but if people use the same properties as each other,\n * their apps will interoperate, which has social value.\n *\n * For a more complete and detailed overview of Graffiti's design, please\n * refer to [this section of the Graffiti paper](https://dl.acm.org/doi/10.1145/3746059.3747627#sec-3),\n * published in ACM UIST 2025. The paper also overviews {@link GraffitiObjectBase.channels | `channels`},\n * which are Graffiti's means of organizing data contextually, and a concept called \"total reification\",\n * which handles explains how moderation, collaboration, and other interactions are managed.\n *\n * @groupDescription 1 - Single-Object Methods\n * Methods for {@link post | creating}, {@link get | reading},\n * and {@link delete | deleting} {@link GraffitiObjectBase | Graffiti objects}.\n * @groupDescription 2 - Multi-Object Methods\n * Methods that retrieve or accumulate information about multiple {@link GraffitiObjectBase | Graffiti objects} at a time.\n * @groupDescription 3 - Media Methods\n * Methods for {@link postMedia | creating}, {@link getMedia | reading},\n * and {@link deleteMedia | deleting} media data.\n * @groupDescription 4 - Identity Methods\n * Methods and properties for logging in and out.\n */\nexport abstract class Graffiti {\n /**\n * Creates a new {@link GraffitiObjectBase | object}.\n *\n * @returns Returns the object that has been posted, complete with its\n * assigned {@link GraffitiObjectBase.url | `url`} and\n * {@link GraffitiObjectBase.actor | `actor`}.\n *\n * @group 1 - Single-Object Methods\n */\n abstract post<Schema extends JSONSchema>(\n /**\n * An object to post, minus its {@link GraffitiObjectBase.url | `url`} and\n * {@link GraffitiObjectBase.actor | `actor`}, which will be assigned once posted.\n * This object is statically type-checked against the [JSON schema](https://json-schema.org/) that can be optionally provided\n * as the generic type parameter. It is recommended to use a schema to\n * ensure that the posted object matches subsequent {@link get} or {@link discover}\n * methods.\n */\n partialObject: GraffitiPostObject<Schema>,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<GraffitiObject<Schema>>;\n\n /**\n * Retrieves an object from a given {@link GraffitiObjectBase.url | `url`} matching\n * the provided `schema`.\n *\n * If the retreiving {@link GraffitiObjectBase.actor | `actor`} is not\n * the object's `actor`,\n * the object's {@link GraffitiObjectBase.allowed | `allowed`} and\n * {@link GraffitiObjectBase.channels | `channels`} properties are\n * not revealed, similar to a BCC email.\n *\n * @returns Returns the retrieved object.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has been deleted, or the actor is not\n * {@link GraffitiObjectBase.allowed | `allowed`} to access it.\n *\n * @throws {@link GraffitiErrorSchemaMismatch} if the retrieved object does not match the provided schema.\n *\n * @group 1 - Single-Object Methods\n */\n abstract get<Schema extends JSONSchema>(\n /**\n * The location of the object to get.\n */\n url: string | GraffitiObjectUrl,\n /**\n * The JSON schema to validate the retrieved object against.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}. If no `session` is provided,\n * the retrieved object's {@link GraffitiObjectBase.allowed | `allowed`}\n * property must be `undefined`.\n */\n session?: GraffitiSession | null,\n ): Promise<GraffitiObject<Schema>>;\n\n /**\n * Deletes an object from a given {@link GraffitiObjectBase.url | `url`}\n * that had previously been {@link post | `post`ed}.\n * The deleting {@link GraffitiObjectBase.actor | `actor`} must be the same as the\n * `actor` that created the object.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,\n * or the actor is not {@link GraffitiObjectBase.allowed | `allowed`} to access it.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * is not the same `actor` as the one who created the object.\n *\n * @group 1 - Single-Object Methods\n */\n abstract delete(\n /**\n * The location of the object to delete.\n */\n url: string | GraffitiObjectUrl,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * Discovers objects created by any actor that are contained\n * in at least one of the given {@link GraffitiObjectBase.channels | `channels`}\n * and match the given [JSON Schema](https://json-schema.org).\n *\n * Objects are returned asynchronously as they are discovered but the stream\n * will end once all leads have been exhausted.\n * The {@link GraffitiObjectStream} ends by returning a\n * {@link GraffitiObjectStreamReturn.continue | `continue`} method and a\n * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string,\n * each of which can be be used to poll for new objects.\n * The `continue` method preserves the type safety of the stream and the `cursor`\n * string can be serialized to continue the stream after an application is closed\n * and reopened.\n *\n * `discover` will not return objects that the querying {@link GraffitiObjectBase.actor | `actor`}\n * is not {@link GraffitiObjectBase.allowed | `allowed`} to access.\n * If the `actor` is not the creator of a discovered object,\n * the allowed list will be masked to only contain the querying actor if the\n * allowed list is not `undefined` (public). Additionally, if the actor is not the\n * creator of a discovered object, any {@link GraffitiObjectBase.channels | `channels`}\n * not specified by the `discover` method will not be revealed. This masking happens\n * before the object is validated against the supplied `schema`.\n *\n * Since different implementations may fetch data from multiple sources there is\n * no guarentee on the order that objects are returned in.\n *\n * @returns Returns a stream of objects that match the given {@link GraffitiObjectBase.channels | `channels`}\n * and [JSON Schema](https://json-schema.org).\n *\n * @group 2 - Multi-Object Methods\n */\n abstract discover<Schema extends JSONSchema>(\n /**\n * The {@link GraffitiObjectBase.channels | `channels`} that objects must be associated with.\n */\n channels: string[],\n /**\n * A [JSON Schema](https://json-schema.org) that objects must satisfy.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}. If no `session` is provided,\n * only objects that have no {@link GraffitiObjectBase.allowed | `allowed`}\n * property will be returned.\n */\n session?: GraffitiSession | null,\n ): GraffitiObjectStream<Schema>;\n\n /**\n * Continues a {@link GraffitiObjectStream} from a given\n * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string.\n * The continuation will return new objects that have been {@link post | `post`ed}\n * that match the original stream, and also returns the\n * {@link GraffitiObjectBase.url | `url`}s of objects that\n * have been {@link delete | `delete`d}, as marked by a `tombstone`.\n *\n * The `cursor` allows the client to\n * serialize the state of the stream and continue it later.\n * However this method loses any typing information that was\n * present in the original stream. For better type safety\n * and when serializing is not necessary, use the\n * {@link GraffitiObjectStreamReturn.continue | `continue`} method\n * instead, which is returned along with the `cursor` at the\n * end of the original stream.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * provided in the `session` is not the same as the `actor`\n * that initiated the original stream.\n *\n * @group 2 - Multi-Object Methods\n */\n abstract continueDiscover(\n cursor: string,\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<{}>;\n\n /**\n * Uploads media data, such as an image or video.\n *\n * Unlike structured {@link GraffitiObjectBase | objects},\n * media is not indexed for {@link discover | `discover`y} and\n * must be retrieved by its exact URL using {@link getMedia}\n *\n * @returns The URL that the media was posted to.\n *\n * @group 3 - Media Methods\n */\n abstract postMedia(\n media: {\n /**\n * The binary data of the media to be uploaded,\n * along with its [media type](https://www.iana.org/assignments/media-types/media-types.xhtml),\n * formatted as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob).\n */\n data: Blob;\n /**\n * An optional list, identical in function to an object's\n * {@link GraffitiObjectBase.allowed | `allowed`} property,\n * that specifies the {@link GraffitiObjectBase.actor | `actor`}s\n * who are allowed to access the media. If the list is `undefined`\n * or `null`, anyone with the URL can access the media. If the list\n * is empty, only the {@link GraffitiObjectBase.actor | `actor`}\n * who {@link postMedia | `post`ed} the media can access it.\n */\n allowed?: string[] | null;\n },\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<string>;\n\n /**\n * Deletes media previously {@link postMedia | `post`ed} to a given URL.\n *\n * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * provided in the `session` is not the same as the `actor` that {@link postMedia | `post`ed}\n * the media.\n *\n * @group 3 - Media Methods\n */\n abstract deleteMedia(\n /**\n * A globally unique identifier and locator for the media.\n */\n mediaUrl: string,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * Retrieves media from the given media URL, adhering to the given requirements.\n *\n * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.\n *\n * @throws {@link GraffitiErrorTooLarge} if the media exceeds the given `maxBytes`.\n *\n * @throws {@link GraffitiErrorNotAcceptable} if the media does not match the given\n * `accept` specification.\n *\n * @returns The URL of the retrieved media, as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob)\n * and the {@link GraffitiObjectBase.actor | `actor`} that posted it.\n *\n * @group 3 - Media Methods\n */\n abstract getMedia(\n /**\n * A globally unique identifier and locator for the media.\n */\n mediaUrl: string,\n /**\n * A set of requirements the retrieved media must meet.\n */\n requirements: {\n /**\n * A list of acceptable media types for the retrieved media,\n * formatted as like an [HTTP Accept header](https://httpwg.org/specs/rfc9110.html#field.accept)\n */\n accept?: string;\n /**\n * The maximum acceptable size, in bytes, of the media.\n */\n maxBytes?: number;\n },\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session?: GraffitiSession | null,\n ): Promise<{\n data: Blob;\n actor: string;\n allowed?: string[] | null;\n }>;\n\n /**\n * Begins the login process. Depending on the implementation, this may\n * involve redirecting to a login page or opening a popup,\n * so it should always be called in response to a gesture, such as clicking\n * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).\n *\n * The {@link GraffitiSession | session} object is returned\n * asynchronously via {@link Graffiti.sessionEvents | sessionEvents}\n * as a {@link GraffitiLoginEvent} with event type `login`.\n *\n * @group 4 - Identity Methods\n */\n abstract login(\n /**\n * A suggested actor to login as. For example, if a user tries to\n * edit a post but are not logged in, the interface can infer that\n * they might want to log in as the actor who created the post\n * they are attempting to edit.\n *\n * Even if provided, the implementation should allow the user\n * to log in as a different actor if they choose.\n */\n actor?: string,\n ): Promise<void>;\n\n /**\n * Begins the logout process for a particular {@link GraffitiSession | session}. Depending on the implementation, this may\n * involve redirecting the user to a logout page or opening a popup,\n * so it should always be called in response to a gesture, such as clicking\n * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).\n *\n * A confirmation will be returned asynchronously via\n * {@link Graffiti.sessionEvents | sessionEvents}\n * as a {@link GraffitiLogoutEvent} as event type `logout`.\n *\n * @group 4 - Identity Methods\n */\n abstract logout(\n /**\n * The {@link GraffitiSession | session} object to logout.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * An event target that can be used to listen for the following\n * events and their corresponding event types:\n * - `login` - {@link GraffitiLoginEvent}\n * - `logout` - {@link GraffitiLogoutEvent}\n * - `initialized` - {@link GraffitiSessionInitializedEvent}\n *\n * @group 4 - Identity Methods\n */\n abstract readonly sessionEvents: EventTarget;\n\n /**\n * Retrieves the human-readable handle associated\n * with the given actor. The handle may change over time\n * and so it should be used for display purposes only.\n *\n * The inverse of {@link handleToActor}.\n *\n * @throws {@link GraffitiErrorNotFound} if a handle cannot be\n * found for the given actor.\n *\n * @returns A human-readable handle for the given actor.\n *\n * @group 4 - Identity Methods\n */\n abstract actorToHandle(actor: string): Promise<string>;\n\n /**\n * Retrieves the actor ID associated with the given handle.\n *\n * The inverse of {@link actorToHandle}.\n *\n * @throws {@link GraffitiErrorNotFound} if there is no actor\n * with the given handle.\n *\n * @returns The actor ID for the given handle.\n *\n * @group 4 - Identity Methods\n */\n abstract handleToActor(handle: string): Promise<string>;\n}\n", "import type { JSONSchema, FromSchema } from \"json-schema-to-ts\";\n\n/**\n * Objects are the atomic unit in Graffiti that can represent both data (*e.g.* a social media post or profile)\n * and activities (*e.g.* a like or follow).\n *\n * Each object embeds the {@link actor | `actor`} that created it.\n * Object content and metadata are static but an object may be deleted by\n * its creating {@link actor | `actor`}.\n *\n * An object's content is stored in its {@link value | `value`} property, which can be any JSON\n * object. However, it is recommended to use properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)\n * or properties that emerge in the Graffiti [folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\n * to promote interoperability.\n *\n * Each object is globally addressable via its {@link url | `url`}.\n *\n * An object's {@link channels | `channels`} and {@link allowed | `allowed`} properties\n * are set by an objects creator to shape the visibility of and access to their object.\n */\nexport interface GraffitiObjectBase {\n /**\n * The object's content as freeform JSON. We recommend using properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)\n * or properties that emerge in the Graffiti [folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\n * to promote interoperability.\n */\n value: {};\n\n /**\n * An array of URIs the creator associates with the object. Objects can only be found by querying\n * one of the object's channels using the\n * {@link Graffiti.discover} method. This allows creators to express the intended audience of their object\n * which helps to prevent [context collapse](https://en.wikipedia.org/wiki/Context_collapse) even\n * in the highly interoperable ecosystem that Graffiti envisions. For example, channel URIs may be:\n * - A actor's own {@link actor | `actor`} URI. Posting an object to this channel is a way to broadcast\n * the object to the actor's followers, like posting a tweet.\n * - The URL of a Graffiti post. Posting an object to this channel is a way to broadcast to anyone viewing\n * the post, like commenting on a tweet.\n * - A URI representing a topic. Posting an object to this channel is a way to broadcast to anyone interested\n * in that topic, like posting in a subreddit.\n */\n channels: string[];\n\n /**\n * An optional array of {@link actor | `actor`} URIs that the creator allows to access the object.\n * If no `allowed` array is provided, the object can be accessed by anyone (so long as they\n * also know the right {@link channels | `channel` } to look in). An object can always be accessed by its creator, even if\n * the `allowed` array is empty.\n *\n * The `allowed` array is not revealed to actors other than the creator, like\n * a BCC email. An actor may choose to add a `to` property to the object's {@link value | `value`} to indicate\n * other recipients, however this is not enforced by Graffiti and may not accurately reflect the actual `allowed` array.\n *\n * `allowed` can be combined with {@link channels | `channels`}. For example, to send someone a direct message\n * the sender should post their object to the channel of the recipient's {@link actor | `actor`} URI to notify them of the message and also add\n * the recipient's {@link actor | `actor`} URI to the `allowed` array to prevent others from seeing the message.\n */\n allowed?: string[] | null;\n\n /**\n * The URI of the `actor` that {@link Graffiti.post | created } the object.\n * This `actor` has the unique permission to\n * {@link Graffiti.delete | delete} the object.\n *\n * We borrow the term actor from the ActivityPub because\n * [like in ActivityPub](https://www.w3.org/TR/activitypub/#h-note-0)\n * there is not necessarily a one-to-one mapping between actors and people/users.\n * Multiple people can share the same actor or one person can have multiple actors.\n * Actors can also be bots.\n *\n * In Graffiti, actors are always globally unique URIs which\n * allows them to also function as {@link channels | `channels`}.\n */\n actor: string;\n\n /**\n * A globally unique identifier and locator for the object. It can be used to point to\n * an object or to retrieve the object directly with {@link Graffiti.get}.\n *\n * An object's URL is generated when the object is first created and\n * should include sufficient randomness to prevent collisions\n * and guessing. The URL starts with a \"scheme,\" just like web URLs start with `http` or `https`, to indicate\n * to indicate the particular Graffiti implementation. This allows for applications\n * to pull from multiple coexisting Graffiti implementations without collision.\n * Existing schemes include `graffiti:local:` for objects stored locally\n * (see the [local implementation](https://github.com/graffiti-garden/implementation-local))\n * and `graffiti:remote:` for objects stored on Graffiti-specific web servers (see the [remote implementation](https://github.com/graffiti-garden/implementation-remote))\n * Options available in the future might include `graffiti:solid:` for objects stored on Solid servers\n * or `graffiti:p2p:` for objects stored on a peer-to-peer network.\n */\n url: string;\n}\n\n/**\n * This type constrains the {@link GraffitiObjectBase} type to adhere to a\n * particular [JSON schema](https://json-schema.org/).\n * This allows for static type-checking of an object's {@link GraffitiObjectBase.value | `value`}\n * which is otherwise a freeform JSON object.\n *\n * Schema-aware objects are returned by {@link Graffiti.get} and {@link Graffiti.discover}.\n */\nexport type GraffitiObject<Schema extends JSONSchema> = GraffitiObjectBase &\n FromSchema<Schema & typeof GraffitiPostObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiObjectBase} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiObjectJSONSchema = {\n type: \"object\",\n properties: {\n value: { type: \"object\" },\n channels: { type: \"array\", items: { type: \"string\" } },\n allowed: { type: \"array\", items: { type: \"string\" }, nullable: true },\n url: { type: \"string\" },\n actor: { type: \"string\" },\n },\n additionalProperties: false,\n required: [\"value\", \"channels\", \"actor\", \"url\"],\n} as const satisfies JSONSchema;\n\n/**\n * This is an object containing only the {@link GraffitiObjectBase.url | `url`}\n * property of a {@link GraffitiObjectBase | GraffitiObject}.\n * It is used as a utility type so that applications can call\n * {@link Graffiti.delete} directly on an object\n * rather than on `object.url`.\n */\nexport type GraffitiObjectUrl = Pick<GraffitiObjectBase, \"url\">;\n\n/**\n * This object is a subset of {@link GraffitiObjectBase} that must be constructed locally before calling {@link Graffiti.post}.\n * This local copy ignores system-generated properties\n * ({@link GraffitiObjectBase.url | `url`} and {@link GraffitiObjectBase.actor | `actor`}),\n * and may be statically typed with\n * a [JSON schema](https://json-schema.org/) to prevent the accidental creation of erroneous objects.\n *\n * This local object must have a {@link GraffitiObjectBase.value | `value`} and {@link GraffitiObjectBase.channels | `channels`}\n * and may optionally have an {@link GraffitiObjectBase.allowed | `allowed`} property.\n */\nexport type GraffitiPostObject<Schema extends JSONSchema> = Pick<\n GraffitiObjectBase,\n \"value\" | \"channels\" | \"allowed\"\n> &\n FromSchema<Schema & typeof GraffitiPostObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiPostObject} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiPostObjectJSONSchema = {\n ...GraffitiObjectJSONSchema,\n required: [\"value\", \"channels\"],\n} as const satisfies JSONSchema;\n\n/**\n * This object contains information that the underlying implementation can\n * use to authenticate a particular {@link GraffitiObjectBase.actor | `actor`}.\n * This object is required of all {@link Graffiti} methods\n * that modify objects or media and is optional for methods that read objects.\n *\n * At a minimum the `session` object must contain the\n * {@link GraffitiSession.actor | `actor`} URI to authenticate with.\n * However it is likely that the `session` object must contain other\n * implementation-specific properties.\n * For example, a Solid implementation might include a\n * [`fetch`](https://docs.inrupt.com/developer-tools/api/javascript/solid-client-authn-browser/functions.html#fetch)\n * function. A distributed implementation may include\n * a cryptographic signature.\n *\n * As to why the `session` object is passed as an argument to every method\n * rather than being an internal property of the {@link Graffiti} instance,\n * this is primarily for type-checking to catch bugs related to login state.\n * Graffiti applications can expose some functionality to people who are not logged in\n * with {@link Graffiti.get} and {@link Graffiti.discover} but without type-checking\n * the `session` it can be easy to forget to hide buttons that trigger\n * other methods that require login.\n *\n * Passing the `session` object per-method also allows for multiple sessions\n * to be used within the same application, like an Email client fetching from\n * multiple accounts.\n */\nexport interface GraffitiSession {\n /**\n * The {@link GraffitiObjectBase.actor | `actor`} to authenticate with.\n */\n actor: string;\n}\n\n/**\n * A stream of data that are returned by {@link Graffiti.discover}.\n *\n * Errors are returned within the stream rather than as\n * exceptions that would halt the entire stream. This is because\n * some implementations may pull data from multiple sources\n * including some that may be unreliable. In many cases,\n * these errors can be safely ignored.\n * See {@link GraffitiObjectStreamError}.\n *\n * The stream is an [`AsyncGenerator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)\n * that can be iterated over using `for await` loops or calling `next` on the generator.\n * The stream can be terminated by breaking out of a loop calling `return` on the generator.\n *\n * The stream ends by returning a {@link GraffitiObjectStreamReturn.continue | `continue`}\n * function and a {@link GraffitiObjectStreamReturn.cursor | `cursor`} string,\n * each of which can be used to resume the stream from where it left off.\n */\nexport type GraffitiObjectStream<Schema extends JSONSchema> = AsyncGenerator<\n GraffitiObjectStreamError | GraffitiObjectStreamEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n>;\n\n/**\n * An error that can occur in a {@link GraffitiObjectStream}.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamError {\n /**\n * The error that occurred while streaming data.\n */\n error: Error;\n /**\n * The origin that the error occurred. It will include\n * the scheme of the Graffiti implementation used and other\n * implementation-specific information like a hostname.\n */\n origin: string;\n}\n\n/**\n * A successful result from a {@link GraffitiObjectStream} or\n * {@link GraffitiObjectStreamContinue} that includes an object.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamEntry<Schema extends JSONSchema> {\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamError}\n */\n error?: undefined;\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamContinueTombstone}\n */\n tombstone?: undefined;\n /**\n * The object returned by the stream.\n */\n object: GraffitiObject<Schema>;\n}\n\n/**\n * A result from a {@link GraffitiObjectStreamContinue} that indicated\n * an object has been deleted since the original stream was run.\n * Only sparse metadata about the deleted object is returned to respect\n * the deleting actor's privacy.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamContinueTombstone {\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamError}\n */\n error?: undefined;\n /**\n * Use this property to differentiate a tombstone from a\n * {@link GraffitiObjectStreamEntry}.\n */\n tombstone: true;\n /**\n * Sparse metadata about the deleted object. The full object is not returned\n * to respect an actor's privacy.\n */\n object: {\n /**\n * The {@link GraffitiObjectBase.url | `url`} of the deleted object.\n */\n url: string;\n };\n}\n\n/**\n * A continuation of the {@link GraffitiObjectStream} type can include\n * both objects and tombstones of deleted objects.\n *\n * @internal\n */\nexport type GraffitiObjectStreamContinueEntry<Schema extends JSONSchema> =\n | GraffitiObjectStreamEntry<Schema>\n | GraffitiObjectStreamContinueTombstone;\n\n/**\n * The output of a {@link GraffitiObjectStream} or a {@link GraffitiObjectStreamContinue}\n * that allows the stream to be continued from where it left off.\n *\n * The {@link continue} function preserves the typing of the original stream,\n * where as the {@link cursor} string can be serialized for use after a person\n * has closed and reopened an application.\n *\n * The continued stream may include `tombstone`s of objects that have been\n * deleted since the original stream was run. See {@link GraffitiObjectStreamContinueTombstone}.\n * The continued stream may also return some objects that were already\n * returned by the original stream, depending on how much state the\n * underlying implementation is able to preserve.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamReturn<Schema extends JSONSchema> {\n /**\n * @returns A function that creates new stream that continues from where the original stream left off.\n * It preserves the typing of the original stream.\n */\n continue: (\n session?: GraffitiSession | null,\n ) => GraffitiObjectStreamContinue<Schema>;\n /**\n * A string that can be serialized and stored to resume the stream later.\n * It must be passed to the {@link Graffiti.continueDiscover} method\n * to resume the stream.\n */\n cursor: string;\n}\n\n/**\n * A continutation of the {@link GraffitiObjectStream} type, as returned by\n * the {@link GraffitiObjectStreamReturn.continue} or by using\n * {@link GraffitiObjectStreamReturn.cursor} with {@link Graffiti.continueDiscover}.\n *\n * The continued stream may include `tombstone`s of objects that have been\n * deleted since the original stream was run. See {@link GraffitiObjectStreamContinueTombstone}.\n *\n * @internal\n */\nexport type GraffitiObjectStreamContinue<Schema extends JSONSchema> =\n AsyncGenerator<\n GraffitiObjectStreamError | GraffitiObjectStreamContinueEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n >;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a actor logs in manually from {@link Graffiti.login}\n * or when their session is restored from a previous login.\n * The event name to listen for is `login`.\n */\nexport type GraffitiLoginEvent = CustomEvent<\n | {\n error: Error;\n session?: undefined;\n }\n | {\n error?: undefined;\n session: GraffitiSession;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a actor logs out either manually with {@link Graffiti.logout}\n * or when their session times out or otherwise becomes invalid.\n * The event name to listen for is `logout`.\n */\nexport type GraffitiLogoutEvent = CustomEvent<\n | {\n error: Error;\n actor?: string;\n }\n | {\n error?: undefined;\n actor: string;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * after an application has attempted to complete any login redirects\n * and restore any previously active sessions.\n * Successful session restores will be returned in parallel as\n * their own {@link GraffitiLoginEvent} events.\n *\n * This event optionally returns an `href` property\n * representing the URL that originated a login request,\n * which may be useful for redirecting the user back to\n * the page they were on after login.\n * The event name to listen for is `initialized`.\n */\nexport type GraffitiSessionInitializedEvent = CustomEvent<\n | {\n error?: Error;\n href?: string;\n }\n | null\n | undefined\n>;\n", "export class GraffitiErrorForbidden extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorForbidden\";\n Object.setPrototypeOf(this, GraffitiErrorForbidden.prototype);\n }\n}\n\nexport class GraffitiErrorNotFound extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorNotFound\";\n Object.setPrototypeOf(this, GraffitiErrorNotFound.prototype);\n }\n}\n\nexport class GraffitiErrorInvalidSchema extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorInvalidSchema\";\n Object.setPrototypeOf(this, GraffitiErrorInvalidSchema.prototype);\n }\n}\n\nexport class GraffitiErrorSchemaMismatch extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorSchemaMismatch\";\n Object.setPrototypeOf(this, GraffitiErrorSchemaMismatch.prototype);\n }\n}\n\nexport class GraffitiErrorTooLarge extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorTooLarge\";\n Object.setPrototypeOf(this, GraffitiErrorTooLarge.prototype);\n }\n}\n\nexport class GraffitiErrorNotAcceptable extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorNotAcceptable\";\n Object.setPrototypeOf(this, GraffitiErrorNotAcceptable.prototype);\n }\n}\n", "import type { JSONSchema } from \"json-schema-to-ts\";\nimport type { Ajv } from \"ajv\";\nimport { GraffitiErrorInvalidSchema } from \"./3-errors\";\nimport type {\n GraffitiObjectBase,\n GraffitiObject,\n GraffitiObjectUrl,\n GraffitiSession,\n} from \"./2-types\";\n\nexport function unpackObjectUrl(url: string | GraffitiObjectUrl) {\n return typeof url === \"string\" ? url : url.url;\n}\n\nexport function compileGraffitiObjectSchema<Schema extends JSONSchema>(\n ajv: Ajv,\n schema: Schema,\n) {\n try {\n // Force the validation guard because\n // it is too big for the type checker.\n // Fortunately json-schema-to-ts is\n // well tested against ajv.\n return ajv.compile(schema) as (\n data: GraffitiObjectBase,\n ) => data is GraffitiObject<Schema>;\n } catch (error) {\n throw new GraffitiErrorInvalidSchema(\n error instanceof Error ? error.message : undefined,\n );\n }\n}\n\nexport function isActorAllowedGraffitiObject(\n object: GraffitiObjectBase,\n session?: GraffitiSession | null,\n) {\n return (\n // If there is no allowed list, the actor is allowed.\n !Array.isArray(object.allowed) ||\n // Otherwise...\n (typeof session?.actor === \"string\" &&\n // The actor must be the creator of the object\n (object.actor === session.actor ||\n // Or be on the allowed list\n object.allowed.includes(session.actor)))\n );\n}\n\nexport function maskGraffitiObject(\n object: GraffitiObjectBase,\n channels: string[],\n session?: GraffitiSession | null,\n): void {\n // If the actor is not the creator, mask the object.\n if (object.actor !== session?.actor) {\n // If there is an allowed list, mask it to only include the actor\n // (This assumes the actor is already allowed to access the object)\n object.allowed = object.allowed && session ? [session.actor] : undefined;\n // Mask the channels to only include the channels that are being queried\n object.channels = object.channels.filter((channel) =>\n channels.includes(channel),\n );\n }\n}\n", "import type {\n Graffiti,\n GraffitiLoginEvent,\n GraffitiLogoutEvent,\n GraffitiSessionInitializedEvent,\n} from \"@graffiti-garden/api\";\nimport { decodeBase64, encodeBase64 } from \"./utilities\";\n\nconst DID_LOCAL_PREFIX = \"did:local:\";\n\n/**\n * A class that implements the login methods\n * of the [Graffiti API]() for use in the browser.\n * It is completely insecure and should only be used\n * for testing and demonstrations.\n *\n * It uses `localStorage` to store login state and\n * window prompts rather than an oauth flow for log in.\n * It can be used in node.js but will not persist\n * login state and a proposed username must be provided.\n */\nexport class GraffitiLocalIdentity {\n sessionEvents: Graffiti[\"sessionEvents\"] = new EventTarget();\n\n handleToActor: Graffiti[\"handleToActor\"] = async (handle: string) => {\n const bytes = new TextEncoder().encode(handle);\n const base64 = encodeBase64(bytes);\n return `${DID_LOCAL_PREFIX}${base64}`;\n };\n\n actorToHandle: Graffiti[\"actorToHandle\"] = async (actor: string) => {\n if (!actor.startsWith(DID_LOCAL_PREFIX)) {\n throw new Error(`actor must start with ${DID_LOCAL_PREFIX}`);\n }\n const base64 = actor.slice(DID_LOCAL_PREFIX.length);\n const bytes = decodeBase64(base64);\n return new TextDecoder().decode(bytes);\n };\n\n constructor() {\n // Look for any existing sessions\n const sessionRestorer = async () => {\n // Allow listeners to be added first\n await Promise.resolve();\n\n // Restore previous sessions\n for (const handle of this.getLoggedInHandles()) {\n const event: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: { session: { actor: await this.handleToActor(handle) } },\n });\n this.sessionEvents.dispatchEvent(event);\n }\n\n const event: GraffitiSessionInitializedEvent = new CustomEvent(\n \"initialized\",\n { detail: {} },\n );\n this.sessionEvents.dispatchEvent(event);\n };\n sessionRestorer();\n }\n\n loggedInHandles: string[] = [];\n\n protected getLoggedInHandles(): string[] {\n if (typeof window !== \"undefined\") {\n const handlesString = window.localStorage.getItem(\"graffiti-handles\");\n return handlesString\n ? handlesString.split(\",\").map(decodeURIComponent)\n : [];\n } else {\n return this.loggedInHandles;\n }\n }\n\n protected setLoggedInHandles(handles: string[]) {\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\"graffiti-handles\", handles.join(\",\"));\n } else {\n this.loggedInHandles = handles;\n }\n }\n\n login: Graffiti[\"login\"] = async (actor) => {\n // Wait a tick for the browser to update the UI\n await new Promise((resolve) => setTimeout(resolve, 0));\n\n let handle = actor ? await this.actorToHandle(actor) : undefined;\n\n if (typeof window !== \"undefined\") {\n const response = window.prompt(\"Choose a username to log in.\", handle);\n handle = response ?? undefined;\n }\n\n if (!handle) {\n const detail: GraffitiLoginEvent[\"detail\"] = {\n error: new Error(\"No handle provided to login\"),\n };\n const event: GraffitiLoginEvent = new CustomEvent(\"login\", { detail });\n this.sessionEvents.dispatchEvent(event);\n } else {\n const existingHandles = this.getLoggedInHandles();\n if (!existingHandles.includes(handle)) {\n this.setLoggedInHandles([...existingHandles, handle]);\n }\n // Refresh the page to simulate oauth\n window.location.reload();\n }\n };\n\n logout: Graffiti[\"logout\"] = async (session) => {\n const handle = await this.actorToHandle(session.actor);\n const existingHandles = this.getLoggedInHandles();\n const exists = existingHandles.includes(handle);\n if (exists) {\n this.setLoggedInHandles(existingHandles.filter((h) => h !== handle));\n }\n\n const detail: GraffitiLogoutEvent[\"detail\"] = exists\n ? {\n actor: session.actor,\n }\n : {\n actor: session.actor,\n error: new Error(\"Not logged in with that actor\"),\n };\n\n const event: GraffitiLogoutEvent = new CustomEvent(\"logout\", { detail });\n this.sessionEvents.dispatchEvent(event);\n };\n}\n", "export function encodeBase64(bytes: Uint8Array): string {\n // Convert it to base64\n const base64 = btoa(String.fromCodePoint(...bytes));\n // Make sure it is url safe\n return base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/\\=+$/, \"\");\n}\n\nexport function decodeBase64(base64Url: string): Uint8Array {\n // Undo url-safe base64\n let base64 = base64Url.replace(/-/g, \"+\").replace(/_/g, \"/\");\n // Add padding if necessary\n while (base64.length % 4 !== 0) base64 += \"=\";\n // Decode\n return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));\n}\n\nexport function randomBase64(numBytes: number = 32): string {\n // Generate random bytes\n const bytes = new Uint8Array(numBytes);\n crypto.getRandomValues(bytes);\n return encodeBase64(bytes);\n}\n\nconst OBJECT_URL_PREFIX = \"graffiti:object:\";\nconst MEDIA_URL_PREFIX = \"graffiti:media:\";\n\nexport function encodeGraffitiUrl(actor: string, id: string, prefix: string) {\n return `${prefix}${encodeURIComponent(actor)}:${encodeURIComponent(id)}`;\n}\nexport function encodeObjectUrl(actor: string, id: string) {\n return encodeGraffitiUrl(actor, id, OBJECT_URL_PREFIX);\n}\nexport function encodeMediaUrl(actor: string, id: string) {\n return encodeGraffitiUrl(actor, id, MEDIA_URL_PREFIX);\n}\n\nexport function decodeGraffitiUrl(url: string, prefix: string) {\n if (!url.startsWith(prefix)) {\n throw new Error(`URL does not start with ${prefix}`);\n }\n const slices = url.slice(prefix.length).split(\":\");\n if (slices.length !== 2) {\n throw new Error(\"URL has too many colon-seperated parts\");\n }\n const [actor, id] = slices.map(decodeURIComponent);\n return { actor, id };\n}\nexport function decodeObjectUrl(url: string) {\n return decodeGraffitiUrl(url, OBJECT_URL_PREFIX);\n}\nexport function decodeMediaUrl(url: string) {\n return decodeGraffitiUrl(url, MEDIA_URL_PREFIX);\n}\n\nexport async function blobToBase64(blob: Blob): Promise<string> {\n if (typeof FileReader !== \"undefined\") {\n return new Promise((resolve, reject) => {\n const r = new FileReader();\n r.onload = () => {\n if (typeof r.result === \"string\") {\n resolve(r.result);\n } else {\n reject(new Error(\"Unexpected result type\"));\n }\n };\n r.onerror = reject;\n r.readAsDataURL(blob);\n });\n }\n\n if (typeof Buffer !== \"undefined\") {\n const ab = await blob.arrayBuffer();\n return `data:${blob.type};base64,${Buffer.from(ab).toString(\"base64\")}`;\n }\n\n throw new Error(\"Unsupported environment\");\n}\n\nexport async function base64ToBlob(dataUrl: string) {\n const response = await fetch(dataUrl);\n return await response.blob();\n}\n", "import type {\n Graffiti,\n GraffitiObjectBase,\n JSONSchema,\n GraffitiSession,\n GraffitiObjectStreamContinue,\n GraffitiObjectStreamContinueEntry,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorNotFound,\n GraffitiErrorSchemaMismatch,\n GraffitiErrorForbidden,\n unpackObjectUrl,\n maskGraffitiObject,\n isActorAllowedGraffitiObject,\n compileGraffitiObjectSchema,\n} from \"@graffiti-garden/api\";\nimport { randomBase64, decodeObjectUrl, encodeObjectUrl } from \"./utilities.js\";\nimport type Ajv from \"ajv\";\n\n/**\n * Constructor options for the GraffitiPoubchDB class.\n */\nexport interface GraffitiLocalOptions {\n /**\n * Options to pass to the PouchDB constructor.\n * Defaults to `{ name: \"graffitiDb\" }`.\n *\n * See the [PouchDB documentation](https://pouchdb.com/api.html#create_database)\n * for available options.\n */\n pouchDBOptions?: PouchDB.Configuration.DatabaseConfiguration;\n /**\n * Wait at least this long (in milliseconds) before continuing a stream.\n * A basic form of rate limiting. Defaults to 2 seconds.\n */\n continueBuffer?: number;\n}\n\ntype GraffitiObjectData = {\n tombstone: boolean;\n value: {};\n channels: string[];\n allowed?: string[] | null;\n lastModified: number;\n};\n\ntype ContinueDiscoverParams = {\n lastDiscovered: number;\n ifModifiedSince: number;\n};\n\n/**\n * An implementation of only the database operations of the\n * GraffitiAPI without synchronization or session management.\n */\nexport class GraffitiLocalObjects {\n protected db_: Promise<PouchDB.Database<GraffitiObjectData>> | undefined;\n protected ajv_: Promise<Ajv> | undefined;\n protected readonly options: GraffitiLocalOptions;\n\n get db() {\n if (!this.db_) {\n this.db_ = (async () => {\n const { default: PouchDB } = await import(\"pouchdb\");\n const pouchDbOptions = {\n name: \"graffitiDb\",\n ...this.options.pouchDBOptions,\n };\n const db = new PouchDB<GraffitiObjectData>(\n pouchDbOptions.name,\n pouchDbOptions,\n );\n await db\n //@ts-ignore\n .put({\n _id: \"_design/indexes\",\n views: {\n objectsPerChannelAndLastModified: {\n map: function (object: GraffitiObjectData) {\n const paddedLastModified = object.lastModified\n .toString()\n .padStart(15, \"0\");\n object.channels.forEach(function (channel) {\n const id =\n encodeURIComponent(channel) + \"/\" + paddedLastModified;\n //@ts-ignore\n emit(id);\n });\n }.toString(),\n },\n },\n })\n //@ts-ignore\n .catch((error) => {\n if (\n error &&\n typeof error === \"object\" &&\n \"name\" in error &&\n error.name === \"conflict\"\n ) {\n // Design document already exists\n return;\n } else {\n throw error;\n }\n });\n return db;\n })();\n }\n return this.db_;\n }\n\n protected get ajv() {\n if (!this.ajv_) {\n this.ajv_ = (async () => {\n const { default: Ajv } = await import(\"ajv\");\n return new Ajv({ strict: false });\n })();\n }\n return this.ajv_;\n }\n\n protected async getOperationClock() {\n return Number((await (await this.db).info()).update_seq);\n }\n\n constructor(options?: GraffitiLocalOptions) {\n this.options = options ?? {};\n }\n\n get: Graffiti[\"get\"] = async (...args) => {\n const [urlObject, schema, session] = args;\n const url = unpackObjectUrl(urlObject);\n\n let doc: GraffitiObjectData;\n try {\n doc = await (await this.db).get(url);\n } catch (error) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n const { actor } = decodeObjectUrl(url);\n const { value, channels, allowed } = doc;\n const object: GraffitiObjectBase = {\n value,\n channels,\n allowed,\n url,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n // Mask out the allowed list and channels\n // if the user is not the owner\n maskGraffitiObject(object, [], session);\n\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n if (!validate(object)) {\n throw new GraffitiErrorSchemaMismatch();\n }\n return object;\n };\n\n delete: Graffiti[\"delete\"] = async (...args) => {\n const [urlObject, session] = args;\n\n const url = unpackObjectUrl(urlObject);\n const { actor } = decodeObjectUrl(url);\n if (actor !== session.actor) {\n throw new GraffitiErrorForbidden(\n \"You cannot delete an object that you did not create.\",\n );\n }\n\n let doc: GraffitiObjectData & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta;\n try {\n doc = await (await this.db).get(url);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n // Set the tombstone and update lastModified\n doc.tombstone = true;\n doc.lastModified = await this.getOperationClock();\n try {\n await (await this.db).put(doc);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n return;\n };\n\n post: Graffiti[\"post\"] = async (...args) => {\n const [objectPartial, session] = args;\n\n const actor = session.actor;\n const id = randomBase64();\n const url = encodeObjectUrl(actor, id);\n\n const { value, channels, allowed } = objectPartial;\n const object: GraffitiObjectData = {\n value,\n channels,\n allowed,\n lastModified: await this.getOperationClock(),\n tombstone: false,\n };\n\n await (\n await this.db\n ).put({\n _id: url,\n ...object,\n });\n\n return {\n ...objectPartial,\n actor,\n url,\n };\n };\n\n protected async *discoverMeta<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams?: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): AsyncGenerator<\n GraffitiObjectStreamContinueEntry<Schema>,\n ContinueDiscoverParams\n > {\n // If we are continuing a discover, make sure to wait at\n // least 2 seconds since the last poll to start a new one.\n if (continueParams) {\n const continueBuffer = this.options.continueBuffer ?? 2000;\n const timeElapsedSinceLastDiscover =\n Date.now() - continueParams.lastDiscovered;\n if (timeElapsedSinceLastDiscover < continueBuffer) {\n // Continue was called too soon,\n // wait a bit before continuing\n await new Promise((resolve) =>\n setTimeout(resolve, continueBuffer - timeElapsedSinceLastDiscover),\n );\n }\n }\n\n const [discoverChannels, schema, session] = args;\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n const startKeySuffix = continueParams\n ? continueParams.ifModifiedSince.toString().padStart(15, \"0\")\n : \"\";\n const endKeySuffix = \"\\uffff\";\n\n const processedUrls = new Set<string>();\n\n const startTime = await this.getOperationClock();\n\n for (const channel of discoverChannels) {\n const keyPrefix = encodeURIComponent(channel) + \"/\";\n const startkey = keyPrefix + startKeySuffix;\n const endkey = keyPrefix + endKeySuffix;\n\n const result = await (\n await this.db\n ).query<GraffitiObjectData>(\"indexes/objectsPerChannelAndLastModified\", {\n startkey,\n endkey,\n include_docs: true,\n });\n\n for (const row of result.rows) {\n const doc = row.doc;\n if (!doc) continue;\n\n const url = doc._id;\n\n if (processedUrls.has(url)) continue;\n processedUrls.add(url);\n\n // If this is not a continuation, skip tombstones\n if (!continueParams && doc.tombstone) continue;\n\n const { tombstone, value, channels, allowed } = doc;\n const { actor } = decodeObjectUrl(url);\n\n const object: GraffitiObjectBase = {\n url,\n value,\n allowed,\n channels,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) continue;\n\n maskGraffitiObject(object, discoverChannels, session);\n\n if (!validate(object)) continue;\n\n yield tombstone\n ? {\n tombstone: true,\n object: { url },\n }\n : { object };\n }\n }\n\n return {\n lastDiscovered: Date.now(),\n ifModifiedSince: startTime,\n };\n }\n\n protected discoverCursor(\n args: Parameters<typeof Graffiti.prototype.discover<{}>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): string {\n const [channels, schema, session] = args;\n return (\n \"discover:\" +\n JSON.stringify({\n channels,\n schema,\n continueParams,\n actor: session?.actor,\n })\n );\n }\n\n protected async *discoverContinue<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<Schema> {\n if (session?.actor !== args[2]?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n const iterator = this.discoverMeta<Schema>(args, continueParams);\n\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this.discoverContinue<Schema>(args, result.value, session),\n cursor: this.discoverCursor(args, result.value),\n };\n }\n yield result.value;\n }\n }\n\n discover: Graffiti[\"discover\"] = (...args) => {\n const [channels, schema, session] = args;\n const iterator = this.discoverMeta<(typeof args)[1]>([\n channels,\n schema,\n session,\n ]);\n\n const this_ = this;\n return (async function* () {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this_.discoverContinue<(typeof args)[1]>(\n args,\n result.value,\n session,\n ),\n cursor: this_.discoverCursor(args, result.value),\n };\n }\n // Make sure to filter out tombstones\n if (result.value.tombstone) continue;\n yield result.value;\n }\n })();\n };\n\n continueDiscover: Graffiti[\"continueDiscover\"] = (...args) => {\n const [cursor, session] = args;\n if (cursor.startsWith(\"discover:\")) {\n // TODO: use AJV here\n const { channels, schema, actor, continueParams } = JSON.parse(\n cursor.slice(\"discover:\".length),\n );\n if (actor && actor !== session?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n return this.discoverContinue<{}>(\n [channels, schema, session],\n continueParams,\n );\n } else {\n throw new GraffitiErrorNotFound(\"Cursor not found\");\n }\n };\n}\n", "import {\n GraffitiErrorNotAcceptable,\n GraffitiErrorTooLarge,\n type Graffiti,\n type JSONSchema,\n} from \"@graffiti-garden/api\";\nimport {\n decodeObjectUrl,\n encodeObjectUrl,\n decodeMediaUrl,\n encodeMediaUrl,\n blobToBase64,\n base64ToBlob,\n} from \"./utilities\";\nimport Negotiator from \"negotiator\";\n\nconst MEDIA_OBJECT_SCHEMA = {\n properties: {\n value: {\n properties: {\n dataBase64: { type: \"string\" },\n type: { type: \"string\" },\n size: { type: \"number\" },\n },\n required: [\"dataBase64\", \"type\", \"size\"],\n },\n },\n} as const satisfies JSONSchema;\n\nexport class GraffitiLocalMedia {\n protected db: Pick<Graffiti, \"post\" | \"get\" | \"delete\">;\n\n constructor(db: Pick<Graffiti, \"post\" | \"get\" | \"delete\">) {\n this.db = db;\n }\n\n postMedia: Graffiti[\"postMedia\"] = async (...args) => {\n const [media, session] = args;\n\n const dataBase64 = await blobToBase64(media.data);\n const type = media.data.type;\n\n const { url } = await this.db.post<typeof MEDIA_OBJECT_SCHEMA>(\n {\n value: {\n dataBase64,\n type,\n size: media.data.size,\n },\n channels: [],\n allowed: media.allowed,\n },\n session,\n );\n\n const { actor, id } = decodeObjectUrl(url);\n return encodeMediaUrl(actor, id);\n };\n\n getMedia: Graffiti[\"getMedia\"] = async (...args) => {\n const [mediaUrl, requirements, session] = args;\n const { actor, id } = decodeMediaUrl(mediaUrl);\n const objectUrl = encodeObjectUrl(actor, id);\n\n const object = await this.db.get<typeof MEDIA_OBJECT_SCHEMA>(\n objectUrl,\n MEDIA_OBJECT_SCHEMA,\n session,\n );\n\n const { dataBase64, type, size } = object.value;\n\n if (requirements?.maxBytes && size > requirements.maxBytes) {\n throw new GraffitiErrorTooLarge(\"File size exceeds limit\");\n }\n\n // Make sure it adheres to requirements.accept\n if (requirements?.accept) {\n const negotiator = new Negotiator({\n headers: { accept: requirements.accept },\n });\n if (negotiator.mediaType([type]) !== type) {\n throw new GraffitiErrorNotAcceptable(`Unsupported media type, ${type}`);\n }\n }\n\n const data = await base64ToBlob(dataBase64);\n if (data.size !== size || data.type !== type) {\n throw new Error(\"Invalid data\");\n }\n\n return {\n data,\n actor: object.actor,\n allowed: object.allowed,\n };\n };\n\n deleteMedia: Graffiti[\"deleteMedia\"] = async (...args) => {\n const [mediaUrl, session] = args;\n const { actor, id } = decodeMediaUrl(mediaUrl);\n const objectUrl = encodeObjectUrl(actor, id);\n\n await this.db.delete(objectUrl, session);\n };\n}\n"],
5
+ "mappings": "4EAAA,IAAAA,GAAAC,EAAA,CAAAC,GAAAC,IAAA,cAAAC,IAAAC,IAAAC,IAeAH,EAAO,QAAUI,GACjBJ,EAAO,QAAQ,kBAAoBI,GAOnC,IAAIC,GAAsB,8BAO1B,SAASC,GAAmBC,EAAQ,CAGlC,QAFIC,EAAUD,EAAO,MAAM,GAAG,EAErBE,EAAI,EAAGC,EAAI,EAAGD,EAAID,EAAQ,OAAQC,IAAK,CAC9C,IAAIE,EAAUC,GAAaJ,EAAQC,CAAC,EAAE,KAAK,EAAGA,CAAC,EAE3CE,IACFH,EAAQE,GAAG,EAAIC,EAEnB,CAGA,OAAAH,EAAQ,OAASE,EAEVF,CACT,CAOA,SAASI,GAAaC,EAAKJ,EAAG,CAC5B,IAAIK,EAAQT,GAAoB,KAAKQ,CAAG,EACxC,GAAI,CAACC,EAAO,OAAO,KAEnB,IAAIH,EAAUG,EAAM,CAAC,EACjBC,EAAI,EACR,GAAID,EAAM,CAAC,EAET,QADIE,EAASF,EAAM,CAAC,EAAE,MAAM,GAAG,EACtBJ,EAAI,EAAGA,EAAIM,EAAO,OAAQN,IAAK,CACtC,IAAIO,EAAID,EAAON,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,EAClC,GAAIO,EAAE,CAAC,IAAM,IAAK,CAChBF,EAAI,WAAWE,EAAE,CAAC,CAAC,EACnB,KACF,CACF,CAGF,MAAO,CACL,QAASN,EACT,EAAGI,EACH,EAAGN,CACL,CACF,CAOA,SAASS,GAAmBP,EAASQ,EAAUC,EAAO,CAGpD,QAFIC,EAAW,CAAC,EAAG,GAAI,EAAG,EAAG,EAAG,CAAC,EAExBZ,EAAI,EAAGA,EAAIU,EAAS,OAAQV,IAAK,CACxC,IAAIa,EAAOC,GAAQZ,EAASQ,EAASV,CAAC,EAAGW,CAAK,EAE1CE,IAASD,EAAS,EAAIC,EAAK,GAAKD,EAAS,EAAIC,EAAK,GAAKD,EAAS,EAAIC,EAAK,GAAK,IAChFD,EAAWC,EAEf,CAEA,OAAOD,CACT,CAOA,SAASE,GAAQZ,EAASW,EAAMF,EAAO,CACrC,IAAII,EAAI,EACR,GAAGF,EAAK,QAAQ,YAAY,IAAMX,EAAQ,YAAY,EACpDa,GAAK,UACIF,EAAK,UAAY,IAC1B,OAAO,KAGT,MAAO,CACL,EAAGF,EACH,EAAGE,EAAK,EACR,EAAGA,EAAK,EACR,EAAGE,CACL,CACF,CAOA,SAASpB,GAAkBG,EAAQkB,EAAU,CAE3C,IAAIjB,EAAUF,GAAmBC,IAAW,OAAY,IAAMA,GAAU,EAAE,EAE1E,GAAI,CAACkB,EAEH,OAAOjB,EACJ,OAAOkB,EAAS,EAChB,KAAKC,EAAY,EACjB,IAAIC,EAAc,EAGvB,IAAIC,EAAaJ,EAAS,IAAI,SAAqBK,EAAMV,EAAO,CAC9D,OAAOF,GAAmBY,EAAMtB,EAASY,CAAK,CAChD,CAAC,EAGD,OAAOS,EAAW,OAAOH,EAAS,EAAE,KAAKC,EAAY,EAAE,IAAI,SAAoBN,EAAU,CACvF,OAAOI,EAASI,EAAW,QAAQR,CAAQ,CAAC,CAC9C,CAAC,CACH,CAOA,SAASM,GAAaI,EAAGC,EAAG,CAC1B,OAAQA,EAAE,EAAID,EAAE,GAAOC,EAAE,EAAID,EAAE,GAAOA,EAAE,EAAIC,EAAE,GAAOD,EAAE,EAAIC,EAAE,GAAM,CACrE,CAOA,SAASJ,GAAeN,EAAM,CAC5B,OAAOA,EAAK,OACd,CAOA,SAASI,GAAUJ,EAAM,CACvB,OAAOA,EAAK,EAAI,CAClB,ICxKA,IAAAW,GAAAC,EAAA,CAAAC,GAAAC,IAAA,cAAAC,IAAAC,IAAAC,IAeAH,EAAO,QAAUI,GACjBJ,EAAO,QAAQ,mBAAqBI,GAOpC,IAAIC,GAAuB,8BAO3B,SAASC,GAAoBC,EAAQ,CAKnC,QAJIC,EAAUD,EAAO,MAAM,GAAG,EAC1BE,EAAc,GACdC,EAAa,EAERC,EAAI,EAAGC,EAAI,EAAGD,EAAIH,EAAQ,OAAQG,IAAK,CAC9C,IAAIE,EAAWC,GAAcN,EAAQG,CAAC,EAAE,KAAK,EAAGA,CAAC,EAE7CE,IACFL,EAAQI,GAAG,EAAIC,EACfJ,EAAcA,GAAeM,GAAQ,WAAYF,CAAQ,EACzDH,EAAa,KAAK,IAAIA,EAAYG,EAAS,GAAK,CAAC,EAErD,CAEA,OAAKJ,IAKHD,EAAQI,GAAG,EAAI,CACb,SAAU,WACV,EAAGF,EACH,EAAGC,CACL,GAIFH,EAAQ,OAASI,EAEVJ,CACT,CAOA,SAASM,GAAcE,EAAKL,EAAG,CAC7B,IAAIM,EAAQZ,GAAqB,KAAKW,CAAG,EACzC,GAAI,CAACC,EAAO,OAAO,KAEnB,IAAIJ,EAAWI,EAAM,CAAC,EAClBC,EAAI,EACR,GAAID,EAAM,CAAC,EAET,QADIE,EAASF,EAAM,CAAC,EAAE,MAAM,GAAG,EACtBL,EAAI,EAAGA,EAAIO,EAAO,OAAQP,IAAK,CACtC,IAAIQ,EAAID,EAAOP,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,EAClC,GAAIQ,EAAE,CAAC,IAAM,IAAK,CAChBF,EAAI,WAAWE,EAAE,CAAC,CAAC,EACnB,KACF,CACF,CAGF,MAAO,CACL,SAAUP,EACV,EAAGK,EACH,EAAGP,CACL,CACF,CAOA,SAASU,GAAoBR,EAAUS,EAAUC,EAAO,CAGtD,QAFIC,EAAW,CAAC,SAAUX,EAAU,EAAG,GAAI,EAAG,EAAG,EAAG,CAAC,EAE5CF,EAAI,EAAGA,EAAIW,EAAS,OAAQX,IAAK,CACxC,IAAIc,EAAOV,GAAQF,EAAUS,EAASX,CAAC,EAAGY,CAAK,EAE3CE,IAASD,EAAS,EAAIC,EAAK,GAAKD,EAAS,EAAIC,EAAK,GAAKD,EAAS,EAAIC,EAAK,GAAK,IAChFD,EAAWC,EAEf,CAEA,OAAOD,CACT,CAOA,SAAST,GAAQF,EAAUY,EAAMF,EAAO,CACtC,IAAIG,EAAI,EACR,GAAGD,EAAK,SAAS,YAAY,IAAMZ,EAAS,YAAY,EACtDa,GAAK,UACID,EAAK,WAAa,IAC3B,OAAO,KAGT,MAAO,CACL,SAAUZ,EACV,EAAGU,EACH,EAAGE,EAAK,EACR,EAAGA,EAAK,EACR,EAAGC,CACL,CACF,CAOA,SAAStB,GAAmBG,EAAQoB,EAAUC,EAAW,CACvD,IAAIpB,EAAUF,GAAoBC,GAAU,EAAE,EAE1CsB,EAAaD,EAAY,SAAqBE,EAAGC,EAAG,CACtD,GAAID,EAAE,IAAMC,EAAE,EACZ,OAAOA,EAAE,EAAID,EAAE,EAGjB,IAAIE,EAAaJ,EAAU,QAAQE,EAAE,QAAQ,EACzCG,EAAaL,EAAU,QAAQG,EAAE,QAAQ,EAE7C,OAAIC,IAAe,IAAMC,IAAe,GAE9BF,EAAE,EAAID,EAAE,GAAOA,EAAE,EAAIC,EAAE,GAAOD,EAAE,EAAIC,EAAE,EAG5CC,IAAe,IAAMC,IAAe,GAC/BD,EAAaC,EAGfD,IAAe,GAAK,EAAI,EACjC,EAAIE,GAEJ,GAAI,CAACP,EAEH,OAAOnB,EACJ,OAAO2B,EAAS,EAChB,KAAKN,CAAU,EACf,IAAIO,EAAe,EAGxB,IAAIC,EAAaV,EAAS,IAAI,SAAqBW,EAAMf,EAAO,CAC9D,OAAOF,GAAoBiB,EAAM9B,EAASe,CAAK,CACjD,CAAC,EAGD,OAAOc,EAAW,OAAOF,EAAS,EAAE,KAAKN,CAAU,EAAE,IAAI,SAAqBL,EAAU,CACtF,OAAOG,EAASU,EAAW,QAAQb,CAAQ,CAAC,CAC9C,CAAC,CACH,CAOA,SAASU,GAAaJ,EAAGC,EAAG,CAC1B,OAAQA,EAAE,EAAID,EAAE,GAAOC,EAAE,EAAID,EAAE,GAAOA,EAAE,EAAIC,EAAE,GAAOD,EAAE,EAAIC,EAAE,CAC/D,CAOA,SAASK,GAAgBX,EAAM,CAC7B,OAAOA,EAAK,QACd,CAOA,SAASU,GAAUV,EAAM,CACvB,OAAOA,EAAK,EAAI,CAClB,IC5MA,IAAAc,GAAAC,EAAA,CAAAC,GAAAC,IAAA,cAAAC,IAAAC,IAAAC,IAeAH,EAAO,QAAUI,GACjBJ,EAAO,QAAQ,mBAAqBI,GAOpC,IAAIC,GAAuB,+CAO3B,SAASC,GAAoBC,EAAQ,CAGnC,QAFIC,EAAUD,EAAO,MAAM,GAAG,EAErBE,EAAI,EAAGC,EAAI,EAAGD,EAAID,EAAQ,OAAQC,IAAK,CAC9C,IAAIE,EAAWC,GAAcJ,EAAQC,CAAC,EAAE,KAAK,EAAGA,CAAC,EAE7CE,IACFH,EAAQE,GAAG,EAAIC,EAEnB,CAGA,OAAAH,EAAQ,OAASE,EAEVF,CACT,CAOA,SAASI,GAAcC,EAAKJ,EAAG,CAC7B,IAAIK,EAAQT,GAAqB,KAAKQ,CAAG,EACzC,GAAI,CAACC,EAAO,OAAO,KAEnB,IAAIC,EAASD,EAAM,CAAC,EAChBE,EAASF,EAAM,CAAC,EAChBG,EAAOF,EAEPC,IAAQC,GAAQ,IAAMD,GAE1B,IAAIE,EAAI,EACR,GAAIJ,EAAM,CAAC,EAET,QADIK,EAASL,EAAM,CAAC,EAAE,MAAM,GAAG,EACtBJ,EAAI,EAAGA,EAAIS,EAAO,OAAQT,IAAK,CACtC,IAAI,EAAIS,EAAOT,CAAC,EAAE,MAAM,GAAG,EACvB,EAAE,CAAC,IAAM,MAAKQ,EAAI,WAAW,EAAE,CAAC,CAAC,EACvC,CAGF,MAAO,CACL,OAAQH,EACR,OAAQC,EACR,EAAGE,EACH,EAAGT,EACH,KAAMQ,CACR,CACF,CAOA,SAASG,GAAoBT,EAAUU,EAAUC,EAAO,CAGtD,QAFIC,EAAW,CAAC,EAAG,GAAI,EAAG,EAAG,EAAG,CAAC,EAExBd,EAAI,EAAGA,EAAIY,EAAS,OAAQZ,IAAK,CACxC,IAAIe,EAAOC,GAAQd,EAAUU,EAASZ,CAAC,EAAGa,CAAK,EAE3CE,IAASD,EAAS,EAAIC,EAAK,GAAKD,EAAS,EAAIC,EAAK,GAAKD,EAAS,EAAIC,EAAK,GAAK,IAChFD,EAAWC,EAEf,CAEA,OAAOD,CACT,CAOA,SAASE,GAAQd,EAAUa,EAAMF,EAAO,CACtC,IAAII,EAAId,GAAcD,CAAQ,EAC9B,GAAI,CAACe,EAAG,OAAO,KACf,IAAIC,EAAI,EACR,GAAGH,EAAK,KAAK,YAAY,IAAME,EAAE,KAAK,YAAY,EAChDC,GAAK,UACIH,EAAK,OAAO,YAAY,IAAME,EAAE,KAAK,YAAY,EAC1DC,GAAK,UACIH,EAAK,KAAK,YAAY,IAAME,EAAE,OAAO,YAAY,EAC1DC,GAAK,UACIH,EAAK,OAAS,IACvB,OAAO,KAGT,MAAO,CACL,EAAGF,EACH,EAAGE,EAAK,EACR,EAAGA,EAAK,EACR,EAAGG,CACL,CACF,CAOA,SAASvB,GAAmBG,EAAQqB,EAAU,CAE5C,IAAIpB,EAAUF,GAAoBC,IAAW,OAAY,IAAMA,GAAU,EAAE,EAE3E,GAAI,CAACqB,EAEH,OAAOpB,EACJ,OAAOqB,EAAS,EAChB,KAAKC,EAAY,EACjB,IAAIC,EAAe,EAGxB,IAAIC,EAAaJ,EAAS,IAAI,SAAqBK,EAAMX,EAAO,CAC9D,OAAOF,GAAoBa,EAAMzB,EAASc,CAAK,CACjD,CAAC,EAGD,OAAOU,EAAW,OAAOH,EAAS,EAAE,KAAKC,EAAY,EAAE,IAAI,SAAqBP,EAAU,CACxF,OAAOK,EAASI,EAAW,QAAQT,CAAQ,CAAC,CAC9C,CAAC,CACH,CAOA,SAASO,GAAaI,EAAGC,EAAG,CAC1B,OAAQA,EAAE,EAAID,EAAE,GAAOC,EAAE,EAAID,EAAE,GAAOA,EAAE,EAAIC,EAAE,GAAOD,EAAE,EAAIC,EAAE,GAAM,CACrE,CAOA,SAASJ,GAAgBP,EAAM,CAC7B,OAAOA,EAAK,IACd,CAOA,SAASK,GAAUL,EAAM,CACvB,OAAOA,EAAK,EAAI,CAClB,IClLA,IAAAY,GAAAC,EAAA,CAAAC,GAAAC,IAAA,cAAAC,IAAAC,IAAAC,IAeAH,EAAO,QAAUI,GACjBJ,EAAO,QAAQ,oBAAsBI,GAOrC,IAAIC,GAAwB,2CAO5B,SAASC,GAAYC,EAAQ,CAG3B,QAFIC,EAAUC,GAAgBF,CAAM,EAE3BG,EAAI,EAAGC,EAAI,EAAGD,EAAIF,EAAQ,OAAQE,IAAK,CAC9C,IAAIE,EAAYC,GAAeL,EAAQE,CAAC,EAAE,KAAK,EAAGA,CAAC,EAE/CE,IACFJ,EAAQG,GAAG,EAAIC,EAEnB,CAGA,OAAAJ,EAAQ,OAASG,EAEVH,CACT,CAOA,SAASK,GAAeC,EAAKJ,EAAG,CAC9B,IAAIK,EAAQV,GAAsB,KAAKS,CAAG,EAC1C,GAAI,CAACC,EAAO,OAAO,KAEnB,IAAIC,EAAS,OAAO,OAAO,IAAI,EAC3BC,EAAI,EACJC,EAAUH,EAAM,CAAC,EACjBI,EAAOJ,EAAM,CAAC,EAElB,GAAIA,EAAM,CAAC,EAGT,QAFIK,EAAOC,GAAgBN,EAAM,CAAC,CAAC,EAAE,IAAIO,EAAiB,EAEjDX,EAAI,EAAGA,EAAIS,EAAK,OAAQT,IAAK,CACpC,IAAIY,EAAOH,EAAKT,CAAC,EACba,EAAMD,EAAK,CAAC,EAAE,YAAY,EAC1BE,EAAMF,EAAK,CAAC,EAGZG,EAAQD,GAAOA,EAAI,CAAC,IAAM,KAAOA,EAAIA,EAAI,OAAS,CAAC,IAAM,IACzDA,EAAI,MAAM,EAAG,EAAE,EACfA,EAEJ,GAAID,IAAQ,IAAK,CACfP,EAAI,WAAWS,CAAK,EACpB,KACF,CAGAV,EAAOQ,CAAG,EAAIE,CAChB,CAGF,MAAO,CACL,KAAMP,EACN,QAASD,EACT,OAAQF,EACR,EAAGC,EACH,EAAGP,CACL,CACF,CAOA,SAASiB,GAAqBR,EAAMS,EAAUC,EAAO,CAGnD,QAFIC,EAAW,CAAC,EAAG,GAAI,EAAG,EAAG,EAAG,CAAC,EAExBpB,EAAI,EAAGA,EAAIkB,EAAS,OAAQlB,IAAK,CACxC,IAAIqB,EAAOC,GAAQb,EAAMS,EAASlB,CAAC,EAAGmB,CAAK,EAEvCE,IAASD,EAAS,EAAIC,EAAK,GAAKD,EAAS,EAAIC,EAAK,GAAKD,EAAS,EAAIC,EAAK,GAAK,IAChFD,EAAWC,EAEf,CAEA,OAAOD,CACT,CAOA,SAASE,GAAQb,EAAMY,EAAMF,EAAO,CAClC,IAAII,EAAIpB,GAAeM,CAAI,EACvBe,EAAI,EAER,GAAI,CAACD,EACH,OAAO,KAGT,GAAGF,EAAK,KAAK,YAAY,GAAKE,EAAE,KAAK,YAAY,EAC/CC,GAAK,UACGH,EAAK,MAAQ,IACrB,OAAO,KAGT,GAAGA,EAAK,QAAQ,YAAY,GAAKE,EAAE,QAAQ,YAAY,EACrDC,GAAK,UACGH,EAAK,SAAW,IACxB,OAAO,KAGT,IAAII,EAAO,OAAO,KAAKJ,EAAK,MAAM,EAClC,GAAII,EAAK,OAAS,EAChB,GAAIA,EAAK,MAAM,SAAUC,EAAG,CAC1B,OAAOL,EAAK,OAAOK,CAAC,GAAK,MAAQL,EAAK,OAAOK,CAAC,GAAK,IAAI,YAAY,IAAMH,EAAE,OAAOG,CAAC,GAAK,IAAI,YAAY,CAC1G,CAAC,EACCF,GAAK,MAEL,QAAO,KAIX,MAAO,CACL,EAAGL,EACH,EAAGE,EAAK,EACR,EAAGA,EAAK,EACR,EAAGG,CACL,CACF,CAOA,SAAS9B,GAAoBG,EAAQ8B,EAAU,CAE7C,IAAI7B,EAAUF,GAAYC,IAAW,OAAY,MAAQA,GAAU,EAAE,EAErE,GAAI,CAAC8B,EAEH,OAAO7B,EACJ,OAAO8B,EAAS,EAChB,KAAKC,EAAY,EACjB,IAAIC,EAAW,EAGpB,IAAIC,EAAaJ,EAAS,IAAI,SAAqBlB,EAAMU,EAAO,CAC9D,OAAOF,GAAqBR,EAAMX,EAASqB,CAAK,CAClD,CAAC,EAGD,OAAOY,EAAW,OAAOH,EAAS,EAAE,KAAKC,EAAY,EAAE,IAAI,SAAiBT,EAAU,CACpF,OAAOO,EAASI,EAAW,QAAQX,CAAQ,CAAC,CAC9C,CAAC,CACH,CAOA,SAASS,GAAaG,EAAGC,EAAG,CAC1B,OAAQA,EAAE,EAAID,EAAE,GAAOC,EAAE,EAAID,EAAE,GAAOA,EAAE,EAAIC,EAAE,GAAOD,EAAE,EAAIC,EAAE,GAAM,CACrE,CAOA,SAASH,GAAYT,EAAM,CACzB,OAAOA,EAAK,KAAO,IAAMA,EAAK,OAChC,CAOA,SAASO,GAAUP,EAAM,CACvB,OAAOA,EAAK,EAAI,CAClB,CAOA,SAASa,GAAWC,EAAQ,CAI1B,QAHIC,EAAQ,EACRjB,EAAQ,GAEJA,EAAQgB,EAAO,QAAQ,IAAKhB,CAAK,KAAO,IAC9CiB,IACAjB,IAGF,OAAOiB,CACT,CAOA,SAASxB,GAAkBR,EAAK,CAC9B,IAAIe,EAAQf,EAAI,QAAQ,GAAG,EACvBU,EACAC,EAEJ,OAAII,IAAU,GACZL,EAAMV,GAENU,EAAMV,EAAI,MAAM,EAAGe,CAAK,EACxBJ,EAAMX,EAAI,MAAMe,EAAQ,CAAC,GAGpB,CAACL,EAAKC,CAAG,CAClB,CAOA,SAAShB,GAAgBF,EAAQ,CAG/B,QAFIC,EAAUD,EAAO,MAAM,GAAG,EAErBG,EAAI,EAAGC,EAAI,EAAGD,EAAIF,EAAQ,OAAQE,IACrCkC,GAAWpC,EAAQG,CAAC,CAAC,EAAI,GAAK,EAChCH,EAAQ,EAAEG,CAAC,EAAIH,EAAQE,CAAC,EAExBF,EAAQG,CAAC,GAAK,IAAMH,EAAQE,CAAC,EAKjC,OAAAF,EAAQ,OAASG,EAAI,EAEdH,CACT,CAOA,SAASa,GAAgBP,EAAK,CAG5B,QAFIiC,EAAajC,EAAI,MAAM,GAAG,EAErBJ,EAAI,EAAGC,EAAI,EAAGD,EAAIqC,EAAW,OAAQrC,IACxCkC,GAAWG,EAAWpC,CAAC,CAAC,EAAI,GAAK,EACnCoC,EAAW,EAAEpC,CAAC,EAAIoC,EAAWrC,CAAC,EAE9BqC,EAAWpC,CAAC,GAAK,IAAMoC,EAAWrC,CAAC,EAKvCqC,EAAW,OAASpC,EAAI,EAExB,QAASD,EAAI,EAAGA,EAAIqC,EAAW,OAAQrC,IACrCqC,EAAWrC,CAAC,EAAIqC,EAAWrC,CAAC,EAAE,KAAK,EAGrC,OAAOqC,CACT,ICrSA,IAAAC,GAAAC,EAAA,CAAAC,GAAAC,IAAA,cAAAC,IAAAC,IAAAC,IAUA,IAAIC,GAAoB,KACpBC,GAAqB,KACrBC,GAAqB,KACrBC,GAAsB,KAO1BP,EAAO,QAAUQ,EACjBR,EAAO,QAAQ,WAAaQ,EAQ5B,SAASA,EAAWC,EAAS,CAC3B,GAAI,EAAE,gBAAgBD,GACpB,OAAO,IAAIA,EAAWC,CAAO,EAG/B,KAAK,QAAUA,CACjB,CAEAD,EAAW,UAAU,QAAU,SAAiBE,EAAW,CACzD,IAAIC,EAAM,KAAK,SAASD,CAAS,EACjC,OAAOC,GAAOA,EAAI,CAAC,CACrB,EAEAH,EAAW,UAAU,SAAW,SAAkBE,EAAW,CAC3D,OAAON,GAAkB,KAAK,QAAQ,QAAQ,gBAAgB,EAAGM,CAAS,CAC5E,EAEAF,EAAW,UAAU,SAAW,SAAkBE,EAAWE,EAAM,CACjE,IAAID,EAAM,KAAK,UAAUD,EAAWE,CAAI,EACxC,OAAOD,GAAOA,EAAI,CAAC,CACrB,EAEAH,EAAW,UAAU,UAAY,SAAmBE,EAAWG,EAAS,CACtE,IAAID,EAAOC,GAAW,CAAC,EACvB,OAAOR,GAAmB,KAAK,QAAQ,QAAQ,iBAAiB,EAAGK,EAAWE,EAAK,SAAS,CAC9F,EAEAJ,EAAW,UAAU,SAAW,SAAkBE,EAAW,CAC3D,IAAIC,EAAM,KAAK,UAAUD,CAAS,EAClC,OAAOC,GAAOA,EAAI,CAAC,CACrB,EAEAH,EAAW,UAAU,UAAY,SAAmBE,EAAW,CAC7D,OAAOJ,GAAmB,KAAK,QAAQ,QAAQ,iBAAiB,EAAGI,CAAS,CAC9E,EAEAF,EAAW,UAAU,UAAY,SAAmBE,EAAW,CAC7D,IAAIC,EAAM,KAAK,WAAWD,CAAS,EACnC,OAAOC,GAAOA,EAAI,CAAC,CACrB,EAEAH,EAAW,UAAU,WAAa,SAAoBE,EAAW,CAC/D,OAAOH,GAAoB,KAAK,QAAQ,QAAQ,OAAQG,CAAS,CACnE,EAGAF,EAAW,UAAU,iBAAmBA,EAAW,UAAU,QAC7DA,EAAW,UAAU,kBAAoBA,EAAW,UAAU,SAC9DA,EAAW,UAAU,kBAAoBA,EAAW,UAAU,SAC9DA,EAAW,UAAU,mBAAqBA,EAAW,UAAU,UAC/DA,EAAW,UAAU,kBAAoBA,EAAW,UAAU,SAC9DA,EAAW,UAAU,mBAAqBA,EAAW,UAAU,UAC/DA,EAAW,UAAU,mBAAqBA,EAAW,UAAU,UAC/DA,EAAW,UAAU,oBAAsBA,EAAW,UAAU,aClFhEM,IAAAC,IAAAC,IC4GOC,IAAAC,IAAAC,ICGA,IAAMC,GAA2B,CACtC,KAAM,SACN,WAAY,CACV,MAAO,CAAE,KAAM,QAAS,EACxB,SAAU,CAAE,KAAM,QAAS,MAAO,CAAE,KAAM,QAAS,CAAE,EACrD,QAAS,CAAE,KAAM,QAAS,MAAO,CAAE,KAAM,QAAS,EAAG,SAAU,EAAK,EACpE,IAAK,CAAE,KAAM,QAAS,EACtB,MAAO,CAAE,KAAM,QAAS,CAC1B,EACA,qBAAsB,GACtB,SAAU,CAAC,QAAS,WAAY,QAAS,KAAK,CAChD,EAgCaC,GAA+B,CAC1C,GAAGD,GACH,SAAU,CAAC,QAAS,UAAU,CAChC,EC7JaE,EAAN,MAAMC,UAA+B,KAAM,CAChD,YAAYC,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,yBACZ,OAAO,eAAe,KAAMD,EAAuB,SAAS,CAC9D,CACF,EAEaE,EAAN,MAAMC,UAA8B,KAAM,CAC/C,YAAYF,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,wBACZ,OAAO,eAAe,KAAME,EAAsB,SAAS,CAC7D,CACF,EAEaC,GAAN,MAAMC,UAAmC,KAAM,CACpD,YAAYJ,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,6BACZ,OAAO,eAAe,KAAMI,EAA2B,SAAS,CAClE,CACF,EAEaC,EAAN,MAAMC,UAAoC,KAAM,CACrD,YAAYN,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,8BACZ,OAAO,eAAe,KAAMM,EAA4B,SAAS,CACnE,CACF,EAEaC,EAAN,MAAMC,UAA8B,KAAM,CAC/C,YAAYR,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,wBACZ,OAAO,eAAe,KAAMQ,EAAsB,SAAS,CAC7D,CACF,EAEaC,EAAN,MAAMC,UAAmC,KAAM,CACpD,YAAYV,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,6BACZ,OAAO,eAAe,KAAMU,EAA2B,SAAS,CAClE,CACF,ECpCO,SAASC,EAAgBC,EAAiC,CAC/D,OAAO,OAAOA,GAAQ,SAAWA,EAAMA,EAAI,GAC7C,CAEO,SAASC,EACdC,EACAC,EACA,CACA,GAAI,CAKF,OAAOD,EAAI,QAAQC,CAAM,CAG3B,OAASC,EAAO,CACd,MAAM,IAAIb,GACRa,aAAiB,MAAQA,EAAM,QAAU,MAC3C,CACF,CACF,CAEO,SAASC,EACdC,EACAC,EACA,CACA,MAEE,CAAC,MAAM,QAAQD,EAAO,OAAO,GAE5B,OAAOC,GAAS,OAAU,WAExBD,EAAO,QAAUC,EAAQ,OAExBD,EAAO,QAAQ,SAASC,EAAQ,KAAK,EAE7C,CAEO,SAASC,EACdF,EACAG,EACAF,EACM,CAEFD,EAAO,QAAUC,GAAS,QAG5BD,EAAO,QAAUA,EAAO,SAAWC,EAAU,CAACA,EAAQ,KAAK,EAAI,OAE/DD,EAAO,SAAWA,EAAO,SAAS,OAAQI,GACxCD,EAAS,SAASC,CAAO,CAC3B,EAEJ,CChEAC,IAAAC,IAAAC,ICAAC,IAAAC,IAAAC,IAAO,SAASC,EAAaC,EAA2B,CAItD,OAFe,KAAK,OAAO,cAAc,GAAGA,CAAK,CAAC,EAEpC,QAAQ,MAAO,GAAG,EAAE,QAAQ,MAAO,GAAG,EAAE,QAAQ,OAAQ,EAAE,CAC1E,CAEO,SAASC,EAAaC,EAA+B,CAE1D,IAAIC,EAASD,EAAU,QAAQ,KAAM,GAAG,EAAE,QAAQ,KAAM,GAAG,EAE3D,KAAOC,EAAO,OAAS,IAAM,GAAGA,GAAU,IAE1C,OAAO,WAAW,KAAK,KAAKA,CAAM,EAAIC,GAAMA,EAAE,WAAW,CAAC,CAAC,CAC7D,CAEO,SAASC,GAAaC,EAAmB,GAAY,CAE1D,IAAMN,EAAQ,IAAI,WAAWM,CAAQ,EACrC,cAAO,gBAAgBN,CAAK,EACrBD,EAAaC,CAAK,CAC3B,CAEA,IAAMO,GAAoB,mBACpBC,GAAmB,kBAElB,SAASC,GAAkBC,EAAeC,EAAYC,EAAgB,CAC3E,MAAO,GAAGA,CAAM,GAAG,mBAAmBF,CAAK,CAAC,IAAI,mBAAmBC,CAAE,CAAC,EACxE,CACO,SAASE,EAAgBH,EAAeC,EAAY,CACzD,OAAOF,GAAkBC,EAAOC,EAAIJ,EAAiB,CACvD,CACO,SAASO,GAAeJ,EAAeC,EAAY,CACxD,OAAOF,GAAkBC,EAAOC,EAAIH,EAAgB,CACtD,CAEO,SAASO,GAAkBC,EAAaJ,EAAgB,CAC7D,GAAI,CAACI,EAAI,WAAWJ,CAAM,EACxB,MAAM,IAAI,MAAM,2BAA2BA,CAAM,EAAE,EAErD,IAAMK,EAASD,EAAI,MAAMJ,EAAO,MAAM,EAAE,MAAM,GAAG,EACjD,GAAIK,EAAO,SAAW,EACpB,MAAM,IAAI,MAAM,wCAAwC,EAE1D,GAAM,CAACP,EAAOC,CAAE,EAAIM,EAAO,IAAI,kBAAkB,EACjD,MAAO,CAAE,MAAAP,EAAO,GAAAC,CAAG,CACrB,CACO,SAASO,EAAgBF,EAAa,CAC3C,OAAOD,GAAkBC,EAAKT,EAAiB,CACjD,CACO,SAASY,EAAeH,EAAa,CAC1C,OAAOD,GAAkBC,EAAKR,EAAgB,CAChD,CAEA,eAAsBY,GAAaC,EAA6B,CAC9D,GAAI,OAAO,WAAe,IACxB,OAAO,IAAI,QAAQ,CAACC,EAASC,IAAW,CACtC,IAAM,EAAI,IAAI,WACd,EAAE,OAAS,IAAM,CACX,OAAO,EAAE,QAAW,SACtBD,EAAQ,EAAE,MAAM,EAEhBC,EAAO,IAAI,MAAM,wBAAwB,CAAC,CAE9C,EACA,EAAE,QAAUA,EACZ,EAAE,cAAcF,CAAI,CACtB,CAAC,EAGH,GAAI,OAAOG,EAAW,IAAa,CACjC,IAAMC,EAAK,MAAMJ,EAAK,YAAY,EAClC,MAAO,QAAQA,EAAK,IAAI,WAAWG,EAAO,KAAKC,CAAE,EAAE,SAAS,QAAQ,CAAC,EACvE,CAEA,MAAM,IAAI,MAAM,yBAAyB,CAC3C,CAEA,eAAsBC,GAAaC,EAAiB,CAElD,OAAO,MADU,MAAM,MAAMA,CAAO,GACd,KAAK,CAC7B,CDzEA,IAAMC,EAAmB,aAaZC,EAAN,KAA4B,CACjC,cAA2C,IAAI,YAE/C,cAA2C,MAAOC,GAAmB,CACnE,IAAMC,EAAQ,IAAI,YAAY,EAAE,OAAOD,CAAM,EACvCE,EAASC,EAAaF,CAAK,EACjC,MAAO,GAAGH,CAAgB,GAAGI,CAAM,EACrC,EAEA,cAA2C,MAAOE,GAAkB,CAClE,GAAI,CAACA,EAAM,WAAWN,CAAgB,EACpC,MAAM,IAAI,MAAM,yBAAyBA,CAAgB,EAAE,EAE7D,IAAMI,EAASE,EAAM,MAAMN,EAAiB,MAAM,EAC5CG,EAAQI,EAAaH,CAAM,EACjC,OAAO,IAAI,YAAY,EAAE,OAAOD,CAAK,CACvC,EAEA,aAAc,EAEY,SAAY,CAElC,MAAM,QAAQ,QAAQ,EAGtB,QAAWD,KAAU,KAAK,mBAAmB,EAAG,CAC9C,IAAMM,EAA4B,IAAI,YAAY,QAAS,CACzD,OAAQ,CAAE,QAAS,CAAE,MAAO,MAAM,KAAK,cAAcN,CAAM,CAAE,CAAE,CACjE,CAAC,EACD,KAAK,cAAc,cAAcM,CAAK,CACxC,CAEA,IAAMA,EAAyC,IAAI,YACjD,cACA,CAAE,OAAQ,CAAC,CAAE,CACf,EACA,KAAK,cAAc,cAAcA,CAAK,CACxC,GACgB,CAClB,CAEA,gBAA4B,CAAC,EAEnB,oBAA+B,CACvC,GAAI,OAAO,OAAW,IAAa,CACjC,IAAMC,EAAgB,OAAO,aAAa,QAAQ,kBAAkB,EACpE,OAAOA,EACHA,EAAc,MAAM,GAAG,EAAE,IAAI,kBAAkB,EAC/C,CAAC,CACP,KACE,QAAO,KAAK,eAEhB,CAEU,mBAAmBC,EAAmB,CAC1C,OAAO,OAAW,IACpB,OAAO,aAAa,QAAQ,mBAAoBA,EAAQ,KAAK,GAAG,CAAC,EAEjE,KAAK,gBAAkBA,CAE3B,CAEA,MAA2B,MAAOJ,GAAU,CAE1C,MAAM,IAAI,QAASK,GAAY,WAAWA,EAAS,CAAC,CAAC,EAErD,IAAIT,EAASI,EAAQ,MAAM,KAAK,cAAcA,CAAK,EAAI,OAOvD,GALI,OAAO,OAAW,MAEpBJ,EADiB,OAAO,OAAO,+BAAgCA,CAAM,GAChD,QAGlBA,EAME,CACL,IAAMU,EAAkB,KAAK,mBAAmB,EAC3CA,EAAgB,SAASV,CAAM,GAClC,KAAK,mBAAmB,CAAC,GAAGU,EAAiBV,CAAM,CAAC,EAGtD,OAAO,SAAS,OAAO,CACzB,KAba,CACX,IAAMW,EAAuC,CAC3C,MAAO,IAAI,MAAM,6BAA6B,CAChD,EACML,EAA4B,IAAI,YAAY,QAAS,CAAE,OAAAK,CAAO,CAAC,EACrE,KAAK,cAAc,cAAcL,CAAK,CACxC,CAQF,EAEA,OAA6B,MAAOM,GAAY,CAC9C,IAAMZ,EAAS,MAAM,KAAK,cAAcY,EAAQ,KAAK,EAC/CF,EAAkB,KAAK,mBAAmB,EAC1CG,EAASH,EAAgB,SAASV,CAAM,EAC1Ca,GACF,KAAK,mBAAmBH,EAAgB,OAAQI,GAAMA,IAAMd,CAAM,CAAC,EAGrE,IAAMW,EAAwCE,EAC1C,CACE,MAAOD,EAAQ,KACjB,EACA,CACE,MAAOA,EAAQ,MACf,MAAO,IAAI,MAAM,+BAA+B,CAClD,EAEEN,EAA6B,IAAI,YAAY,SAAU,CAAE,OAAAK,CAAO,CAAC,EACvE,KAAK,cAAc,cAAcL,CAAK,CACxC,CACF,EElIAS,IAAAC,IAAAC,IAwDO,IAAMC,EAAN,KAA2B,CACtB,IACA,KACS,QAEnB,IAAI,IAAK,CACP,OAAK,KAAK,MACR,KAAK,KAAO,SAAY,CACtB,GAAM,CAAE,QAASC,CAAQ,EAAI,KAAM,QAAO,gCAAS,EAC7CC,EAAiB,CACrB,KAAM,aACN,GAAG,KAAK,QAAQ,cAClB,EACMC,EAAK,IAAIF,EACbC,EAAe,KACfA,CACF,EACA,aAAMC,EAEH,IAAI,CACH,IAAK,kBACL,MAAO,CACL,iCAAkC,CAChC,IAAK,SAAUC,EAA4B,CACzC,IAAMC,EAAqBD,EAAO,aAC/B,SAAS,EACT,SAAS,GAAI,GAAG,EACnBA,EAAO,SAAS,QAAQ,SAAUE,EAAS,CACzC,IAAMC,EACJ,mBAAmBD,CAAO,EAAI,IAAMD,EAEtC,KAAKE,CAAE,CACT,CAAC,CACH,EAAE,SAAS,CACb,CACF,CACF,CAAC,EAEA,MAAOC,GAAU,CAChB,GACE,EAAAA,GACA,OAAOA,GAAU,UACjB,SAAUA,GACVA,EAAM,OAAS,YAKf,MAAMA,CAEV,CAAC,EACIL,CACT,GAAG,GAEE,KAAK,GACd,CAEA,IAAc,KAAM,CAClB,OAAK,KAAK,OACR,KAAK,MAAQ,SAAY,CACvB,GAAM,CAAE,QAASM,CAAI,EAAI,KAAM,QAAO,mBAAK,EAC3C,OAAO,IAAIA,EAAI,CAAE,OAAQ,EAAM,CAAC,CAClC,GAAG,GAEE,KAAK,IACd,CAEA,MAAgB,mBAAoB,CAClC,OAAO,QAAQ,MAAO,MAAM,KAAK,IAAI,KAAK,GAAG,UAAU,CACzD,CAEA,YAAYC,EAAgC,CAC1C,KAAK,QAAUA,GAAW,CAAC,CAC7B,CAEA,IAAuB,SAAUC,IAAS,CACxC,GAAM,CAACC,EAAWC,EAAQC,CAAO,EAAIH,EAC/BI,EAAMC,EAAgBJ,CAAS,EAEjCK,EACJ,GAAI,CACFA,EAAM,MAAO,MAAM,KAAK,IAAI,IAAIF,CAAG,CACrC,MAAgB,CACd,MAAM,IAAIG,EACR,yFACF,CACF,CAEA,GAAID,EAAI,UACN,MAAM,IAAIC,EACR,yFACF,EAGF,GAAM,CAAE,MAAAC,CAAM,EAAIC,EAAgBL,CAAG,EAC/B,CAAE,MAAAM,EAAO,SAAAC,EAAU,QAAAC,CAAQ,EAAIN,EAC/Bb,EAA6B,CACjC,MAAAiB,EACA,SAAAC,EACA,QAAAC,EACA,IAAAR,EACA,MAAAI,CACF,EAEA,GAAI,CAACK,EAA6BpB,EAAQU,CAAO,EAC/C,MAAM,IAAII,EACR,yFACF,EAQF,GAHAO,EAAmBrB,EAAQ,CAAC,EAAGU,CAAO,EAGlC,CADaY,EAA4B,MAAM,KAAK,IAAKb,CAAM,EACrDT,CAAM,EAClB,MAAM,IAAIuB,EAEZ,OAAOvB,CACT,EAEA,OAA6B,SAAUO,IAAS,CAC9C,GAAM,CAACC,EAAWE,CAAO,EAAIH,EAEvBI,EAAMC,EAAgBJ,CAAS,EAC/B,CAAE,MAAAO,CAAM,EAAIC,EAAgBL,CAAG,EACrC,GAAII,IAAUL,EAAQ,MACpB,MAAM,IAAIc,EACR,sDACF,EAGF,IAAIX,EACJ,GAAI,CACFA,EAAM,MAAO,MAAM,KAAK,IAAI,IAAIF,CAAG,CACrC,MAAQ,CACN,MAAM,IAAIG,EAAsB,mBAAmB,CACrD,CAEA,GAAID,EAAI,UACN,MAAM,IAAIC,EAAsB,mBAAmB,EAIrDD,EAAI,UAAY,GAChBA,EAAI,aAAe,MAAM,KAAK,kBAAkB,EAChD,GAAI,CACF,MAAO,MAAM,KAAK,IAAI,IAAIA,CAAG,CAC/B,MAAQ,CACN,MAAM,IAAIC,EAAsB,mBAAmB,CACrD,CAGF,EAEA,KAAyB,SAAUP,IAAS,CAC1C,GAAM,CAACkB,EAAef,CAAO,EAAIH,EAE3BQ,EAAQL,EAAQ,MAChBP,EAAKuB,GAAa,EAClBf,EAAMgB,EAAgBZ,EAAOZ,CAAE,EAE/B,CAAE,MAAAc,EAAO,SAAAC,EAAU,QAAAC,CAAQ,EAAIM,EAC/BzB,EAA6B,CACjC,MAAAiB,EACA,SAAAC,EACA,QAAAC,EACA,aAAc,MAAM,KAAK,kBAAkB,EAC3C,UAAW,EACb,EAEA,aACE,MAAM,KAAK,IACX,IAAI,CACJ,IAAKR,EACL,GAAGX,CACL,CAAC,EAEM,CACL,GAAGyB,EACH,MAAAV,EACA,IAAAJ,CACF,CACF,EAEA,MAAiB,aACfJ,EACAqB,EAOA,CAGA,GAAIA,EAAgB,CAClB,IAAMC,EAAiB,KAAK,QAAQ,gBAAkB,IAChDC,EACJ,KAAK,IAAI,EAAIF,EAAe,eAC1BE,EAA+BD,GAGjC,MAAM,IAAI,QAASE,GACjB,WAAWA,EAASF,EAAiBC,CAA4B,CACnE,CAEJ,CAEA,GAAM,CAACE,EAAkBvB,EAAQC,CAAO,EAAIH,EACtC0B,EAAWX,EAA4B,MAAM,KAAK,IAAKb,CAAM,EAC7DyB,EAAiBN,EACnBA,EAAe,gBAAgB,SAAS,EAAE,SAAS,GAAI,GAAG,EAC1D,GACEO,EAAe,SAEfC,EAAgB,IAAI,IAEpBC,EAAY,MAAM,KAAK,kBAAkB,EAE/C,QAAWnC,KAAW8B,EAAkB,CACtC,IAAMM,EAAY,mBAAmBpC,CAAO,EAAI,IAC1CqC,EAAWD,EAAYJ,EACvBM,GAASF,EAAYH,EAErBM,GAAS,MACb,MAAM,KAAK,IACX,MAA0B,2CAA4C,CACtE,SAAAF,EACA,OAAAC,GACA,aAAc,EAChB,CAAC,EAED,QAAWE,MAAOD,GAAO,KAAM,CAC7B,IAAM5B,EAAM6B,GAAI,IAChB,GAAI,CAAC7B,EAAK,SAEV,IAAMF,EAAME,EAAI,IAMhB,GAJIuB,EAAc,IAAIzB,CAAG,IACzByB,EAAc,IAAIzB,CAAG,EAGjB,CAACiB,GAAkBf,EAAI,WAAW,SAEtC,GAAM,CAAE,UAAA8B,GAAW,MAAA1B,GAAO,SAAAC,GAAU,QAAAC,EAAQ,EAAIN,EAC1C,CAAE,MAAAE,EAAM,EAAIC,EAAgBL,CAAG,EAE/BX,EAA6B,CACjC,IAAAW,EACA,MAAAM,GACA,QAAAE,GACA,SAAAD,GACA,MAAAH,EACF,EAEKK,EAA6BpB,EAAQU,CAAO,IAEjDW,EAAmBrB,EAAQgC,EAAkBtB,CAAO,EAE/CuB,EAASjC,CAAM,IAEpB,MAAM2C,GACF,CACE,UAAW,GACX,OAAQ,CAAE,IAAAhC,CAAI,CAChB,EACA,CAAE,OAAAX,CAAO,GACf,CACF,CAEA,MAAO,CACL,eAAgB,KAAK,IAAI,EACzB,gBAAiBqC,CACnB,CACF,CAEU,eACR9B,EACAqB,EAIQ,CACR,GAAM,CAACV,EAAUT,EAAQC,CAAO,EAAIH,EACpC,MACE,YACA,KAAK,UAAU,CACb,SAAAW,EACA,OAAAT,EACA,eAAAmB,EACA,MAAOlB,GAAS,KAClB,CAAC,CAEL,CAEA,MAAiB,iBACfH,EACAqB,EAIAlB,EACsC,CACtC,GAAIA,GAAS,QAAUH,EAAK,CAAC,GAAG,MAC9B,MAAM,IAAIiB,EACR,mDACF,EAEF,IAAMoB,EAAW,KAAK,aAAqBrC,EAAMqB,CAAc,EAE/D,OAAa,CACX,IAAMa,EAAS,MAAMG,EAAS,KAAK,EACnC,GAAIH,EAAO,KACT,MAAO,CACL,SAAW/B,GACT,KAAK,iBAAyBH,EAAMkC,EAAO,MAAO/B,CAAO,EAC3D,OAAQ,KAAK,eAAeH,EAAMkC,EAAO,KAAK,CAChD,EAEF,MAAMA,EAAO,KACf,CACF,CAEA,SAAiC,IAAIlC,IAAS,CAC5C,GAAM,CAACW,EAAUT,EAAQC,CAAO,EAAIH,EAC9BqC,EAAW,KAAK,aAA+B,CACnD1B,EACAT,EACAC,CACF,CAAC,EAEKmC,EAAQ,KACd,OAAQ,iBAAmB,CACzB,OAAa,CACX,IAAMJ,EAAS,MAAMG,EAAS,KAAK,EACnC,GAAIH,EAAO,KACT,MAAO,CACL,SAAW/B,GACTmC,EAAM,iBACJtC,EACAkC,EAAO,MACP/B,CACF,EACF,OAAQmC,EAAM,eAAetC,EAAMkC,EAAO,KAAK,CACjD,EAGEA,EAAO,MAAM,YACjB,MAAMA,EAAO,MACf,CACF,GAAG,CACL,EAEA,iBAAiD,IAAIlC,IAAS,CAC5D,GAAM,CAACuC,EAAQpC,CAAO,EAAIH,EAC1B,GAAIuC,EAAO,WAAW,WAAW,EAAG,CAElC,GAAM,CAAE,SAAA5B,EAAU,OAAAT,EAAQ,MAAAM,EAAO,eAAAa,CAAe,EAAI,KAAK,MACvDkB,EAAO,MAAM,CAAkB,CACjC,EACA,GAAI/B,GAASA,IAAUL,GAAS,MAC9B,MAAM,IAAIc,EACR,mDACF,EAEF,OAAO,KAAK,iBACV,CAACN,EAAUT,EAAQC,CAAO,EAC1BkB,CACF,CACF,KACE,OAAM,IAAId,EAAsB,kBAAkB,CAEtD,CACF,EC/aAiC,IAAAC,IAAAC,IAcA,IAAAC,GAAuB,SAEjBC,GAAsB,CAC1B,WAAY,CACV,MAAO,CACL,WAAY,CACV,WAAY,CAAE,KAAM,QAAS,EAC7B,KAAM,CAAE,KAAM,QAAS,EACvB,KAAM,CAAE,KAAM,QAAS,CACzB,EACA,SAAU,CAAC,aAAc,OAAQ,MAAM,CACzC,CACF,CACF,EAEaC,EAAN,KAAyB,CACpB,GAEV,YAAYC,EAA+C,CACzD,KAAK,GAAKA,CACZ,CAEA,UAAmC,SAAUC,IAAS,CACpD,GAAM,CAACC,EAAOC,CAAO,EAAIF,EAEnBG,EAAa,MAAMC,GAAaH,EAAM,IAAI,EAC1CI,EAAOJ,EAAM,KAAK,KAElB,CAAE,IAAAK,CAAI,EAAI,MAAM,KAAK,GAAG,KAC5B,CACE,MAAO,CACL,WAAAH,EACA,KAAAE,EACA,KAAMJ,EAAM,KAAK,IACnB,EACA,SAAU,CAAC,EACX,QAASA,EAAM,OACjB,EACAC,CACF,EAEM,CAAE,MAAAK,EAAO,GAAAC,CAAG,EAAIC,EAAgBH,CAAG,EACzC,OAAOI,GAAeH,EAAOC,CAAE,CACjC,EAEA,SAAiC,SAAUR,IAAS,CAClD,GAAM,CAACW,EAAUC,EAAcV,CAAO,EAAIF,EACpC,CAAE,MAAAO,EAAO,GAAAC,CAAG,EAAIK,EAAeF,CAAQ,EACvCG,EAAYC,EAAgBR,EAAOC,CAAE,EAErCQ,EAAS,MAAM,KAAK,GAAG,IAC3BF,EACAjB,GACAK,CACF,EAEM,CAAE,WAAAC,EAAY,KAAAE,EAAM,KAAAY,CAAK,EAAID,EAAO,MAE1C,GAAIJ,GAAc,UAAYK,EAAOL,EAAa,SAChD,MAAM,IAAIM,EAAsB,yBAAyB,EAI3D,GAAIN,GAAc,QACG,IAAI,GAAAO,QAAW,CAChC,QAAS,CAAE,OAAQP,EAAa,MAAO,CACzC,CAAC,EACc,UAAU,CAACP,CAAI,CAAC,IAAMA,EACnC,MAAM,IAAIe,EAA2B,2BAA2Bf,CAAI,EAAE,EAI1E,IAAMgB,EAAO,MAAMC,GAAanB,CAAU,EAC1C,GAAIkB,EAAK,OAASJ,GAAQI,EAAK,OAAShB,EACtC,MAAM,IAAI,MAAM,cAAc,EAGhC,MAAO,CACL,KAAAgB,EACA,MAAOL,EAAO,MACd,QAASA,EAAO,OAClB,CACF,EAEA,YAAuC,SAAUhB,IAAS,CACxD,GAAM,CAACW,EAAUT,CAAO,EAAIF,EACtB,CAAE,MAAAO,EAAO,GAAAC,CAAG,EAAIK,EAAeF,CAAQ,EACvCG,EAAYC,EAAgBR,EAAOC,CAAE,EAE3C,MAAM,KAAK,GAAG,OAAOM,EAAWZ,CAAO,CACzC,CACF,ER3FO,IAAMqB,GAAN,KAAwC,CACnC,sBAAwB,IAAIC,EACtC,MAAQ,KAAK,sBAAsB,MAAM,KAAK,KAAK,qBAAqB,EACxE,OAAS,KAAK,sBAAsB,OAAO,KAAK,KAAK,qBAAqB,EAC1E,cAAgB,KAAK,sBAAsB,cAAc,KACvD,KAAK,qBACP,EACA,cAAgB,KAAK,sBAAsB,cAAc,KACvD,KAAK,qBACP,EACA,cAAgB,KAAK,sBAAsB,cAEjC,qBACV,KACA,IACA,OACA,SACA,iBAEU,mBACV,UACA,SACA,YAEA,YAAYC,EAAgC,CAC1C,KAAK,qBAAuB,IAAIC,EAAqBD,CAAO,EAC5D,KAAK,KAAO,KAAK,qBAAqB,KAAK,KAAK,KAAK,oBAAoB,EACzE,KAAK,IAAM,KAAK,qBAAqB,IAAI,KAAK,KAAK,oBAAoB,EACvE,KAAK,OAAS,KAAK,qBAAqB,OAAO,KAC7C,KAAK,oBACP,EACA,KAAK,SAAW,KAAK,qBAAqB,SAAS,KACjD,KAAK,oBACP,EACA,KAAK,iBAAmB,KAAK,qBAAqB,iBAAiB,KACjE,KAAK,oBACP,EAEA,KAAK,mBAAqB,IAAIE,EAAmB,KAAK,oBAAoB,EAC1E,KAAK,UAAY,KAAK,mBAAmB,UAAU,KACjD,KAAK,kBACP,EACA,KAAK,SAAW,KAAK,mBAAmB,SAAS,KAC/C,KAAK,kBACP,EACA,KAAK,YAAc,KAAK,mBAAmB,YAAY,KACrD,KAAK,kBACP,CACF,CACF",
6
+ "names": ["require_charset", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredCharsets", "simpleCharsetRegExp", "parseAcceptCharset", "accept", "accepts", "i", "j", "charset", "parseCharset", "str", "match", "q", "params", "p", "getCharsetPriority", "accepted", "index", "priority", "spec", "specify", "s", "provided", "isQuality", "compareSpecs", "getFullCharset", "priorities", "type", "a", "b", "require_encoding", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredEncodings", "simpleEncodingRegExp", "parseAcceptEncoding", "accept", "accepts", "hasIdentity", "minQuality", "i", "j", "encoding", "parseEncoding", "specify", "str", "match", "q", "params", "p", "getEncodingPriority", "accepted", "index", "priority", "spec", "s", "provided", "preferred", "comparator", "a", "b", "aPreferred", "bPreferred", "compareSpecs", "isQuality", "getFullEncoding", "priorities", "type", "require_language", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredLanguages", "simpleLanguageRegExp", "parseAcceptLanguage", "accept", "accepts", "i", "j", "language", "parseLanguage", "str", "match", "prefix", "suffix", "full", "q", "params", "getLanguagePriority", "accepted", "index", "priority", "spec", "specify", "p", "s", "provided", "isQuality", "compareSpecs", "getFullLanguage", "priorities", "type", "a", "b", "require_mediaType", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredMediaTypes", "simpleMediaTypeRegExp", "parseAccept", "accept", "accepts", "splitMediaTypes", "i", "j", "mediaType", "parseMediaType", "str", "match", "params", "q", "subtype", "type", "kvps", "splitParameters", "splitKeyValuePair", "pair", "key", "val", "value", "getMediaTypePriority", "accepted", "index", "priority", "spec", "specify", "p", "s", "keys", "k", "provided", "isQuality", "compareSpecs", "getFullType", "priorities", "a", "b", "quoteCount", "string", "count", "parameters", "require_negotiator", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredCharsets", "preferredEncodings", "preferredLanguages", "preferredMediaTypes", "Negotiator", "request", "available", "set", "opts", "options", "init_dirname", "init_buffer", "init_process", "init_dirname", "init_buffer", "init_process", "GraffitiObjectJSONSchema", "GraffitiPostObjectJSONSchema", "GraffitiErrorForbidden", "_GraffitiErrorForbidden", "message", "GraffitiErrorNotFound", "_GraffitiErrorNotFound", "GraffitiErrorInvalidSchema", "_GraffitiErrorInvalidSchema", "GraffitiErrorSchemaMismatch", "_GraffitiErrorSchemaMismatch", "GraffitiErrorTooLarge", "_GraffitiErrorTooLarge", "GraffitiErrorNotAcceptable", "_GraffitiErrorNotAcceptable", "unpackObjectUrl", "url", "compileGraffitiObjectSchema", "ajv", "schema", "error", "isActorAllowedGraffitiObject", "object", "session", "maskGraffitiObject", "channels", "channel", "init_dirname", "init_buffer", "init_process", "init_dirname", "init_buffer", "init_process", "encodeBase64", "bytes", "decodeBase64", "base64Url", "base64", "c", "randomBase64", "numBytes", "OBJECT_URL_PREFIX", "MEDIA_URL_PREFIX", "encodeGraffitiUrl", "actor", "id", "prefix", "encodeObjectUrl", "encodeMediaUrl", "decodeGraffitiUrl", "url", "slices", "decodeObjectUrl", "decodeMediaUrl", "blobToBase64", "blob", "resolve", "reject", "Buffer", "ab", "base64ToBlob", "dataUrl", "DID_LOCAL_PREFIX", "GraffitiLocalIdentity", "handle", "bytes", "base64", "encodeBase64", "actor", "decodeBase64", "event", "handlesString", "handles", "resolve", "existingHandles", "detail", "session", "exists", "h", "init_dirname", "init_buffer", "init_process", "GraffitiLocalObjects", "PouchDB", "pouchDbOptions", "db", "object", "paddedLastModified", "channel", "id", "error", "Ajv", "options", "args", "urlObject", "schema", "session", "url", "u", "doc", "o", "actor", "decodeObjectUrl", "value", "channels", "allowed", "G", "j", "d", "n", "s", "objectPartial", "randomBase64", "encodeObjectUrl", "continueParams", "continueBuffer", "timeElapsedSinceLastDiscover", "resolve", "discoverChannels", "validate", "startKeySuffix", "endKeySuffix", "processedUrls", "startTime", "keyPrefix", "startkey", "endkey", "result", "row", "tombstone", "iterator", "this_", "cursor", "init_dirname", "init_buffer", "init_process", "import_negotiator", "MEDIA_OBJECT_SCHEMA", "GraffitiLocalMedia", "db", "args", "media", "session", "dataBase64", "blobToBase64", "type", "url", "actor", "id", "decodeObjectUrl", "encodeMediaUrl", "mediaUrl", "requirements", "decodeMediaUrl", "objectUrl", "encodeObjectUrl", "object", "size", "c", "Negotiator", "f", "data", "base64ToBlob", "GraffitiLocal", "GraffitiLocalIdentity", "options", "GraffitiLocalObjects", "GraffitiLocalMedia"]
7
7
  }
@@ -37,7 +37,6 @@ class GraffitiLocalObjects {
37
37
  db_;
38
38
  ajv_;
39
39
  options;
40
- operationClock = 0;
41
40
  get db() {
42
41
  if (!this.db_) {
43
42
  this.db_ = (async () => {
@@ -84,6 +83,9 @@ class GraffitiLocalObjects {
84
83
  }
85
84
  return this.ajv_;
86
85
  }
86
+ async getOperationClock() {
87
+ return Number((await (await this.db).info()).update_seq);
88
+ }
87
89
  constructor(options) {
88
90
  this.options = options ?? {};
89
91
  }
@@ -143,13 +145,12 @@ class GraffitiLocalObjects {
143
145
  throw new import_api.GraffitiErrorNotFound("Object not found.");
144
146
  }
145
147
  doc.tombstone = true;
146
- doc.lastModified = this.operationClock;
148
+ doc.lastModified = await this.getOperationClock();
147
149
  try {
148
150
  await (await this.db).put(doc);
149
151
  } catch {
150
152
  throw new import_api.GraffitiErrorNotFound("Object not found.");
151
153
  }
152
- this.operationClock++;
153
154
  return;
154
155
  };
155
156
  post = async (...args) => {
@@ -162,14 +163,13 @@ class GraffitiLocalObjects {
162
163
  value,
163
164
  channels,
164
165
  allowed,
165
- lastModified: this.operationClock,
166
+ lastModified: await this.getOperationClock(),
166
167
  tombstone: false
167
168
  };
168
169
  await (await this.db).put({
169
170
  _id: url,
170
171
  ...object
171
172
  });
172
- this.operationClock++;
173
173
  return {
174
174
  ...objectPartial,
175
175
  actor,
@@ -191,7 +191,7 @@ class GraffitiLocalObjects {
191
191
  const startKeySuffix = continueParams ? continueParams.ifModifiedSince.toString().padStart(15, "0") : "";
192
192
  const endKeySuffix = "\uFFFF";
193
193
  const processedUrls = /* @__PURE__ */ new Set();
194
- const startTime = this.operationClock;
194
+ const startTime = await this.getOperationClock();
195
195
  for (const channel of discoverChannels) {
196
196
  const keyPrefix = encodeURIComponent(channel) + "/";
197
197
  const startkey = keyPrefix + startKeySuffix;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/objects.ts"],
4
- "sourcesContent": ["import type {\n Graffiti,\n GraffitiObjectBase,\n JSONSchema,\n GraffitiSession,\n GraffitiObjectStreamContinue,\n GraffitiObjectStreamContinueEntry,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorNotFound,\n GraffitiErrorSchemaMismatch,\n GraffitiErrorForbidden,\n unpackObjectUrl,\n maskGraffitiObject,\n isActorAllowedGraffitiObject,\n compileGraffitiObjectSchema,\n} from \"@graffiti-garden/api\";\nimport { randomBase64, decodeObjectUrl, encodeObjectUrl } from \"./utilities.js\";\nimport type Ajv from \"ajv\";\n\n/**\n * Constructor options for the GraffitiPoubchDB class.\n */\nexport interface GraffitiLocalOptions {\n /**\n * Options to pass to the PouchDB constructor.\n * Defaults to `{ name: \"graffitiDb\" }`.\n *\n * See the [PouchDB documentation](https://pouchdb.com/api.html#create_database)\n * for available options.\n */\n pouchDBOptions?: PouchDB.Configuration.DatabaseConfiguration;\n /**\n * Wait at least this long (in milliseconds) before continuing a stream.\n * A basic form of rate limiting. Defaults to 2 seconds.\n */\n continueBuffer?: number;\n}\n\ntype GraffitiObjectData = {\n tombstone: boolean;\n value: {};\n channels: string[];\n allowed?: string[] | null;\n lastModified: number;\n};\n\ntype ContinueDiscoverParams = {\n lastDiscovered: number;\n ifModifiedSince: number;\n};\n\n/**\n * An implementation of only the database operations of the\n * GraffitiAPI without synchronization or session management.\n */\nexport class GraffitiLocalObjects {\n protected db_: Promise<PouchDB.Database<GraffitiObjectData>> | undefined;\n protected ajv_: Promise<Ajv> | undefined;\n protected readonly options: GraffitiLocalOptions;\n protected operationClock: number = 0;\n\n get db() {\n if (!this.db_) {\n this.db_ = (async () => {\n const { default: PouchDB } = await import(\"pouchdb\");\n const pouchDbOptions = {\n name: \"graffitiDb\",\n ...this.options.pouchDBOptions,\n };\n const db = new PouchDB<GraffitiObjectData>(\n pouchDbOptions.name,\n pouchDbOptions,\n );\n await db\n //@ts-ignore\n .put({\n _id: \"_design/indexes\",\n views: {\n objectsPerChannelAndLastModified: {\n map: function (object: GraffitiObjectData) {\n const paddedLastModified = object.lastModified\n .toString()\n .padStart(15, \"0\");\n object.channels.forEach(function (channel) {\n const id =\n encodeURIComponent(channel) + \"/\" + paddedLastModified;\n //@ts-ignore\n emit(id);\n });\n }.toString(),\n },\n },\n })\n //@ts-ignore\n .catch((error) => {\n if (\n error &&\n typeof error === \"object\" &&\n \"name\" in error &&\n error.name === \"conflict\"\n ) {\n // Design document already exists\n return;\n } else {\n throw error;\n }\n });\n return db;\n })();\n }\n return this.db_;\n }\n\n protected get ajv() {\n if (!this.ajv_) {\n this.ajv_ = (async () => {\n const { default: Ajv } = await import(\"ajv\");\n return new Ajv({ strict: false });\n })();\n }\n return this.ajv_;\n }\n\n constructor(options?: GraffitiLocalOptions) {\n this.options = options ?? {};\n }\n\n get: Graffiti[\"get\"] = async (...args) => {\n const [urlObject, schema, session] = args;\n const url = unpackObjectUrl(urlObject);\n\n let doc: GraffitiObjectData;\n try {\n doc = await (await this.db).get(url);\n } catch (error) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n const { actor } = decodeObjectUrl(url);\n const { value, channels, allowed } = doc;\n const object: GraffitiObjectBase = {\n value,\n channels,\n allowed,\n url,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n // Mask out the allowed list and channels\n // if the user is not the owner\n maskGraffitiObject(object, [], session);\n\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n if (!validate(object)) {\n throw new GraffitiErrorSchemaMismatch();\n }\n return object;\n };\n\n delete: Graffiti[\"delete\"] = async (...args) => {\n const [urlObject, session] = args;\n\n const url = unpackObjectUrl(urlObject);\n const { actor } = decodeObjectUrl(url);\n if (actor !== session.actor) {\n throw new GraffitiErrorForbidden(\n \"You cannot delete an object that you did not create.\",\n );\n }\n\n let doc: GraffitiObjectData & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta;\n try {\n doc = await (await this.db).get(url);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n // Set the tombstone and update lastModified\n doc.tombstone = true;\n doc.lastModified = this.operationClock;\n try {\n await (await this.db).put(doc);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n this.operationClock++;\n\n return;\n };\n\n post: Graffiti[\"post\"] = async (...args) => {\n const [objectPartial, session] = args;\n\n const actor = session.actor;\n const id = randomBase64();\n const url = encodeObjectUrl(actor, id);\n\n const { value, channels, allowed } = objectPartial;\n const object: GraffitiObjectData = {\n value,\n channels,\n allowed,\n lastModified: this.operationClock,\n tombstone: false,\n };\n\n await (\n await this.db\n ).put({\n _id: url,\n ...object,\n });\n this.operationClock++;\n\n return {\n ...objectPartial,\n actor,\n url,\n };\n };\n\n protected async *discoverMeta<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams?: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): AsyncGenerator<\n GraffitiObjectStreamContinueEntry<Schema>,\n ContinueDiscoverParams\n > {\n // If we are continuing a discover, make sure to wait at\n // least 2 seconds since the last poll to start a new one.\n if (continueParams) {\n const continueBuffer = this.options.continueBuffer ?? 2000;\n const timeElapsedSinceLastDiscover =\n Date.now() - continueParams.lastDiscovered;\n if (timeElapsedSinceLastDiscover < continueBuffer) {\n // Continue was called too soon,\n // wait a bit before continuing\n await new Promise((resolve) =>\n setTimeout(resolve, continueBuffer - timeElapsedSinceLastDiscover),\n );\n }\n }\n\n const [discoverChannels, schema, session] = args;\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n const startKeySuffix = continueParams\n ? continueParams.ifModifiedSince.toString().padStart(15, \"0\")\n : \"\";\n const endKeySuffix = \"\\uffff\";\n\n const processedUrls = new Set<string>();\n\n const startTime = this.operationClock;\n\n for (const channel of discoverChannels) {\n const keyPrefix = encodeURIComponent(channel) + \"/\";\n const startkey = keyPrefix + startKeySuffix;\n const endkey = keyPrefix + endKeySuffix;\n\n const result = await (\n await this.db\n ).query<GraffitiObjectData>(\"indexes/objectsPerChannelAndLastModified\", {\n startkey,\n endkey,\n include_docs: true,\n });\n\n for (const row of result.rows) {\n const doc = row.doc;\n if (!doc) continue;\n\n const url = doc._id;\n\n if (processedUrls.has(url)) continue;\n processedUrls.add(url);\n\n // If this is not a continuation, skip tombstones\n if (!continueParams && doc.tombstone) continue;\n\n const { tombstone, value, channels, allowed } = doc;\n const { actor } = decodeObjectUrl(url);\n\n const object: GraffitiObjectBase = {\n url,\n value,\n allowed,\n channels,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) continue;\n\n maskGraffitiObject(object, discoverChannels, session);\n\n if (!validate(object)) continue;\n\n yield tombstone\n ? {\n tombstone: true,\n object: { url },\n }\n : { object };\n }\n }\n\n return {\n lastDiscovered: Date.now(),\n ifModifiedSince: startTime,\n };\n }\n\n protected discoverCursor(\n args: Parameters<typeof Graffiti.prototype.discover<{}>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): string {\n const [channels, schema, session] = args;\n return (\n \"discover:\" +\n JSON.stringify({\n channels,\n schema,\n continueParams,\n actor: session?.actor,\n })\n );\n }\n\n protected async *discoverContinue<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<Schema> {\n if (session?.actor !== args[2]?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n const iterator = this.discoverMeta<Schema>(args, continueParams);\n\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this.discoverContinue<Schema>(args, result.value, session),\n cursor: this.discoverCursor(args, result.value),\n };\n }\n yield result.value;\n }\n }\n\n discover: Graffiti[\"discover\"] = (...args) => {\n const [channels, schema, session] = args;\n const iterator = this.discoverMeta<(typeof args)[1]>([\n channels,\n schema,\n session,\n ]);\n\n const this_ = this;\n return (async function* () {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this_.discoverContinue<(typeof args)[1]>(\n args,\n result.value,\n session,\n ),\n cursor: this_.discoverCursor(args, result.value),\n };\n }\n // Make sure to filter out tombstones\n if (result.value.tombstone) continue;\n yield result.value;\n }\n })();\n };\n\n continueDiscover: Graffiti[\"continueDiscover\"] = (...args) => {\n const [cursor, session] = args;\n if (cursor.startsWith(\"discover:\")) {\n // TODO: use AJV here\n const { channels, schema, actor, continueParams } = JSON.parse(\n cursor.slice(\"discover:\".length),\n );\n if (actor && actor !== session?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n return this.discoverContinue<{}>(\n [channels, schema, session],\n continueParams,\n );\n } else {\n throw new GraffitiErrorNotFound(\"Cursor not found\");\n }\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type {\n Graffiti,\n GraffitiObjectBase,\n JSONSchema,\n GraffitiSession,\n GraffitiObjectStreamContinue,\n GraffitiObjectStreamContinueEntry,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorNotFound,\n GraffitiErrorSchemaMismatch,\n GraffitiErrorForbidden,\n unpackObjectUrl,\n maskGraffitiObject,\n isActorAllowedGraffitiObject,\n compileGraffitiObjectSchema,\n} from \"@graffiti-garden/api\";\nimport { randomBase64, decodeObjectUrl, encodeObjectUrl } from \"./utilities.js\";\nimport type Ajv from \"ajv\";\n\n/**\n * Constructor options for the GraffitiPoubchDB class.\n */\nexport interface GraffitiLocalOptions {\n /**\n * Options to pass to the PouchDB constructor.\n * Defaults to `{ name: \"graffitiDb\" }`.\n *\n * See the [PouchDB documentation](https://pouchdb.com/api.html#create_database)\n * for available options.\n */\n pouchDBOptions?: PouchDB.Configuration.DatabaseConfiguration;\n /**\n * Wait at least this long (in milliseconds) before continuing a stream.\n * A basic form of rate limiting. Defaults to 2 seconds.\n */\n continueBuffer?: number;\n}\n\ntype GraffitiObjectData = {\n tombstone: boolean;\n value: {};\n channels: string[];\n allowed?: string[] | null;\n lastModified: number;\n};\n\ntype ContinueDiscoverParams = {\n lastDiscovered: number;\n ifModifiedSince: number;\n};\n\n/**\n * An implementation of only the database operations of the\n * GraffitiAPI without synchronization or session management.\n */\nexport class GraffitiLocalObjects {\n protected db_: Promise<PouchDB.Database<GraffitiObjectData>> | undefined;\n protected ajv_: Promise<Ajv> | undefined;\n protected readonly options: GraffitiLocalOptions;\n\n get db() {\n if (!this.db_) {\n this.db_ = (async () => {\n const { default: PouchDB } = await import(\"pouchdb\");\n const pouchDbOptions = {\n name: \"graffitiDb\",\n ...this.options.pouchDBOptions,\n };\n const db = new PouchDB<GraffitiObjectData>(\n pouchDbOptions.name,\n pouchDbOptions,\n );\n await db\n //@ts-ignore\n .put({\n _id: \"_design/indexes\",\n views: {\n objectsPerChannelAndLastModified: {\n map: function (object: GraffitiObjectData) {\n const paddedLastModified = object.lastModified\n .toString()\n .padStart(15, \"0\");\n object.channels.forEach(function (channel) {\n const id =\n encodeURIComponent(channel) + \"/\" + paddedLastModified;\n //@ts-ignore\n emit(id);\n });\n }.toString(),\n },\n },\n })\n //@ts-ignore\n .catch((error) => {\n if (\n error &&\n typeof error === \"object\" &&\n \"name\" in error &&\n error.name === \"conflict\"\n ) {\n // Design document already exists\n return;\n } else {\n throw error;\n }\n });\n return db;\n })();\n }\n return this.db_;\n }\n\n protected get ajv() {\n if (!this.ajv_) {\n this.ajv_ = (async () => {\n const { default: Ajv } = await import(\"ajv\");\n return new Ajv({ strict: false });\n })();\n }\n return this.ajv_;\n }\n\n protected async getOperationClock() {\n return Number((await (await this.db).info()).update_seq);\n }\n\n constructor(options?: GraffitiLocalOptions) {\n this.options = options ?? {};\n }\n\n get: Graffiti[\"get\"] = async (...args) => {\n const [urlObject, schema, session] = args;\n const url = unpackObjectUrl(urlObject);\n\n let doc: GraffitiObjectData;\n try {\n doc = await (await this.db).get(url);\n } catch (error) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n const { actor } = decodeObjectUrl(url);\n const { value, channels, allowed } = doc;\n const object: GraffitiObjectBase = {\n value,\n channels,\n allowed,\n url,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n // Mask out the allowed list and channels\n // if the user is not the owner\n maskGraffitiObject(object, [], session);\n\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n if (!validate(object)) {\n throw new GraffitiErrorSchemaMismatch();\n }\n return object;\n };\n\n delete: Graffiti[\"delete\"] = async (...args) => {\n const [urlObject, session] = args;\n\n const url = unpackObjectUrl(urlObject);\n const { actor } = decodeObjectUrl(url);\n if (actor !== session.actor) {\n throw new GraffitiErrorForbidden(\n \"You cannot delete an object that you did not create.\",\n );\n }\n\n let doc: GraffitiObjectData & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta;\n try {\n doc = await (await this.db).get(url);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n // Set the tombstone and update lastModified\n doc.tombstone = true;\n doc.lastModified = await this.getOperationClock();\n try {\n await (await this.db).put(doc);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n return;\n };\n\n post: Graffiti[\"post\"] = async (...args) => {\n const [objectPartial, session] = args;\n\n const actor = session.actor;\n const id = randomBase64();\n const url = encodeObjectUrl(actor, id);\n\n const { value, channels, allowed } = objectPartial;\n const object: GraffitiObjectData = {\n value,\n channels,\n allowed,\n lastModified: await this.getOperationClock(),\n tombstone: false,\n };\n\n await (\n await this.db\n ).put({\n _id: url,\n ...object,\n });\n\n return {\n ...objectPartial,\n actor,\n url,\n };\n };\n\n protected async *discoverMeta<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams?: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): AsyncGenerator<\n GraffitiObjectStreamContinueEntry<Schema>,\n ContinueDiscoverParams\n > {\n // If we are continuing a discover, make sure to wait at\n // least 2 seconds since the last poll to start a new one.\n if (continueParams) {\n const continueBuffer = this.options.continueBuffer ?? 2000;\n const timeElapsedSinceLastDiscover =\n Date.now() - continueParams.lastDiscovered;\n if (timeElapsedSinceLastDiscover < continueBuffer) {\n // Continue was called too soon,\n // wait a bit before continuing\n await new Promise((resolve) =>\n setTimeout(resolve, continueBuffer - timeElapsedSinceLastDiscover),\n );\n }\n }\n\n const [discoverChannels, schema, session] = args;\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n const startKeySuffix = continueParams\n ? continueParams.ifModifiedSince.toString().padStart(15, \"0\")\n : \"\";\n const endKeySuffix = \"\\uffff\";\n\n const processedUrls = new Set<string>();\n\n const startTime = await this.getOperationClock();\n\n for (const channel of discoverChannels) {\n const keyPrefix = encodeURIComponent(channel) + \"/\";\n const startkey = keyPrefix + startKeySuffix;\n const endkey = keyPrefix + endKeySuffix;\n\n const result = await (\n await this.db\n ).query<GraffitiObjectData>(\"indexes/objectsPerChannelAndLastModified\", {\n startkey,\n endkey,\n include_docs: true,\n });\n\n for (const row of result.rows) {\n const doc = row.doc;\n if (!doc) continue;\n\n const url = doc._id;\n\n if (processedUrls.has(url)) continue;\n processedUrls.add(url);\n\n // If this is not a continuation, skip tombstones\n if (!continueParams && doc.tombstone) continue;\n\n const { tombstone, value, channels, allowed } = doc;\n const { actor } = decodeObjectUrl(url);\n\n const object: GraffitiObjectBase = {\n url,\n value,\n allowed,\n channels,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) continue;\n\n maskGraffitiObject(object, discoverChannels, session);\n\n if (!validate(object)) continue;\n\n yield tombstone\n ? {\n tombstone: true,\n object: { url },\n }\n : { object };\n }\n }\n\n return {\n lastDiscovered: Date.now(),\n ifModifiedSince: startTime,\n };\n }\n\n protected discoverCursor(\n args: Parameters<typeof Graffiti.prototype.discover<{}>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): string {\n const [channels, schema, session] = args;\n return (\n \"discover:\" +\n JSON.stringify({\n channels,\n schema,\n continueParams,\n actor: session?.actor,\n })\n );\n }\n\n protected async *discoverContinue<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<Schema> {\n if (session?.actor !== args[2]?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n const iterator = this.discoverMeta<Schema>(args, continueParams);\n\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this.discoverContinue<Schema>(args, result.value, session),\n cursor: this.discoverCursor(args, result.value),\n };\n }\n yield result.value;\n }\n }\n\n discover: Graffiti[\"discover\"] = (...args) => {\n const [channels, schema, session] = args;\n const iterator = this.discoverMeta<(typeof args)[1]>([\n channels,\n schema,\n session,\n ]);\n\n const this_ = this;\n return (async function* () {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this_.discoverContinue<(typeof args)[1]>(\n args,\n result.value,\n session,\n ),\n cursor: this_.discoverCursor(args, result.value),\n };\n }\n // Make sure to filter out tombstones\n if (result.value.tombstone) continue;\n yield result.value;\n }\n })();\n };\n\n continueDiscover: Graffiti[\"continueDiscover\"] = (...args) => {\n const [cursor, session] = args;\n if (cursor.startsWith(\"discover:\")) {\n // TODO: use AJV here\n const { channels, schema, actor, continueParams } = JSON.parse(\n cursor.slice(\"discover:\".length),\n );\n if (actor && actor !== session?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n return this.discoverContinue<{}>(\n [channels, schema, session],\n continueParams,\n );\n } else {\n throw new GraffitiErrorNotFound(\"Cursor not found\");\n }\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["session"]
7
7
  }
@@ -12,7 +12,6 @@ class GraffitiLocalObjects {
12
12
  db_;
13
13
  ajv_;
14
14
  options;
15
- operationClock = 0;
16
15
  get db() {
17
16
  if (!this.db_) {
18
17
  this.db_ = (async () => {
@@ -59,6 +58,9 @@ class GraffitiLocalObjects {
59
58
  }
60
59
  return this.ajv_;
61
60
  }
61
+ async getOperationClock() {
62
+ return Number((await (await this.db).info()).update_seq);
63
+ }
62
64
  constructor(options) {
63
65
  this.options = options ?? {};
64
66
  }
@@ -118,13 +120,12 @@ class GraffitiLocalObjects {
118
120
  throw new GraffitiErrorNotFound("Object not found.");
119
121
  }
120
122
  doc.tombstone = true;
121
- doc.lastModified = this.operationClock;
123
+ doc.lastModified = await this.getOperationClock();
122
124
  try {
123
125
  await (await this.db).put(doc);
124
126
  } catch {
125
127
  throw new GraffitiErrorNotFound("Object not found.");
126
128
  }
127
- this.operationClock++;
128
129
  return;
129
130
  };
130
131
  post = async (...args) => {
@@ -137,14 +138,13 @@ class GraffitiLocalObjects {
137
138
  value,
138
139
  channels,
139
140
  allowed,
140
- lastModified: this.operationClock,
141
+ lastModified: await this.getOperationClock(),
141
142
  tombstone: false
142
143
  };
143
144
  await (await this.db).put({
144
145
  _id: url,
145
146
  ...object
146
147
  });
147
- this.operationClock++;
148
148
  return {
149
149
  ...objectPartial,
150
150
  actor,
@@ -166,7 +166,7 @@ class GraffitiLocalObjects {
166
166
  const startKeySuffix = continueParams ? continueParams.ifModifiedSince.toString().padStart(15, "0") : "";
167
167
  const endKeySuffix = "\uFFFF";
168
168
  const processedUrls = /* @__PURE__ */ new Set();
169
- const startTime = this.operationClock;
169
+ const startTime = await this.getOperationClock();
170
170
  for (const channel of discoverChannels) {
171
171
  const keyPrefix = encodeURIComponent(channel) + "/";
172
172
  const startkey = keyPrefix + startKeySuffix;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/objects.ts"],
4
- "sourcesContent": ["import type {\n Graffiti,\n GraffitiObjectBase,\n JSONSchema,\n GraffitiSession,\n GraffitiObjectStreamContinue,\n GraffitiObjectStreamContinueEntry,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorNotFound,\n GraffitiErrorSchemaMismatch,\n GraffitiErrorForbidden,\n unpackObjectUrl,\n maskGraffitiObject,\n isActorAllowedGraffitiObject,\n compileGraffitiObjectSchema,\n} from \"@graffiti-garden/api\";\nimport { randomBase64, decodeObjectUrl, encodeObjectUrl } from \"./utilities.js\";\nimport type Ajv from \"ajv\";\n\n/**\n * Constructor options for the GraffitiPoubchDB class.\n */\nexport interface GraffitiLocalOptions {\n /**\n * Options to pass to the PouchDB constructor.\n * Defaults to `{ name: \"graffitiDb\" }`.\n *\n * See the [PouchDB documentation](https://pouchdb.com/api.html#create_database)\n * for available options.\n */\n pouchDBOptions?: PouchDB.Configuration.DatabaseConfiguration;\n /**\n * Wait at least this long (in milliseconds) before continuing a stream.\n * A basic form of rate limiting. Defaults to 2 seconds.\n */\n continueBuffer?: number;\n}\n\ntype GraffitiObjectData = {\n tombstone: boolean;\n value: {};\n channels: string[];\n allowed?: string[] | null;\n lastModified: number;\n};\n\ntype ContinueDiscoverParams = {\n lastDiscovered: number;\n ifModifiedSince: number;\n};\n\n/**\n * An implementation of only the database operations of the\n * GraffitiAPI without synchronization or session management.\n */\nexport class GraffitiLocalObjects {\n protected db_: Promise<PouchDB.Database<GraffitiObjectData>> | undefined;\n protected ajv_: Promise<Ajv> | undefined;\n protected readonly options: GraffitiLocalOptions;\n protected operationClock: number = 0;\n\n get db() {\n if (!this.db_) {\n this.db_ = (async () => {\n const { default: PouchDB } = await import(\"pouchdb\");\n const pouchDbOptions = {\n name: \"graffitiDb\",\n ...this.options.pouchDBOptions,\n };\n const db = new PouchDB<GraffitiObjectData>(\n pouchDbOptions.name,\n pouchDbOptions,\n );\n await db\n //@ts-ignore\n .put({\n _id: \"_design/indexes\",\n views: {\n objectsPerChannelAndLastModified: {\n map: function (object: GraffitiObjectData) {\n const paddedLastModified = object.lastModified\n .toString()\n .padStart(15, \"0\");\n object.channels.forEach(function (channel) {\n const id =\n encodeURIComponent(channel) + \"/\" + paddedLastModified;\n //@ts-ignore\n emit(id);\n });\n }.toString(),\n },\n },\n })\n //@ts-ignore\n .catch((error) => {\n if (\n error &&\n typeof error === \"object\" &&\n \"name\" in error &&\n error.name === \"conflict\"\n ) {\n // Design document already exists\n return;\n } else {\n throw error;\n }\n });\n return db;\n })();\n }\n return this.db_;\n }\n\n protected get ajv() {\n if (!this.ajv_) {\n this.ajv_ = (async () => {\n const { default: Ajv } = await import(\"ajv\");\n return new Ajv({ strict: false });\n })();\n }\n return this.ajv_;\n }\n\n constructor(options?: GraffitiLocalOptions) {\n this.options = options ?? {};\n }\n\n get: Graffiti[\"get\"] = async (...args) => {\n const [urlObject, schema, session] = args;\n const url = unpackObjectUrl(urlObject);\n\n let doc: GraffitiObjectData;\n try {\n doc = await (await this.db).get(url);\n } catch (error) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n const { actor } = decodeObjectUrl(url);\n const { value, channels, allowed } = doc;\n const object: GraffitiObjectBase = {\n value,\n channels,\n allowed,\n url,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n // Mask out the allowed list and channels\n // if the user is not the owner\n maskGraffitiObject(object, [], session);\n\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n if (!validate(object)) {\n throw new GraffitiErrorSchemaMismatch();\n }\n return object;\n };\n\n delete: Graffiti[\"delete\"] = async (...args) => {\n const [urlObject, session] = args;\n\n const url = unpackObjectUrl(urlObject);\n const { actor } = decodeObjectUrl(url);\n if (actor !== session.actor) {\n throw new GraffitiErrorForbidden(\n \"You cannot delete an object that you did not create.\",\n );\n }\n\n let doc: GraffitiObjectData & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta;\n try {\n doc = await (await this.db).get(url);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n // Set the tombstone and update lastModified\n doc.tombstone = true;\n doc.lastModified = this.operationClock;\n try {\n await (await this.db).put(doc);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n this.operationClock++;\n\n return;\n };\n\n post: Graffiti[\"post\"] = async (...args) => {\n const [objectPartial, session] = args;\n\n const actor = session.actor;\n const id = randomBase64();\n const url = encodeObjectUrl(actor, id);\n\n const { value, channels, allowed } = objectPartial;\n const object: GraffitiObjectData = {\n value,\n channels,\n allowed,\n lastModified: this.operationClock,\n tombstone: false,\n };\n\n await (\n await this.db\n ).put({\n _id: url,\n ...object,\n });\n this.operationClock++;\n\n return {\n ...objectPartial,\n actor,\n url,\n };\n };\n\n protected async *discoverMeta<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams?: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): AsyncGenerator<\n GraffitiObjectStreamContinueEntry<Schema>,\n ContinueDiscoverParams\n > {\n // If we are continuing a discover, make sure to wait at\n // least 2 seconds since the last poll to start a new one.\n if (continueParams) {\n const continueBuffer = this.options.continueBuffer ?? 2000;\n const timeElapsedSinceLastDiscover =\n Date.now() - continueParams.lastDiscovered;\n if (timeElapsedSinceLastDiscover < continueBuffer) {\n // Continue was called too soon,\n // wait a bit before continuing\n await new Promise((resolve) =>\n setTimeout(resolve, continueBuffer - timeElapsedSinceLastDiscover),\n );\n }\n }\n\n const [discoverChannels, schema, session] = args;\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n const startKeySuffix = continueParams\n ? continueParams.ifModifiedSince.toString().padStart(15, \"0\")\n : \"\";\n const endKeySuffix = \"\\uffff\";\n\n const processedUrls = new Set<string>();\n\n const startTime = this.operationClock;\n\n for (const channel of discoverChannels) {\n const keyPrefix = encodeURIComponent(channel) + \"/\";\n const startkey = keyPrefix + startKeySuffix;\n const endkey = keyPrefix + endKeySuffix;\n\n const result = await (\n await this.db\n ).query<GraffitiObjectData>(\"indexes/objectsPerChannelAndLastModified\", {\n startkey,\n endkey,\n include_docs: true,\n });\n\n for (const row of result.rows) {\n const doc = row.doc;\n if (!doc) continue;\n\n const url = doc._id;\n\n if (processedUrls.has(url)) continue;\n processedUrls.add(url);\n\n // If this is not a continuation, skip tombstones\n if (!continueParams && doc.tombstone) continue;\n\n const { tombstone, value, channels, allowed } = doc;\n const { actor } = decodeObjectUrl(url);\n\n const object: GraffitiObjectBase = {\n url,\n value,\n allowed,\n channels,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) continue;\n\n maskGraffitiObject(object, discoverChannels, session);\n\n if (!validate(object)) continue;\n\n yield tombstone\n ? {\n tombstone: true,\n object: { url },\n }\n : { object };\n }\n }\n\n return {\n lastDiscovered: Date.now(),\n ifModifiedSince: startTime,\n };\n }\n\n protected discoverCursor(\n args: Parameters<typeof Graffiti.prototype.discover<{}>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): string {\n const [channels, schema, session] = args;\n return (\n \"discover:\" +\n JSON.stringify({\n channels,\n schema,\n continueParams,\n actor: session?.actor,\n })\n );\n }\n\n protected async *discoverContinue<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<Schema> {\n if (session?.actor !== args[2]?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n const iterator = this.discoverMeta<Schema>(args, continueParams);\n\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this.discoverContinue<Schema>(args, result.value, session),\n cursor: this.discoverCursor(args, result.value),\n };\n }\n yield result.value;\n }\n }\n\n discover: Graffiti[\"discover\"] = (...args) => {\n const [channels, schema, session] = args;\n const iterator = this.discoverMeta<(typeof args)[1]>([\n channels,\n schema,\n session,\n ]);\n\n const this_ = this;\n return (async function* () {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this_.discoverContinue<(typeof args)[1]>(\n args,\n result.value,\n session,\n ),\n cursor: this_.discoverCursor(args, result.value),\n };\n }\n // Make sure to filter out tombstones\n if (result.value.tombstone) continue;\n yield result.value;\n }\n })();\n };\n\n continueDiscover: Graffiti[\"continueDiscover\"] = (...args) => {\n const [cursor, session] = args;\n if (cursor.startsWith(\"discover:\")) {\n // TODO: use AJV here\n const { channels, schema, actor, continueParams } = JSON.parse(\n cursor.slice(\"discover:\".length),\n );\n if (actor && actor !== session?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n return this.discoverContinue<{}>(\n [channels, schema, session],\n continueParams,\n );\n } else {\n throw new GraffitiErrorNotFound(\"Cursor not found\");\n }\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type {\n Graffiti,\n GraffitiObjectBase,\n JSONSchema,\n GraffitiSession,\n GraffitiObjectStreamContinue,\n GraffitiObjectStreamContinueEntry,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorNotFound,\n GraffitiErrorSchemaMismatch,\n GraffitiErrorForbidden,\n unpackObjectUrl,\n maskGraffitiObject,\n isActorAllowedGraffitiObject,\n compileGraffitiObjectSchema,\n} from \"@graffiti-garden/api\";\nimport { randomBase64, decodeObjectUrl, encodeObjectUrl } from \"./utilities.js\";\nimport type Ajv from \"ajv\";\n\n/**\n * Constructor options for the GraffitiPoubchDB class.\n */\nexport interface GraffitiLocalOptions {\n /**\n * Options to pass to the PouchDB constructor.\n * Defaults to `{ name: \"graffitiDb\" }`.\n *\n * See the [PouchDB documentation](https://pouchdb.com/api.html#create_database)\n * for available options.\n */\n pouchDBOptions?: PouchDB.Configuration.DatabaseConfiguration;\n /**\n * Wait at least this long (in milliseconds) before continuing a stream.\n * A basic form of rate limiting. Defaults to 2 seconds.\n */\n continueBuffer?: number;\n}\n\ntype GraffitiObjectData = {\n tombstone: boolean;\n value: {};\n channels: string[];\n allowed?: string[] | null;\n lastModified: number;\n};\n\ntype ContinueDiscoverParams = {\n lastDiscovered: number;\n ifModifiedSince: number;\n};\n\n/**\n * An implementation of only the database operations of the\n * GraffitiAPI without synchronization or session management.\n */\nexport class GraffitiLocalObjects {\n protected db_: Promise<PouchDB.Database<GraffitiObjectData>> | undefined;\n protected ajv_: Promise<Ajv> | undefined;\n protected readonly options: GraffitiLocalOptions;\n\n get db() {\n if (!this.db_) {\n this.db_ = (async () => {\n const { default: PouchDB } = await import(\"pouchdb\");\n const pouchDbOptions = {\n name: \"graffitiDb\",\n ...this.options.pouchDBOptions,\n };\n const db = new PouchDB<GraffitiObjectData>(\n pouchDbOptions.name,\n pouchDbOptions,\n );\n await db\n //@ts-ignore\n .put({\n _id: \"_design/indexes\",\n views: {\n objectsPerChannelAndLastModified: {\n map: function (object: GraffitiObjectData) {\n const paddedLastModified = object.lastModified\n .toString()\n .padStart(15, \"0\");\n object.channels.forEach(function (channel) {\n const id =\n encodeURIComponent(channel) + \"/\" + paddedLastModified;\n //@ts-ignore\n emit(id);\n });\n }.toString(),\n },\n },\n })\n //@ts-ignore\n .catch((error) => {\n if (\n error &&\n typeof error === \"object\" &&\n \"name\" in error &&\n error.name === \"conflict\"\n ) {\n // Design document already exists\n return;\n } else {\n throw error;\n }\n });\n return db;\n })();\n }\n return this.db_;\n }\n\n protected get ajv() {\n if (!this.ajv_) {\n this.ajv_ = (async () => {\n const { default: Ajv } = await import(\"ajv\");\n return new Ajv({ strict: false });\n })();\n }\n return this.ajv_;\n }\n\n protected async getOperationClock() {\n return Number((await (await this.db).info()).update_seq);\n }\n\n constructor(options?: GraffitiLocalOptions) {\n this.options = options ?? {};\n }\n\n get: Graffiti[\"get\"] = async (...args) => {\n const [urlObject, schema, session] = args;\n const url = unpackObjectUrl(urlObject);\n\n let doc: GraffitiObjectData;\n try {\n doc = await (await this.db).get(url);\n } catch (error) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n const { actor } = decodeObjectUrl(url);\n const { value, channels, allowed } = doc;\n const object: GraffitiObjectBase = {\n value,\n channels,\n allowed,\n url,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n // Mask out the allowed list and channels\n // if the user is not the owner\n maskGraffitiObject(object, [], session);\n\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n if (!validate(object)) {\n throw new GraffitiErrorSchemaMismatch();\n }\n return object;\n };\n\n delete: Graffiti[\"delete\"] = async (...args) => {\n const [urlObject, session] = args;\n\n const url = unpackObjectUrl(urlObject);\n const { actor } = decodeObjectUrl(url);\n if (actor !== session.actor) {\n throw new GraffitiErrorForbidden(\n \"You cannot delete an object that you did not create.\",\n );\n }\n\n let doc: GraffitiObjectData & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta;\n try {\n doc = await (await this.db).get(url);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n // Set the tombstone and update lastModified\n doc.tombstone = true;\n doc.lastModified = await this.getOperationClock();\n try {\n await (await this.db).put(doc);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n return;\n };\n\n post: Graffiti[\"post\"] = async (...args) => {\n const [objectPartial, session] = args;\n\n const actor = session.actor;\n const id = randomBase64();\n const url = encodeObjectUrl(actor, id);\n\n const { value, channels, allowed } = objectPartial;\n const object: GraffitiObjectData = {\n value,\n channels,\n allowed,\n lastModified: await this.getOperationClock(),\n tombstone: false,\n };\n\n await (\n await this.db\n ).put({\n _id: url,\n ...object,\n });\n\n return {\n ...objectPartial,\n actor,\n url,\n };\n };\n\n protected async *discoverMeta<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams?: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): AsyncGenerator<\n GraffitiObjectStreamContinueEntry<Schema>,\n ContinueDiscoverParams\n > {\n // If we are continuing a discover, make sure to wait at\n // least 2 seconds since the last poll to start a new one.\n if (continueParams) {\n const continueBuffer = this.options.continueBuffer ?? 2000;\n const timeElapsedSinceLastDiscover =\n Date.now() - continueParams.lastDiscovered;\n if (timeElapsedSinceLastDiscover < continueBuffer) {\n // Continue was called too soon,\n // wait a bit before continuing\n await new Promise((resolve) =>\n setTimeout(resolve, continueBuffer - timeElapsedSinceLastDiscover),\n );\n }\n }\n\n const [discoverChannels, schema, session] = args;\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n const startKeySuffix = continueParams\n ? continueParams.ifModifiedSince.toString().padStart(15, \"0\")\n : \"\";\n const endKeySuffix = \"\\uffff\";\n\n const processedUrls = new Set<string>();\n\n const startTime = await this.getOperationClock();\n\n for (const channel of discoverChannels) {\n const keyPrefix = encodeURIComponent(channel) + \"/\";\n const startkey = keyPrefix + startKeySuffix;\n const endkey = keyPrefix + endKeySuffix;\n\n const result = await (\n await this.db\n ).query<GraffitiObjectData>(\"indexes/objectsPerChannelAndLastModified\", {\n startkey,\n endkey,\n include_docs: true,\n });\n\n for (const row of result.rows) {\n const doc = row.doc;\n if (!doc) continue;\n\n const url = doc._id;\n\n if (processedUrls.has(url)) continue;\n processedUrls.add(url);\n\n // If this is not a continuation, skip tombstones\n if (!continueParams && doc.tombstone) continue;\n\n const { tombstone, value, channels, allowed } = doc;\n const { actor } = decodeObjectUrl(url);\n\n const object: GraffitiObjectBase = {\n url,\n value,\n allowed,\n channels,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) continue;\n\n maskGraffitiObject(object, discoverChannels, session);\n\n if (!validate(object)) continue;\n\n yield tombstone\n ? {\n tombstone: true,\n object: { url },\n }\n : { object };\n }\n }\n\n return {\n lastDiscovered: Date.now(),\n ifModifiedSince: startTime,\n };\n }\n\n protected discoverCursor(\n args: Parameters<typeof Graffiti.prototype.discover<{}>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): string {\n const [channels, schema, session] = args;\n return (\n \"discover:\" +\n JSON.stringify({\n channels,\n schema,\n continueParams,\n actor: session?.actor,\n })\n );\n }\n\n protected async *discoverContinue<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<Schema> {\n if (session?.actor !== args[2]?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n const iterator = this.discoverMeta<Schema>(args, continueParams);\n\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this.discoverContinue<Schema>(args, result.value, session),\n cursor: this.discoverCursor(args, result.value),\n };\n }\n yield result.value;\n }\n }\n\n discover: Graffiti[\"discover\"] = (...args) => {\n const [channels, schema, session] = args;\n const iterator = this.discoverMeta<(typeof args)[1]>([\n channels,\n schema,\n session,\n ]);\n\n const this_ = this;\n return (async function* () {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this_.discoverContinue<(typeof args)[1]>(\n args,\n result.value,\n session,\n ),\n cursor: this_.discoverCursor(args, result.value),\n };\n }\n // Make sure to filter out tombstones\n if (result.value.tombstone) continue;\n yield result.value;\n }\n })();\n };\n\n continueDiscover: Graffiti[\"continueDiscover\"] = (...args) => {\n const [cursor, session] = args;\n if (cursor.startsWith(\"discover:\")) {\n // TODO: use AJV here\n const { channels, schema, actor, continueParams } = JSON.parse(\n cursor.slice(\"discover:\".length),\n );\n if (actor && actor !== session?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n return this.discoverContinue<{}>(\n [channels, schema, session],\n continueParams,\n );\n } else {\n throw new GraffitiErrorNotFound(\"Cursor not found\");\n }\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["session"]
7
7
  }
package/dist/objects.d.ts CHANGED
@@ -37,9 +37,9 @@ export declare class GraffitiLocalObjects {
37
37
  protected db_: Promise<PouchDB.Database<GraffitiObjectData>> | undefined;
38
38
  protected ajv_: Promise<Ajv> | undefined;
39
39
  protected readonly options: GraffitiLocalOptions;
40
- protected operationClock: number;
41
40
  get db(): Promise<PouchDB.Database<GraffitiObjectData>>;
42
41
  protected get ajv(): Promise<Ajv>;
42
+ protected getOperationClock(): Promise<number>;
43
43
  constructor(options?: GraffitiLocalOptions);
44
44
  get: Graffiti["get"];
45
45
  delete: Graffiti["delete"];
@@ -1 +1 @@
1
- {"version":3,"file":"objects.d.ts","sourceRoot":"","sources":["../src/objects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EAER,UAAU,EACV,eAAe,EACf,4BAA4B,EAC5B,iCAAiC,EAClC,MAAM,sBAAsB,CAAC;AAW9B,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAE3B;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAC;IAC7D;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,KAAK,kBAAkB,GAAG;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,EAAE,CAAC;IACV,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;GAGG;AACH,qBAAa,oBAAoB;IAC/B,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,GAAG,SAAS,CAAC;IACzE,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IACzC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IACjD,SAAS,CAAC,cAAc,EAAE,MAAM,CAAK;IAErC,IAAI,EAAE,kDAkDL;IAED,SAAS,KAAK,GAAG,iBAQhB;gBAEW,OAAO,CAAC,EAAE,oBAAoB;IAI1C,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,CA4ClB;IAEF,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAiCxB;IAEF,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CA6BpB;cAEe,YAAY,CAAC,MAAM,SAAS,UAAU,EACrD,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAC5D,cAAc,CAAC,EAAE;QACf,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,GACA,cAAc,CACf,iCAAiC,CAAC,MAAM,CAAC,EACzC,sBAAsB,CACvB;IAoFD,SAAS,CAAC,cAAc,CACtB,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,EACxD,cAAc,EAAE;QACd,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,GACA,MAAM;cAaQ,gBAAgB,CAAC,MAAM,SAAS,UAAU,EACzD,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAC5D,cAAc,EAAE;QACd,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,EACD,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,GAC/B,4BAA4B,CAAC,MAAM,CAAC;IAqBvC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CA4B5B;IAEF,gBAAgB,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAmB5C;CACH"}
1
+ {"version":3,"file":"objects.d.ts","sourceRoot":"","sources":["../src/objects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EAER,UAAU,EACV,eAAe,EACf,4BAA4B,EAC5B,iCAAiC,EAClC,MAAM,sBAAsB,CAAC;AAW9B,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAE3B;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAC;IAC7D;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,KAAK,kBAAkB,GAAG;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,EAAE,CAAC;IACV,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;GAGG;AACH,qBAAa,oBAAoB;IAC/B,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,GAAG,SAAS,CAAC;IACzE,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IACzC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IAEjD,IAAI,EAAE,kDAkDL;IAED,SAAS,KAAK,GAAG,iBAQhB;cAEe,iBAAiB;gBAIrB,OAAO,CAAC,EAAE,oBAAoB;IAI1C,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,CA4ClB;IAEF,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAgCxB;IAEF,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CA4BpB;cAEe,YAAY,CAAC,MAAM,SAAS,UAAU,EACrD,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAC5D,cAAc,CAAC,EAAE;QACf,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,GACA,cAAc,CACf,iCAAiC,CAAC,MAAM,CAAC,EACzC,sBAAsB,CACvB;IAoFD,SAAS,CAAC,cAAc,CACtB,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,EACxD,cAAc,EAAE;QACd,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,GACA,MAAM;cAaQ,gBAAgB,CAAC,MAAM,SAAS,UAAU,EACzD,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAC5D,cAAc,EAAE;QACd,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,EACD,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,GAC/B,4BAA4B,CAAC,MAAM,CAAC;IAqBvC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CA4B5B;IAEF,gBAAgB,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAmB5C;CACH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graffiti-garden/implementation-local",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "A local implementation of the Graffiti API using PouchDB",
5
5
  "types": "./dist/index.d.ts",
6
6
  "module": "./dist/esm/index.js",
@@ -94,7 +94,7 @@
94
94
  "vitest": "^4.0.16"
95
95
  },
96
96
  "dependencies": {
97
- "@graffiti-garden/api": "^1.0.1",
97
+ "@graffiti-garden/api": "^1.0.2",
98
98
  "ajv": "^8.17.1",
99
99
  "negotiator": "^1.0.0",
100
100
  "pouchdb": "^9.0.0"
package/src/objects.ts CHANGED
@@ -58,7 +58,6 @@ export class GraffitiLocalObjects {
58
58
  protected db_: Promise<PouchDB.Database<GraffitiObjectData>> | undefined;
59
59
  protected ajv_: Promise<Ajv> | undefined;
60
60
  protected readonly options: GraffitiLocalOptions;
61
- protected operationClock: number = 0;
62
61
 
63
62
  get db() {
64
63
  if (!this.db_) {
@@ -122,6 +121,10 @@ export class GraffitiLocalObjects {
122
121
  return this.ajv_;
123
122
  }
124
123
 
124
+ protected async getOperationClock() {
125
+ return Number((await (await this.db).info()).update_seq);
126
+ }
127
+
125
128
  constructor(options?: GraffitiLocalOptions) {
126
129
  this.options = options ?? {};
127
130
  }
@@ -196,13 +199,12 @@ export class GraffitiLocalObjects {
196
199
 
197
200
  // Set the tombstone and update lastModified
198
201
  doc.tombstone = true;
199
- doc.lastModified = this.operationClock;
202
+ doc.lastModified = await this.getOperationClock();
200
203
  try {
201
204
  await (await this.db).put(doc);
202
205
  } catch {
203
206
  throw new GraffitiErrorNotFound("Object not found.");
204
207
  }
205
- this.operationClock++;
206
208
 
207
209
  return;
208
210
  };
@@ -219,7 +221,7 @@ export class GraffitiLocalObjects {
219
221
  value,
220
222
  channels,
221
223
  allowed,
222
- lastModified: this.operationClock,
224
+ lastModified: await this.getOperationClock(),
223
225
  tombstone: false,
224
226
  };
225
227
 
@@ -229,7 +231,6 @@ export class GraffitiLocalObjects {
229
231
  _id: url,
230
232
  ...object,
231
233
  });
232
- this.operationClock++;
233
234
 
234
235
  return {
235
236
  ...objectPartial,
@@ -272,7 +273,7 @@ export class GraffitiLocalObjects {
272
273
 
273
274
  const processedUrls = new Set<string>();
274
275
 
275
- const startTime = this.operationClock;
276
+ const startTime = await this.getOperationClock();
276
277
 
277
278
  for (const channel of discoverChannels) {
278
279
  const keyPrefix = encodeURIComponent(channel) + "/";