webdaemon 14.3.0 → 15.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- async function J(r,t=0){let e=new TextEncoder().encode(r);if(!crypto.subtle)throw"Requires secure origin (localhost or https:)";let n=await crypto.subtle.digest("SHA-1",e),s=new Uint8Array(n),o=String.fromCodePoint(...s),a=btoa(o).replaceAll("+","-").replaceAll("/","_").replaceAll("=","");return t?a.substring(0,t):a}async function it(r,t=0){let e=new TextEncoder().encode(r);if(!crypto.subtle)throw"Requires secure origin (localhost or https:)";let n=await crypto.subtle.digest("SHA-1",e),o=Array.from(new Uint8Array(n)).map(i=>i.toString(16).padStart(2,"0")).join("");return t?o.substring(0,t):o}async function P(r){return await J(String(r),8)}var B={name:"RSASSA-PKCS1-v1_5",modulusLength:2048,publicExponent:new Uint8Array([1,0,1]),hash:"SHA-256"},A=class{#t;#e;constructor(t=B){if(this.#t=t,!crypto.subtle)throw"Crypto.subtle requires secure environment"}async generate(){this.#e=await crypto.subtle.generateKey(this.#t,!0,["sign","verify"])}async publicJwk(){if(!this.#e)throw"Must generate keypair";let t=this.#e.publicKey;return await crypto.subtle.exportKey("jwk",t)}async privateJwk(){if(!this.#e)throw"Must generate keypair";let t=this.#e.privateKey;return await crypto.subtle.exportKey("jwk",t)}async publicPem(){if(!this.#e)throw"Must generate keypair";let t=this.#e.publicKey,e=await crypto.subtle.exportKey("spki",t);return`-----BEGIN PUBLIC KEY-----
1
+ async function J(r,t=0){let e=new TextEncoder().encode(r);if(!crypto.subtle)throw"Requires secure origin (localhost or https:)";let n=await crypto.subtle.digest("SHA-1",e),s=new Uint8Array(n),o=String.fromCodePoint(...s),a=btoa(o).replaceAll("+","-").replaceAll("/","_").replaceAll("=","");return t?a.substring(0,t):a}async function at(r,t=0){let e=new TextEncoder().encode(r);if(!crypto.subtle)throw"Requires secure origin (localhost or https:)";let n=await crypto.subtle.digest("SHA-1",e),o=Array.from(new Uint8Array(n)).map(i=>i.toString(16).padStart(2,"0")).join("");return t?o.substring(0,t):o}async function P(r){return await J(String(r),8)}var D={name:"RSASSA-PKCS1-v1_5",modulusLength:2048,publicExponent:new Uint8Array([1,0,1]),hash:"SHA-256"},A=class{#t;#e;constructor(t=D){if(this.#t=t,!crypto.subtle)throw"Crypto.subtle requires secure environment"}async generate(){this.#e=await crypto.subtle.generateKey(this.#t,!0,["sign","verify"])}async publicJwk(){if(!this.#e)throw"Must generate keypair";let t=this.#e.publicKey;return await crypto.subtle.exportKey("jwk",t)}async privateJwk(){if(!this.#e)throw"Must generate keypair";let t=this.#e.privateKey;return await crypto.subtle.exportKey("jwk",t)}async publicPem(){if(!this.#e)throw"Must generate keypair";let t=this.#e.publicKey,e=await crypto.subtle.exportKey("spki",t);return`-----BEGIN PUBLIC KEY-----
2
2
  ${btoa(String.fromCharCode(...new Uint8Array(e))).match(/.{1,64}/g)?.join(`
3
3
  `)??""}
4
4
  -----END PUBLIC KEY-----`}async privatePem(){if(!this.#e)throw"Must generate keypair";let t=this.#e.privateKey,e=await crypto.subtle.exportKey("pkcs8",t);return`-----BEGIN PRIVATE KEY-----
5
5
  ${btoa(String.fromCharCode(...new Uint8Array(e))).match(/.{1,64}/g)?.join(`
6
6
  `)??""}
7
- -----END PRIVATE KEY-----`}};var f={name:"RSASSA-PKCS1-v1_5",hash:"SHA-256"},R=/https?:\/\/[^\/]+$/,D=1e3*30,h=class r{static Source={PARTY_CONTROL:"party:control"};static Counterparty={PUBLIC:"public"};static Capability={OPEN:"open",CLOSE:"close",GRANT:"grant",REVOKE:"revoke",OFFER:"offer",RETRACT:"retract",DELETE:"delete",CATALOG:"catalog",ALIAS:"alias",UNALIAS:"unalias",GET_ITEM:"getitem",SET_ITEM:"setitem",ALERT:"alert",LOG:"log",READ:"read",WRITE:"write"};static DEFAULT_EXPIRY_MILLIS=1e3*60*60*24;static TOKEN_HEADER="x-tabserver-token";static SCOPE_SEPARATOR=/[\s,;\|]+/;#t=null;#e=null;#n;#s;#o;#r;constructor(t){let e=null;if(typeof t=="object")e=t;else if(typeof t=="string")try{e=JSON.parse(atob(t))}catch{throw"Invalid token format"}else throw`Cannot construct token from ${typeof t}`;this.#e=e.sig,delete e.sig,this.#t=e;let{iss:n,aud:s,sub:o,src:i,scope:a,iat:p,exp:c}=e;if(!n||!s||!o||!i||!a||!p||!c)throw"Token must include iss, aud, sub, src, scope, iat and exp";if(!s.match(R)||!o.match(R))throw"The aud and sub attributes must be origins";if(this.#n=new URL(s),this.#s=new URL(o),this.#o=new URL(i),this.#o.search||this.#o.hash)throw"The src attribute must have no query or fragment component.";this.#t=e}async signWith(t){if(this.#t==null)throw"No payload to sign";let e=await t,n=await crypto.subtle.importKey("jwk",e,f,!0,["sign"]),s=JSON.stringify(this.#t),o=new TextEncoder().encode(s).buffer,i=new Uint8Array(o),a=await crypto.subtle.sign(f,n,i),p=r.bytesToBase64(new Uint8Array(a));return this.#e=p,p}async verifySignatory(){if(await this.fetchSignatory(),!this.#r)throw"verifySignatory: no signatory";await this.checkSignature(Promise.resolve(this.#r.jwk))}async fetchSignatory(){let t=this.getIssuer();try{let n=await(await fetch(t,{headers:{"content-type":"application/json"}})).json();if("error"in n)throw n.error;this.#r=n.ok}catch{throw`Failed to read signatory at ${t}`}}async checkSignature(t){if(!this.#t||!this.#e)throw"checkSignature: no payload or signatureBase64";let e=await t;if(!e||"error"in e)throw`Missing or invalid public JWK at issuer ${this.getIssuer()}`;let n=await crypto.subtle.importKey("jwk",e,f,!0,["verify"]),s=JSON.stringify(this.#t),o=new TextEncoder().encode(s),i=r.base64ToBytes(this.#e);if(!await crypto.subtle.verify(f,n,i,o))throw`Signature rejected by issuer ${this.getIssuer()}`}getSignedPayload(){if(!this.#e||!this.#t)throw"getSignedPayload: no signatureBase64 or payload";return{...this.#t,sig:this.#e}}getPayload(){if(!this.#t)throw"getPayload: no payload";return this.#t}getAud(){return this.#n.origin}getParty(){return this.#n.host}getSub(){return this.#s.origin}getSrc(){return String(this.#o)}getCounterparty(){return this.#s.host}getSignatorySrc(){if(!this.#r)throw"getSignatorySrc: no signatory";return this.#r?.src??String(this.#o)}getSourceUrl(){return this.#o}getIssuer(){return new URL(this.getPayload().iss)}getSources(){if(!this.#t)throw"getSources: no payload";let t=[];for(let e in this.#t.scope)t.push(e);return t}getCapabilities(t){if(!this.#t)throw"getCapabilities: no payload";let e=this.#t.scope;if(!(t in e))throw`Source '${t}' not in scope`;return e[t].split(r.SCOPE_SEPARATOR)}hasCapability(t,e){return this.getCapabilities(t).includes(e)}checkPeriod(){if(!this.#t)throw"checkPeriod: no payload";let{iat:t,exp:e}=this.#t,n=Date.now();if(n<t-D)throw"Token is not yet valid";if(n>e)throw"Token has expired"}asSignedBase64(){return btoa(JSON.stringify(this.getSignedPayload()))}static bytesToBase64(t){let e=Array.from(t,n=>String.fromCodePoint(n)).join("");return btoa(e)}static base64ToBytes(t){let e=atob(t);return Uint8Array.from(e,n=>n.codePointAt(0)??0)}static from(t){let e=t.headers.get(r.TOKEN_HEADER);return e?new r(e):null}static toSearchString(t){let e=new URLSearchParams;for(let n in t){let o=t[n].asSignedBase64();e.append(n,o)}return e.toString()}};var H=/^[\w\-.:]+(?:\/[\w\-.:]+)*$/,M=/^[\w\-.:%_]+(?:\/[\w\-.:%_]+)*$/,u=class{#t;constructor(t){this.#t=t}getItem(t){return W(this.#t,t)}getItemsLike(t){return G(this.#t,t)}setItem(t,e){return V(this.#t,t,e)}removeItem(t){return X(this.#t,t)}};function T(r){if(r.match(H)==null)throw`DaemonStorage: Invalid key: '${r}`}function F(r){if(r.match(M)==null)throw`DaemonStorage: Invalid like key: ${r}`}function _(r){try{return JSON.stringify(r)}catch{throw"DaemonStorage: Invalid value"}}async function V(r,t,e){T(t);let n=_(e),s=`${r.getAud()}/storage/${t}`;return(await fetch(s,{method:"PUT",headers:{"X-Tabserver-Token":r.asSignedBase64(),"Content-Type":"application/json"},body:n})).json()}async function W(r,t){T(t);let e=`${r.getAud()}/storage/${t}`;return(await fetch(e,{method:"GET",headers:{"X-Tabserver-Token":r.asSignedBase64()}})).json()}async function G(r,t){F(t);let e=new URL(`${r.getAud()}/storage`);return e.searchParams.append("like",t),await(await fetch(e,{method:"GET",headers:{"X-Tabserver-Token":r.asSignedBase64()}})).json()}async function X(r,t){T(t);let e=`${r.getAud()}/storage/${t}`;return(await fetch(e,{method:"DELETE",headers:{"X-Tabserver-Token":r.asSignedBase64()}})).json()}var x={Ash:"\xE6",Hash:"#"},Y={Ash:encodeURIComponent("\xE6")},y=class{params=new URLSearchParams;extractHash(t){t=t.startsWith(x.Hash)?t.substring(1):t;let e=t.indexOf(Y.Ash);if(e===-1)return t;let n=new URLSearchParams(t.slice(e));return this.extract(n),t.slice(0,e)}extractQuery(t){let e=new URLSearchParams(t);return this.extract(e),e.toString()}extract(t){let e=Array.from(t.keys());for(let n of e)n.startsWith(x.Ash)&&(this.params.set(n.substring(1),t.get(n)??""),t.delete(n))}getParams(){return this.params}};var b="party",C="prefix",d=class r{static#t;#e;#n;#s;#o;static async getInstance(t=void 0){if(!t&&!r.#t)throw"Must provide app name";if(t&&r.#t&&t!==r.#t.#e)throw`Cannot change app name from ${r.#t.#e} to ${t}`;if(t&&!r.#t&&(r.#t=new r(t),await r.#t.init()),!r.#t)throw"No instance of BrowserApp";return r.#t}constructor(t){this.#e=t,this.#n=`${t}Party`,this.#s=`${t}Tokens`,this.#o=`${t}AppUrl`}async init(){let t=new URL(globalThis.location.href),e=new y,n=e.extractHash(t.hash),s=e.extractQuery(t.search),o=e.getParams();if(o.has(b)){sessionStorage[this.#s]=o.toString();let i=this.getToken();await i.verifySignatory(),sessionStorage[this.#n]=i.getParty();let a=i.getSourceUrl();if(a.origin!==t.origin||a.pathname!==t.pathname)throw`Party token is for different app: ${a}`}return t.hash=n,t.search=s,sessionStorage[this.#o]=t.toString(),globalThis.history.replaceState(null,"",t.toString()),this}isOrphan(){return sessionStorage[this.#n]===void 0||sessionStorage[this.#s]===void 0}getAppName(){return this.#e}getAppUrl(){return sessionStorage[this.#o]}getParty(){return sessionStorage[this.#n]}getPartyOrigin(){return`${globalThis.location.protocol}//${this.getParty()}`}getDefaultPrefix(t=null){t||(t=this.getAppUrl());let e=new URL(t);return e.hash="",P(e)}getPrefix(){return this.hasParam(C)?Promise.resolve(this.getParam(C)??""):this.getDefaultPrefix()}async getAgentUrl(t){let e=this.getPartyOrigin(),n=await this.getPrefix();return`${e}/tab/${n}/${t}`}getTokenBase64(t=b){let e=sessionStorage[this.#s],s=new URLSearchParams(e).get(t);if(!s)throw`No token for ${t}, check 'audience' in YML`;return s}getToken(t=b){let e=this.getTokenBase64(t);return new h(e)}getParam(t){let e=new URL(globalThis.location.href).searchParams;for(let[s,o]of e)if(t.toLowerCase()==s.toLowerCase())return o;return document.querySelector("link[rel=webdaemon]")?.getAttribute(t)??null}hasParam(t){return new URL(globalThis.location.href).searchParams.has(t)?!0:document.querySelector("link[rel=webdaemon]")?.hasAttribute(t)??!1}async getLaunchAlert(){let t=this.getPartyOrigin(),e=new URL(`${t}/catalog`);e.searchParams.append("alert","");let s=await(await fetch(e,{headers:{"X-Tabserver-Token":this.getTokenBase64(),Accept:"application/json"}})).json();if("error"in s)throw s.error;return s.ok.alert.find(i=>i.type=="LAUNCH"&&i.sourceUrl==new URL(this.getAppUrl()))||null}async resolveLaunchAlert(){let t=this.getPartyOrigin(),e=new URL(`${t}/resolve`),s=await(await fetch(e,{method:"POST",headers:{"X-Tabserver-Token":this.getTokenBase64(),"Content-Type":"application/json"},body:JSON.stringify({type:"LAUNCH",source:this.getAppUrl()})})).json();if("error"in s)throw s.error}async rejectLaunchAlert(){let t=this.getPartyOrigin(),e=new URL(`${t}/reject`),s=await(await fetch(e,{method:"POST",headers:{"X-Tabserver-Token":this.getTokenBase64(),"Content-Type":"application/json"},body:JSON.stringify({type:"LAUNCH",source:this.getAppUrl()})})).json();if("error"in s)throw s.error}getStorage(){return new u(this.getToken())}};async function mt(){let r=await d.getInstance(),t=r.getPartyOrigin(),e=new URL(`${t}/catalog`);e.searchParams.append("alert","");let s=await(await fetch(e,{headers:{"X-Tabserver-Token":r.getTokenBase64(),Accept:"application/json"}})).json();if("error"in s)throw s.error;return s.ok.alert.find(i=>i.type=="LAUNCH"&&i.sourceUrl==new URL(r.getAppUrl()))||null}var E=class{#t;#e;#n;#s;#o;#r;#i;async initWithKey(t,e,n,s){this.#e=e.startsWith("https:")?"https:":"http:",this.#n=t,this.#o=e,this.#t=n,this.#s=s,await this.#a()}async checkActivate(){if(!this.#r)throw"checkActivate: No token";let t=new URL(`${this.#r.getAud()}/activate`),e=this.#r.asSignedBase64(),n={},s;try{s=await fetch(t,{method:"POST",headers:{"x-tabserver-token":e,"content-type":"application/json"},body:JSON.stringify(n)});let{ok:o,error:i}=await s.json();if(i)throw i;return!!o.isNew}catch(o){throw console.error(o),`Cannot activate ${this.#r.getParty()}`}}async makeOffer(t,e=null){let{type:n="TRANSIENT",ttl:s=300,expiry:o=30,payload:i}=e??{};if(!this.#r)throw"makeOffer: No token";let a=new URL(`${this.#r.getAud()}/offer`),p=this.#r.asSignedBase64(),c={role:"party",type:n,ttl:s,description:`Offered by ${this.#r.getCounterparty()}`,payload:i,expiry:new Date(Date.now()+1e3*o),flow:t},l=await(await fetch(a,{method:"POST",headers:{"x-tabserver-token":p,"content-type":"application/json"},body:JSON.stringify(c)})).json();if("error"in l)throw l.error;let{claimCode:m,_checkState:S}=l.ok;return this.#i=m,l.ok}async checkState(){if(!this.#r||!this.#i)throw"checkState: No token or claimCode";let t=new URL(`${this.#r.getAud()}/offer/check`);t.searchParams.append("claimCode",this.#i);let e=this.#r.asSignedBase64(),s=await(await fetch(t,{headers:{"x-tabserver-token":e}})).json();if("error"in s)throw s.error;return s.ok.checkState}async confirmClaim(t){if(!this.#r||!this.#i)throw"confirmClaim: No token or claimCode";let e=new URL(`${this.#r.getAud()}/offer/confirm`),n=await this.#r.asSignedBase64(),s={claimCode:this.#i,checkCode:t},i=await(await fetch(e,{method:"POST",headers:{"x-tabserver-token":n,"content-type":"application/json"},body:JSON.stringify(s)})).json();if("error"in i)throw i.error;return i.ok}getChild(){if(!this.#n)throw"getChild: no child";return this.#n}getChildOrigin(){if(!this.#s||!this.#n)throw"getDaemonUrl: no issUrl or daemon";return`${this.#e}//${this.#n}`}async#a(){if(!this.#s||!this.#n||!this.#t)throw"buildToken: missing iss, child or privateJwk";let t=this.#s,e=this.getChildOrigin(),n=new URL(this.#s).origin,s;if(this.#o){let a=new URL(this.#o);s=`${a.origin}${a.pathname}`}else s="party:control";let o={iss:t,aud:e,sub:n,src:s,scope:{"party:control":"offer"},iat:Date.now(),exp:Date.now()+1e3*60*3},i=new h(o);await i.signWith(Promise.resolve(this.#t)),this.#r=i}};var q="L",Q="qrcode",U=class{#t;#e;constructor(t,e){this.#t=t,this.#e=e}generate(){let t=qrcode(0,q);t.addData(this.#e),t.make();let e=t.createDataURL();return this.#t.classList.add(Q),this.#t.src=e,new Promise((n,s)=>{this.#t.onload=n,this.#t.onerror=s})}};function Pt(r){return r!==null}function At(r){return r!==void 0}function w(r,t){if("ok"in r){let i=JSON.stringify(r);return new Response(i,{status:200,headers:{...t,"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}let e=r.error,[,n,s]=e.match(/^(\d{3})\s(.+)/)??[null,"200",e],o=JSON.stringify({error:s});return new Response(o,{status:Number(n),headers:{...t,"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}function xt(r={error:"404 Not Found"}){let t=JSON.stringify(r);return new Response(t,{status:404,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}function Ct(r,t=[]){return new Response(null,{status:302,headers:{Location:r.toString(),"Access-Control-Allow-Origin":"*",...t}})}function Et(r){let{name:t,value:e,domain:n,sameSite:s,path:o="/",expires:i,secure:a=!0,httpOnly:p=!0}=r,c=[];return c.push(`${encodeURIComponent(t)}=${encodeURIComponent(e)}`),n&&c.push(`Domain=${n}`),s&&c.push(`SameSite=${s}`),c.push(`Path=${o}`),i&&c.push(`Expires=${i.toUTCString()}`),a&&c.push("Secure"),p&&c.push("HttpOnly"),c.join("; ")}var L="daemon",z="config",Z="event",O={Config:"config"},v="config",tt=1e3*60*60,$=class r extends EventTarget{static#t=new r;static Ev=O;static getInstance(){return this.#t}static shouldHandle(t){return r.isConfig(t)||r.isEvent(t)}static isConfig(t){let e=new URL(t.url);return t.method=="POST"&&e.pathname==`/${L}/${z}`&&t.headers.get("Content-Type")=="application/json"}static isEvent(t){let e=new URL(t.url);return t.method=="POST"&&e.pathname==`/${L}/${Z}`&&t.headers.get("Content-Type")=="application/json"}async handler(t){return r.isConfig(t)?await this.handleConfig(t):r.isEvent(t)?await this.handleEvent(t):w({error:"Invalid daemon request"})}async handleConfig(t){let e=await t.json();return sessionStorage.setItem(v,JSON.stringify(e)),this.dispatchEvent(new CustomEvent(O.Config,{detail:e})),w({ok:!0})}async handleEvent(t){let{type:e,payload:n}=await t.json();return this.dispatchEvent(new CustomEvent(e,{detail:n})),w({ok:!0})}static getConfig(){let t=sessionStorage.getItem(v);if(!t)throw"DaemonConfig not ready";return JSON.parse(t)}static getPrivateKey(){let{system:{privateJwk:t}}=r.getConfig();return Promise.resolve(t)}static async getTokenFor(t,e=null,n=null,s=tt){let o=r.getConfig();if(!o)throw"Lifecycle configuration not yet available";let{system:{protocol:i,party:a,issuer:p,source:c}}=o,g=new URL(c),l=e?`${i}//${e}`:`${i}//${a}`,m=`${i}//${a}`,S=Date.now(),K={iss:p,aud:l,sub:m,src:n??`${g.origin}${g.pathname}`,scope:t,iat:S,exp:S+s},k=new h(K);return await k.signWith(r.getPrivateKey()),k}static async getStorage(){let t=await r.getStorageToken();return new u(t)}static getStorageToken(){let{system:{party:t}}=r.getConfig(),e={[h.Source.PARTY_CONTROL]:`${h.Capability.GET_ITEM} ${h.Capability.SET_ITEM}`};return r.getTokenFor(e,t)}};var I="x-forwarded-host",N="x-forwarded-proto",j="x-forwarded-pathname";function et(r){return r.headers.has(N)?r.headers.get(N)+":":new URL(r.url).protocol}function rt(r){return r.headers.has(I)?r.headers.get(I)||"":new URL(r.url).host}function nt(r){return r.headers.has(j)?r.headers.get(j)||"":new URL(r.url).pathname}function st(r){let t=et(r),e=rt(r),n=nt(r);return new URL(`${t}//${e}${n}`)}function Nt(r){let t=new URL(r.url),e=st(r);return e.search=t.search,e.hash=t.hash,e}function jt(r){return new URL(r.url).pathname}function Kt(r,t){let e=ot(r);return t in e?e[t]:null}function ot(r){let t={},e=r.headers.get("Cookie");if(e)for(let n of e.split("; ")){let[s,o]=n.split("=");t[s]=o}return t}export{d as BrowserApp,z as CONFIG_PATH,L as DAEMON_PREFIX,Z as EVENT_PATH,Q as ImgClass,A as KeyPair,$ as Lifecycle,E as ParentHelper,U as QrCode,u as Storage,h as Token,Et as buildCookie,P as getAppId,rt as getClientHost,nt as getClientPathname,et as getClientProtocol,Nt as getClientUrl,st as getClientUrlPhp,Kt as getCookie,ot as getCookies,W as getItem,G as getItemsLike,mt as getLaunchAlert,jt as getPathname,At as isDefined,Pt as notNull,X as removeItem,w as response,Ct as response302,xt as response404,V as setItem,it as shortHexDigest,J as shortSafeDigest};
7
+ -----END PRIVATE KEY-----`}};var f={name:"RSASSA-PKCS1-v1_5",hash:"SHA-256"},R=/https?:\/\/[^\/]+$/,B=1e3*30,h=class r{static Source={PARTY_CONTROL:"party:control"};static Counterparty={PUBLIC:"public"};static Capability={OPEN:"open",CLOSE:"close",GRANT:"grant",REVOKE:"revoke",OFFER:"offer",RETRACT:"retract",DELETE:"delete",CATALOG:"catalog",ALIAS:"alias",UNALIAS:"unalias",GET_ITEM:"getitem",SET_ITEM:"setitem",ALERT:"alert",LOG:"log",READ:"read",WRITE:"write"};static DEFAULT_EXPIRY_MILLIS=1e3*60*60*24;static TOKEN_HEADER="x-tabserver-token";static SCOPE_SEPARATOR=/[\s,;\|]+/;#t=null;#e=null;#n;#s;#o;#r;constructor(t){let e=null;if(typeof t=="object")e=t;else if(typeof t=="string")try{e=JSON.parse(atob(t))}catch{throw"Invalid token format"}else throw`Cannot construct token from ${typeof t}`;this.#e=e.sig,delete e.sig,this.#t=e;let{iss:n,aud:s,sub:o,src:i,scope:a,iat:p,exp:c}=e;if(!n||!s||!o||!i||!a||!p||!c)throw"Token must include iss, aud, sub, src, scope, iat and exp";if(!s.match(R)||!o.match(R))throw"The aud and sub attributes must be origins";if(this.#n=new URL(s),this.#s=new URL(o),this.#o=new URL(i),this.#o.search||this.#o.hash)throw"The src attribute must have no query or fragment component.";this.#t=e}async signWith(t){if(this.#t==null)throw"No payload to sign";let e=await t,n=await crypto.subtle.importKey("jwk",e,f,!0,["sign"]),s=JSON.stringify(this.#t),o=new TextEncoder().encode(s).buffer,i=new Uint8Array(o),a=await crypto.subtle.sign(f,n,i),p=r.bytesToBase64(new Uint8Array(a));return this.#e=p,p}async verifySignatory(){if(await this.fetchSignatory(),!this.#r)throw"verifySignatory: no signatory";await this.checkSignature(Promise.resolve(this.#r.jwk))}async fetchSignatory(){let t=this.getIssuer();try{let n=await(await fetch(t,{headers:{"content-type":"application/json"}})).json();if("error"in n)throw n.error;this.#r=n.ok}catch{throw`Failed to read signatory at ${t}`}}async checkSignature(t){if(!this.#t||!this.#e)throw"checkSignature: no payload or signatureBase64";let e=await t;if(!e||"error"in e)throw`Missing or invalid public JWK at issuer ${this.getIssuer()}`;let n=await crypto.subtle.importKey("jwk",e,f,!0,["verify"]),s=JSON.stringify(this.#t),o=new TextEncoder().encode(s),i=r.base64ToBytes(this.#e);if(!await crypto.subtle.verify(f,n,i,o))throw`Signature rejected by issuer ${this.getIssuer()}`}getSignedPayload(){if(!this.#e||!this.#t)throw"getSignedPayload: no signatureBase64 or payload";return{...this.#t,sig:this.#e}}getPayload(){if(!this.#t)throw"getPayload: no payload";return this.#t}getAud(){return this.#n.origin}getParty(){return this.#n.host}getSub(){return this.#s.origin}getSrc(){return String(this.#o)}getCounterparty(){return this.#s.host}getSignatorySrc(){if(!this.#r)throw"getSignatorySrc: no signatory";return this.#r?.src??String(this.#o)}getSourceUrl(){return this.#o}getIssuer(){return new URL(this.getPayload().iss)}getSources(){if(!this.#t)throw"getSources: no payload";let t=[];for(let e in this.#t.scope)t.push(e);return t}getCapabilities(t){if(!this.#t)throw"getCapabilities: no payload";let e=this.#t.scope;if(!(t in e))throw`Source '${t}' not in scope`;return e[t].split(r.SCOPE_SEPARATOR)}hasCapability(t,e){return this.getCapabilities(t).includes(e)}checkPeriod(){if(!this.#t)throw"checkPeriod: no payload";let{iat:t,exp:e}=this.#t,n=Date.now();if(n<t-B)throw"Token is not yet valid";if(n>e)throw"Token has expired"}asSignedBase64(){return btoa(JSON.stringify(this.getSignedPayload()))}static bytesToBase64(t){let e=Array.from(t,n=>String.fromCodePoint(n)).join("");return btoa(e)}static base64ToBytes(t){let e=atob(t);return Uint8Array.from(e,n=>n.codePointAt(0)??0)}static from(t){let e=t.headers.get(r.TOKEN_HEADER);return e?new r(e):null}static toSearchString(t){let e=new URLSearchParams;for(let n in t){let o=t[n].asSignedBase64();e.append(n,o)}return e.toString()}};var H=/^[\w\-.:]+(?:\/[\w\-.:]+)*$/,M=/^[\w\-.:%_]+(?:\/[\w\-.:%_]+)*$/,u=class{#t;constructor(t){this.#t=t}getItem(t){return V(this.#t,t)}getItemsLike(t){return G(this.#t,t)}setItem(t,e){return W(this.#t,t,e)}removeItem(t){return X(this.#t,t)}};function T(r){if(r.match(H)==null)throw`DaemonStorage: Invalid key: '${r}`}function _(r){if(r.match(M)==null)throw`DaemonStorage: Invalid like key: ${r}`}function F(r){try{return JSON.stringify(r)}catch{throw"DaemonStorage: Invalid value"}}async function W(r,t,e){T(t);let n=F(e),s=`${r.getAud()}/storage/${t}`;return(await fetch(s,{method:"PUT",headers:{"X-Tabserver-Token":r.asSignedBase64(),"Content-Type":"application/json"},body:n})).json()}async function V(r,t){T(t);let e=`${r.getAud()}/storage/${t}`;return(await fetch(e,{method:"GET",headers:{"X-Tabserver-Token":r.asSignedBase64()}})).json()}async function G(r,t){_(t);let e=new URL(`${r.getAud()}/storage`);return e.searchParams.append("like",t),await(await fetch(e,{method:"GET",headers:{"X-Tabserver-Token":r.asSignedBase64()}})).json()}async function X(r,t){T(t);let e=`${r.getAud()}/storage/${t}`;return(await fetch(e,{method:"DELETE",headers:{"X-Tabserver-Token":r.asSignedBase64()}})).json()}var x={Ash:"\xE6",Hash:"#"},Y={Ash:encodeURIComponent("\xE6")},y=class{params=new URLSearchParams;extractHash(t){t=t.startsWith(x.Hash)?t.substring(1):t;let e=t.indexOf(Y.Ash);if(e===-1)return t;let n=new URLSearchParams(t.slice(e));return this.extract(n),t.slice(0,e)}extractQuery(t){let e=new URLSearchParams(t);return this.extract(e),e.toString()}extract(t){let e=Array.from(t.keys());for(let n of e)n.startsWith(x.Ash)&&(this.params.set(n.substring(1),t.get(n)??""),t.delete(n))}getParams(){return this.params}};var b="party",C="prefix",q="lastKnownDaemon",d=class r{static#t;#e;#n;#s;#o;static async getInstance(t=void 0){if(!t&&!r.#t)throw"Must provide app name";if(t&&r.#t&&t!==r.#t.#e)throw`Cannot change app name from ${r.#t.#e} to ${t}`;if(t&&!r.#t&&(r.#t=new r(t),await r.#t.init()),!r.#t)throw"No instance of BrowserApp";return r.#t}constructor(t){this.#e=t,this.#n=`${t}Party`,this.#s=`${t}Tokens`,this.#o=`${t}AppUrl`}async init(){let t=new URL(globalThis.location.href),e=new y,n=e.extractHash(t.hash),s=e.extractQuery(t.search),o=e.getParams();if(o.has(b)){sessionStorage[this.#s]=o.toString();let i=this.getToken();await i.verifySignatory(),sessionStorage[this.#n]=i.getParty(),localStorage.setItem(q,i.getParty());let a=i.getSourceUrl();if(a.origin!==t.origin||a.pathname!==t.pathname)throw`Party token is for different app: ${a}`}return t.hash=n,t.search=s,sessionStorage[this.#o]=t.toString(),globalThis.history.replaceState(null,"",t.toString()),this}isOrphan(){return sessionStorage[this.#n]===void 0||sessionStorage[this.#s]===void 0}getAppName(){return this.#e}getAppUrl(){return sessionStorage[this.#o]}getParty(){return sessionStorage[this.#n]}getPartyOrigin(){return`${globalThis.location.protocol}//${this.getParty()}`}getDefaultPrefix(t=null){t||(t=this.getAppUrl());let e=new URL(t);return e.hash="",P(e)}getPrefix(){return this.hasParam(C)?Promise.resolve(this.getParam(C)??""):this.getDefaultPrefix()}async getAgentUrl(t){let e=this.getPartyOrigin(),n=await this.getPrefix();return`${e}/tab/${n}/${t}`}getTokenBase64(t=b){let e=sessionStorage[this.#s],s=new URLSearchParams(e).get(t);if(!s)throw`No token for ${t}, check 'audience' in YML`;return s}getToken(t=b){let e=this.getTokenBase64(t);return new h(e)}getParam(t){let e=new URL(globalThis.location.href).searchParams;for(let[s,o]of e)if(t.toLowerCase()==s.toLowerCase())return o;return document.querySelector("link[rel=webdaemon]")?.getAttribute(t)??null}hasParam(t){return new URL(globalThis.location.href).searchParams.has(t)?!0:document.querySelector("link[rel=webdaemon]")?.hasAttribute(t)??!1}async getLaunchAlert(){let t=this.getPartyOrigin(),e=new URL(`${t}/catalog`);e.searchParams.append("alert","");let s=await(await fetch(e,{headers:{"X-Tabserver-Token":this.getTokenBase64(),Accept:"application/json"}})).json();if("error"in s)throw s.error;return s.ok.alert.find(i=>i.type=="LAUNCH"&&i.sourceUrl==new URL(this.getAppUrl()))||null}async resolveLaunchAlert(){let t=this.getPartyOrigin(),e=new URL(`${t}/resolve`),s=await(await fetch(e,{method:"POST",headers:{"X-Tabserver-Token":this.getTokenBase64(),"Content-Type":"application/json"},body:JSON.stringify({type:"LAUNCH",source:this.getAppUrl()})})).json();if("error"in s)throw s.error}async rejectLaunchAlert(){let t=this.getPartyOrigin(),e=new URL(`${t}/reject`),s=await(await fetch(e,{method:"POST",headers:{"X-Tabserver-Token":this.getTokenBase64(),"Content-Type":"application/json"},body:JSON.stringify({type:"LAUNCH",source:this.getAppUrl()})})).json();if("error"in s)throw s.error}getStorage(){return new u(this.getToken())}};async function St(){let r=await d.getInstance(),t=r.getPartyOrigin(),e=new URL(`${t}/catalog`);e.searchParams.append("alert","");let s=await(await fetch(e,{headers:{"X-Tabserver-Token":r.getTokenBase64(),Accept:"application/json"}})).json();if("error"in s)throw s.error;return s.ok.alert.find(i=>i.type=="LAUNCH"&&i.sourceUrl==new URL(r.getAppUrl()))||null}var E=class{#t;#e;#n;#s;#o;#r;#i;async initWithKey(t,e,n,s){this.#e=e.startsWith("https:")?"https:":"http:",this.#n=t,this.#o=e,this.#t=n,this.#s=s,await this.#a()}async checkActivate(){if(!this.#r)throw"checkActivate: No token";let t=new URL(`${this.#r.getAud()}/activate`),e=this.#r.asSignedBase64(),n={},s;try{s=await fetch(t,{method:"POST",headers:{"x-tabserver-token":e,"content-type":"application/json"},body:JSON.stringify(n)});let{ok:o,error:i}=await s.json();if(i)throw i;return!!o.isNew}catch(o){throw console.error(o),`Cannot activate ${this.#r.getParty()}`}}async makeOffer(t,e=null){let{type:n="TRANSIENT",ttl:s=300,expiry:o=30,payload:i}=e??{};if(!this.#r)throw"makeOffer: No token";let a=new URL(`${this.#r.getAud()}/offer`),p=this.#r.asSignedBase64(),c={role:"party",type:n,ttl:s,description:`Offered by ${this.#r.getCounterparty()}`,payload:i,expiry:new Date(Date.now()+1e3*o),flow:t},l=await(await fetch(a,{method:"POST",headers:{"x-tabserver-token":p,"content-type":"application/json"},body:JSON.stringify(c)})).json();if("error"in l)throw l.error;let{claimCode:m,_checkState:S}=l.ok;return this.#i=m,l.ok}async checkState(){if(!this.#r||!this.#i)throw"checkState: No token or claimCode";let t=new URL(`${this.#r.getAud()}/offer/check`);t.searchParams.append("claimCode",this.#i);let e=this.#r.asSignedBase64(),s=await(await fetch(t,{headers:{"x-tabserver-token":e}})).json();if("error"in s)throw s.error;return s.ok.checkState}async confirmClaim(t){if(!this.#r||!this.#i)throw"confirmClaim: No token or claimCode";let e=new URL(`${this.#r.getAud()}/offer/confirm`),n=await this.#r.asSignedBase64(),s={claimCode:this.#i,checkCode:t},i=await(await fetch(e,{method:"POST",headers:{"x-tabserver-token":n,"content-type":"application/json"},body:JSON.stringify(s)})).json();if("error"in i)throw i.error;return i.ok}getChild(){if(!this.#n)throw"getChild: no child";return this.#n}getChildOrigin(){if(!this.#s||!this.#n)throw"getDaemonUrl: no issUrl or daemon";return`${this.#e}//${this.#n}`}async#a(){if(!this.#s||!this.#n||!this.#t)throw"buildToken: missing iss, child or privateJwk";let t=this.#s,e=this.getChildOrigin(),n=new URL(this.#s).origin,s;if(this.#o){let a=new URL(this.#o);s=`${a.origin}${a.pathname}`}else s="party:control";let o={iss:t,aud:e,sub:n,src:s,scope:{"party:control":"offer"},iat:Date.now(),exp:Date.now()+1e3*60*3},i=new h(o);await i.signWith(Promise.resolve(this.#t)),this.#r=i}};var Q="L",z="qrcode",L=class{#t;#e;constructor(t,e){this.#t=t,this.#e=e}generate(){let t=qrcode(0,Q);t.addData(this.#e),t.make();let e=t.createDataURL();return this.#t.classList.add(z),this.#t.src=e,new Promise((n,s)=>{this.#t.onload=n,this.#t.onerror=s})}};function At(r){return r!==null}function Rt(r){return r!==void 0}function w(r,t){if("ok"in r){let i=JSON.stringify(r);return new Response(i,{status:200,headers:{...t,"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}let e=r.error,[,n,s]=e.match(/^(\d{3})\s(.+)/)??[null,"200",e],o=JSON.stringify({error:s});return new Response(o,{status:Number(n),headers:{...t,"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}function Ct(r={error:"404 Not Found"}){let t=JSON.stringify(r);return new Response(t,{status:404,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}function Et(r,t=[]){return new Response(null,{status:302,headers:{Location:r.toString(),"Access-Control-Allow-Origin":"*",...t}})}function Lt(r){let{name:t,value:e,domain:n,sameSite:s,path:o="/",expires:i,secure:a=!0,httpOnly:p=!0}=r,c=[];return c.push(`${encodeURIComponent(t)}=${encodeURIComponent(e)}`),n&&c.push(`Domain=${n}`),s&&c.push(`SameSite=${s}`),c.push(`Path=${o}`),i&&c.push(`Expires=${i.toUTCString()}`),a&&c.push("Secure"),p&&c.push("HttpOnly"),c.join("; ")}var U="daemon",Z="config",tt="event",O={Config:"config"},v="config",et=1e3*60*60,$=class r extends EventTarget{static#t=new r;static Ev=O;static getInstance(){return this.#t}static shouldHandle(t){return r.isConfig(t)||r.isEvent(t)}static isConfig(t){let e=new URL(t.url);return t.method=="POST"&&e.pathname==`/${U}/${Z}`&&t.headers.get("Content-Type")=="application/json"}static isEvent(t){let e=new URL(t.url);return t.method=="POST"&&e.pathname==`/${U}/${tt}`&&t.headers.get("Content-Type")=="application/json"}async handler(t){return r.isConfig(t)?await this.handleConfig(t):r.isEvent(t)?await this.handleEvent(t):w({error:"Invalid daemon request"})}async handleConfig(t){let e=await t.json();return sessionStorage.setItem(v,JSON.stringify(e)),this.dispatchEvent(new CustomEvent(O.Config,{detail:e})),w({ok:!0})}async handleEvent(t){let{type:e,payload:n}=await t.json();return this.dispatchEvent(new CustomEvent(e,{detail:n})),w({ok:!0})}static getConfig(){let t=sessionStorage.getItem(v);if(!t)throw"DaemonConfig not ready";return JSON.parse(t)}static getPrivateKey(){let{system:{privateJwk:t}}=r.getConfig();return Promise.resolve(t)}static async getTokenFor(t,e=null,n=null,s=et){let o=r.getConfig();if(!o)throw"Lifecycle configuration not yet available";let{system:{protocol:i,party:a,issuer:p,source:c}}=o,g=new URL(c),l=e?`${i}//${e}`:`${i}//${a}`,m=`${i}//${a}`,S=Date.now(),K={iss:p,aud:l,sub:m,src:n??`${g.origin}${g.pathname}`,scope:t,iat:S,exp:S+s},k=new h(K);return await k.signWith(r.getPrivateKey()),k}static async getStorage(){let t=await r.getStorageToken();return new u(t)}static getStorageToken(){let{system:{party:t}}=r.getConfig(),e={[h.Source.PARTY_CONTROL]:`${h.Capability.GET_ITEM} ${h.Capability.SET_ITEM}`};return r.getTokenFor(e,t)}};var I="x-forwarded-host",N="x-forwarded-proto",j="x-forwarded-pathname";function rt(r){return r.headers.has(N)?r.headers.get(N)+":":new URL(r.url).protocol}function nt(r){return r.headers.has(I)?r.headers.get(I)||"":new URL(r.url).host}function st(r){return r.headers.has(j)?r.headers.get(j)||"":new URL(r.url).pathname}function ot(r){let t=rt(r),e=nt(r),n=st(r);return new URL(`${t}//${e}${n}`)}function jt(r){let t=new URL(r.url),e=ot(r);return e.search=t.search,e.hash=t.hash,e}function Kt(r){return new URL(r.url).pathname}function Jt(r,t){let e=it(r);return t in e?e[t]:null}function it(r){let t={},e=r.headers.get("Cookie");if(e)for(let n of e.split("; ")){let[s,o]=n.split("=");t[s]=o}return t}export{d as BrowserApp,Z as CONFIG_PATH,U as DAEMON_PREFIX,tt as EVENT_PATH,z as ImgClass,A as KeyPair,$ as Lifecycle,E as ParentHelper,L as QrCode,u as Storage,h as Token,Lt as buildCookie,P as getAppId,nt as getClientHost,st as getClientPathname,rt as getClientProtocol,jt as getClientUrl,ot as getClientUrlPhp,Jt as getCookie,it as getCookies,V as getItem,G as getItemsLike,St as getLaunchAlert,Kt as getPathname,Rt as isDefined,At as notNull,X as removeItem,w as response,Et as response302,Ct as response404,W as setItem,at as shortHexDigest,J as shortSafeDigest};
8
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../js/Digest.js", "../js/KeyPair.js", "../js/Token.js", "../js/Storage.js", "../js/ParamExtractor.js", "../js/BrowserApp.js", "../js/Alert.js", "../js/ParentHelper.js", "../js/QrCode.js", "../ts/Assertions.ts", "../ts/Responses.ts", "../ts/Lifecycle.ts", "../ts/Requests.ts"],
4
- "sourcesContent": ["/**\n * Generates a SHA-1 digest of the supplied string, and returns\n * it as a base64 string optionally truncated to the first 'length'\n * characters.\n *\n * @param {string} message to digest.\n * @return {Promise<string>} base64-encoded digest.\n */\nexport async function shortSafeDigest(message, length = 0) {\n const msgUint8 = new TextEncoder().encode(message)\n if (!crypto.subtle) {\n throw 'Requires secure origin (localhost or https:)'\n }\n const hashBuffer = await crypto.subtle.digest(\"SHA-1\", msgUint8)\n const hashArray = new Uint8Array(hashBuffer)\n const byteString = String.fromCodePoint(...hashArray)\n const base64 = btoa(byteString)\n const base64Safe = base64\n .replaceAll('+','-')\n .replaceAll('/','_')\n .replaceAll('=','')\n return length ? base64Safe.substring(0, length) : base64Safe\n}\n\n/**\n * Generates a SHA-1 digest of the supplied string, and returns\n * it as a hex string optionally truncated to the first 'length'\n * characters.\n *\n * @param {string} message to digest.\n * @return {Promise<string>} hex-encoded digest.\n */\nexport async function shortHexDigest(message, length = 0) {\n const msgUint8 = new TextEncoder().encode(message)\n if (!crypto.subtle) {\n throw 'Requires secure origin (localhost or https:)'\n }\n const hashBuffer = await crypto.subtle.digest(\"SHA-1\", msgUint8)\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n const hashHex = hashArray\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n\n return length ? hashHex.substring(0, length) : hashHex\n}\n\n/**\n * Returns the standard app id for the supplied app URL.\n *\n * This is the shortSafeDigest of the URL truncated to 8 base64 chars,\n * i.e. distributed over a 48-bit space which seems reasonable for the\n * number of apps on a given daemon.\n *\n * @param {string | URL} url of the app whose id is to be computed.s\n * @return {Promise<string>} default id for the app, commonly used in prefixes.\n */\nexport async function getAppId(url) {\n const appId = await shortSafeDigest(String(url), 8)\n return appId\n}\n", "const DEFAULT_ALGORITHM = {\n name: 'RSASSA-PKCS1-v1_5',\n modulusLength: 2048,\n publicExponent: new Uint8Array([0x01, 0x00, 0x01]),\n hash: 'SHA-256'\n}\n\nexport class KeyPair {\n #algorithm\n\n /** @type {CryptoKeyPair | undefined} */\n #keypair\n\n constructor(algorithm = DEFAULT_ALGORITHM) {\n this.#algorithm = algorithm\n if (!crypto.subtle) {\n throw 'Crypto.subtle requires secure environment'\n }\n }\n\n async generate() {\n this.#keypair = await crypto.subtle.generateKey(\n this.#algorithm,\n true,\n ['sign', 'verify']\n )\n }\n\n async publicJwk() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const publicKey = this.#keypair.publicKey\n const publicJwk = await crypto.subtle.exportKey('jwk', publicKey)\n return publicJwk\n }\n\n async privateJwk() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const privateKey = this.#keypair.privateKey\n const privateJwk = await crypto.subtle.exportKey('jwk', privateKey)\n return privateJwk\n }\n\n async publicPem() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const publicKey = this.#keypair.publicKey\n const spkiKey = await crypto.subtle.exportKey('spki', publicKey)\n const base64Key = btoa(String.fromCharCode(...new Uint8Array(spkiKey)))\n const base64KeyLines = base64Key.match(/.{1,64}/g)?.join('\\n') ?? ''\n const publicPem = `-----BEGIN PUBLIC KEY-----\\n${base64KeyLines}\\n-----END PUBLIC KEY-----`\n return publicPem\n }\n\n async privatePem() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const privateKey = this.#keypair.privateKey\n const pkcs8Key = await crypto.subtle.exportKey('pkcs8', privateKey)\n const base64Key = btoa(String.fromCharCode(...new Uint8Array(pkcs8Key)))\n const base64KeyLines = base64Key.match(/.{1,64}/g)?.join('\\n') ?? ''\n const privatePem = `-----BEGIN PRIVATE KEY-----\\n${base64KeyLines}\\n-----END PRIVATE KEY-----`\n return privatePem\n }\n}\n", "/**\n * Encapsulates the functionality associated with a bearer token normally\n * passed in a request `x-tabserver-token` header as a base64-encoded\n * JSON object.\n *\n * Note the type definition annotations which are helpful when using this\n * Javascript class in Typescript.\n *\n * Example token:\n *\n * {\n * \"iss\": \"http://foo.daemon/some/public.jwk\",\n * \"aud\": \"https://bar.daemon\",\n * \"sub\": \"https://foo.daemon\",\n * \"src\": \"https://some.com\",\n * \"scope\": {\n * \"http://localhost:9000/helloWorld1.html\": \"read write\",\n * \"party:control\": \"grant revoke\"\n * }\n * \"iat\": 1698576567000,\n * \"exp\": 1698576344000,\n * \"sig\": \"long base64 string\"\n * }\n *\n * Generally:\n * iss - the issuer URL references the public JWK used to verify the sig.\n * aud - the party handling the request, expressed as an origin.\n * sub - the counterparty making the request, expressed as an origin.\n * src - the source HTML file making the request, protocol://host/path only.\n * scope - a map of requested source -> scope pairs.\n * A scope is a space-separated list of capabilities.\n * iat - issued at time in absolute millis.\n * exp - expiry time, ditto.\n * sig - the base64 signature, verified by the JWK pointed to by iss.\n *\n * Normally the sub field must match the host portion of the iss URL. But\n * the possiblity remains open for an intermediary trusted by the party to\n * hold the public keys of counterparties.\n */\n\n/**\n * @typedef {Object.<string, string>} Scope\n */\n\n/**\n * @typedef TokenPayload\n * @property {string} iss // URL of public key of counterparty making request.\n * @property {string} aud // Party origin handling the request.\n * @property {string} sub // Counterparty origin making the request.\n * @property {string} src // Source HTML document context of the request excluding fragment and query.\n * @property {Scope} scope // Map of scope (space-separated capabilities) keyed by source URL.\n * @property {number} iat // Issued at time.\n * @property {number} exp // Expiry time.\n * @property {string} sig // Signature if signed.\n */\n\n/**\n * @typedef {Object} Signatory the object to be served on the iss URL.\n * @property {string?} src which is 'party:control' if the shell, or source URL if a runner.\n * @property {JsonWebKey} jwk\n */\n\n/**\n * @typedef TokenSet\n * @type {Object<string, Token>}\n */\n\n/** Must match algorithm in Keypair.js */\nconst ALGORITHM = {\n name: 'RSASSA-PKCS1-v1_5',\n hash: 'SHA-256'\n}\n\n/** Matches an origin with protocol and no path. */\nconst MATCH_ORIGIN = /https?:\\/\\/[^\\/]+$/\n\n/**\n * Allow a 30s leeway to allow for clock difference when checking\n * token issued at time. We allow our clock to be up to 30s behind\n * the system that generated a token.\n */\nconst TOKEN_IAT_LEEWAY_MILLIS = 1000 * 30\n\nexport class Token {\n\n // Standard sources.\n static Source = {\n PARTY_CONTROL: 'party:control' // Party control subject, a pseudo-source with scope granted to counterparties.\n }\n\n // Standard counterparty pattern.\n static Counterparty = {\n PUBLIC: 'public' // Public counterparty, matching requests with no host specified in sub.\n }\n\n // Standard scope labels.\n static Capability = {\n OPEN: 'open', // Capability to open source files and start tab runners.\n CLOSE: 'close', // Capability to close source files and stop tab runners.\n GRANT: 'grant', // Capability to create relation records.\n REVOKE: 'revoke', // Capability to delete relation records.\n OFFER: 'offer', // Capability to create a party offer for a device to claim.\n RETRACT: 'retract', // Capability to delete a party offer before it is claimed.\n DELETE: 'delete', // Capability to delete devices.\n CATALOG: 'catalog', // Capability to get sources, devices, alerts etc.\n ALIAS: 'alias', // Capability to set the alias for a counterparty host.\n UNALIAS: 'unalias', // Capability to unset the alias for a counterparty host.\n GET_ITEM: 'getitem', // Capability to get a storage item.\n SET_ITEM: 'setitem', // Capability to set (and remove) a storage item.\n ALERT: 'alert', // Capability to post and delete alerts.\n LOG: 'log', // Capability to view logs.\n READ: 'read', // Capability to read from drive.\n WRITE: 'write' // Capability to write to drive.\n }\n\n static DEFAULT_EXPIRY_MILLIS = 1000 * 60 * 60 * 24\n static TOKEN_HEADER = 'x-tabserver-token'\n static SCOPE_SEPARATOR = /[\\s,;\\|]+/ // Specification per String.split() function.\n\n /** @type{TokenPayload | null} */\n #payload = null\n\n /** @type{string | null} */\n #signatureBase64 = null\n\n /** @type{URL} */\n #audUrl\n\n /** @type{URL} */\n #subUrl\n\n /** @type{URL} */\n #srcUrl\n\n /** @type {Signatory | undefined} */\n #signatory // Set upon successful verification of signature.\n\n\n /**\n * Constructor takes either a signed base64 token, or a payload object.\n *\n * If successful, the payload and optionally the signature fields are\n * populated.\n *\n * @param {object | string} source a payload, or a base64 token\n */\n constructor(source) {\n let payload = null\n\n if (typeof source == 'object') {\n payload = source\n }\n else if (typeof source == 'string') {\n try {\n payload = JSON.parse(atob(source))\n }\n catch (_e) {\n throw 'Invalid token format'\n }\n }\n else {\n throw `Cannot construct token from ${typeof source}`\n }\n\n // Separate out the signature from the payload.\n this.#signatureBase64 = payload.sig\n delete payload.sig\n this.#payload = payload\n\n const {\n iss,\n aud,\n sub,\n src,\n scope,\n iat,\n exp\n } = payload\n\n // Check payload is complete.\n if ((!iss || !aud || !sub || !src || !scope || !iat || !exp)) {\n throw 'Token must include iss, aud, sub, src, scope, iat and exp'\n }\n\n // Check aud, sub and src fields are origins (i.e. proto://host.name)\n if (\n !aud.match(MATCH_ORIGIN) ||\n !sub.match(MATCH_ORIGIN)\n ) {\n throw 'The aud and sub attributes must be origins'\n }\n\n this.#audUrl = new URL(aud)\n this.#subUrl = new URL(sub)\n this.#srcUrl = new URL(src)\n\n // The `src` attribute must not have query or fragment.\n if (this.#srcUrl.search || this.#srcUrl.hash) {\n throw 'The src attribute must have no query or fragment component.'\n }\n\n this.#payload = payload\n }\n\n /**\n * Generates the signature for the object using the private key provided by the\n * supplied callback and stores the result.\n *\n * @param {Promise<JsonWebKey>} privateJwkPromise the private key used for signing.\n * @return {Promise<string>} resolved with the base64 signature if signed successfully.\n */\n async signWith(privateJwkPromise) {\n if (this.#payload == null) {\n throw 'No payload to sign'\n }\n\n const privateJwk = await privateJwkPromise\n const privateKey = await crypto.subtle.importKey('jwk', privateJwk, ALGORITHM, true, ['sign'])\n const toSign = JSON.stringify(this.#payload)\n const messageBuffer = new TextEncoder().encode(toSign).buffer\n const bufferSource = new Uint8Array(messageBuffer)\n const signature = await crypto.subtle.sign(ALGORITHM, privateKey, bufferSource)\n const signatureBase64 = Token.bytesToBase64(new Uint8Array(signature))\n this.#signatureBase64 = signatureBase64\n return signatureBase64\n }\n\n /**\n * Retrieves the signatory using the `iss` field, checks the signature\n * and if valid sets the signatory property in this instance.\n *\n * @throws {string} if signatory cannot be retrieved or signature doesn't match.\n */\n async verifySignatory() {\n await this.fetchSignatory()\n if (!this.#signatory) {\n throw 'verifySignatory: no signatory'\n }\n await this.checkSignature(Promise.resolve(this.#signatory.jwk))\n }\n\n /**\n * Fetches the signatory from the `iss` URL, setting the instance\n * property.\n *\n * The signatory comprises the `src` representing the app that has\n * signed the token and the public `jwk`.\n *\n * Caching may be introduced to reduce latency and bandwidth use.\n *\n * @throws {string} error if fetch fails or returns an error response.\n */\n async fetchSignatory() {\n const issuer = this.getIssuer()\n try {\n const response = await fetch(issuer, {\n headers: {\n 'content-type': 'application/json'\n }\n })\n\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n this.#signatory = json.ok\n }\n catch (_e) {\n throw `Failed to read signatory at ${issuer}`\n }\n }\n\n /**\n * Checks the signature using the public key provided by the supplied callback,\n * and stores the result.\n *\n * @param {Promise<JsonWebKey>} publicJwkPromise the public key used to verify the signature.\n * @throws {string} exception if signature cannot be verified or the public key is null or error.\n */\n async checkSignature(publicJwkPromise) {\n if (!this.#payload || !this.#signatureBase64) {\n throw 'checkSignature: no payload or signatureBase64'\n }\n\n const publicJwk = await publicJwkPromise\n if (!publicJwk || 'error' in publicJwk) {\n throw `Missing or invalid public JWK at issuer ${this.getIssuer()}`\n }\n const publicKey = await crypto.subtle.importKey('jwk', publicJwk, ALGORITHM, true, ['verify'])\n const toCheck = JSON.stringify(this.#payload)\n const messageBuffer = new TextEncoder().encode(toCheck)\n const sigBuffer = Token.base64ToBytes(this.#signatureBase64)\n const isVerified = await crypto.subtle.verify(ALGORITHM, publicKey, sigBuffer, messageBuffer)\n if (!isVerified) {\n throw `Signature rejected by issuer ${this.getIssuer()}`\n }\n }\n\n /**\n * Returns the payload with the `sig` property added.\n *\n * @return {TokenPayload} the payload with `sig` property.\n */\n getSignedPayload() {\n if (!this.#signatureBase64 || !this.#payload) {\n throw 'getSignedPayload: no signatureBase64 or payload'\n }\n\n return {\n ...this.#payload,\n sig: this.#signatureBase64\n }\n }\n\n /**\n * Returns the payload whether signed or not.\n *\n * @return {TokenPayload} the payload object without signature.\n */\n getPayload() {\n if (!this.#payload) {\n throw 'getPayload: no payload'\n }\n return this.#payload\n }\n\n /**\n * Returns the `aud` field as a string.\n *\n * @return {string} the `aud` field value.\n */\n getAud() {\n return this.#audUrl.origin\n }\n\n\n /**\n * Returns the `aud` host which is the party name handling the request.\n *\n * @return {string} the party name.\n */\n getParty() {\n return this.#audUrl.host\n }\n\n /**\n * Returns the `sub` field as a string.\n *\n * @return {string} the `sub` field value.\n */\n getSub() {\n return this.#subUrl.origin\n }\n\n /**\n * Returns the `src` field as a string.\n *\n * @return {string} the `src` field value.\n */\n getSrc() {\n return String(this.#srcUrl)\n }\n\n /**\n * Returns the `sub` host, which is the counterparty name making the\n * request.\n *\n * @return {string} the counterparty name.\n */\n getCounterparty() {\n return this.#subUrl.host\n }\n\n /**\n * Returns the source of the signatory who signed on behalf of the\n * counterparty. This is only available once the token is\n * verified, as it is provided by the `iss` endpoint.\n *\n * @returns {string} source of the verified signatory.\n */\n getSignatorySrc() {\n if (!this.#signatory) {\n throw 'getSignatorySrc: no signatory'\n }\n\n // For backward compatibility, a missing `src` is\n // substituted.\n // https://github.com/sparkl/tabserver/issues/161\n return this.#signatory?.src ?? String(this.#srcUrl)\n }\n\n /**\n * Returns the `src` URL.\n *\n * @return {URL} the source URL.\n */\n getSourceUrl() {\n return this.#srcUrl\n }\n\n /**\n * Returns the `issuer` field as a URL. The counterparty is inferred from\n * the host name which generally matches the sub field.\n *\n * @return {URL} the issuer URL.\n */\n getIssuer() {\n return new URL(this.getPayload().iss)\n }\n\n /**\n * Returns the list of zero or more sources for which scope is requested.\n *\n * @return {string[]} a list of source URL strings.\n */\n getSources() {\n if (!this.#payload) {\n throw 'getSources: no payload'\n }\n\n const sources = []\n for (const source in this.#payload.scope) {\n sources.push(source)\n }\n return sources\n }\n\n /**\n * Returns the scope for the source as an array of capability tokens.\n *\n * These can be separated by any mix of space, comma, semicolon\n * or vertical bar.\n *\n * @param {string} source whose capabilities are returned.\n * @return {string[]} zero or more scope tokens.\n */\n getCapabilities(source) {\n if (!this.#payload) {\n throw 'getCapabilities: no payload'\n }\n\n const scope = this.#payload.scope\n if (! (source in scope)) {\n throw `Source '${source}' not in scope`\n }\n\n return scope[source].split(Token.SCOPE_SEPARATOR)\n }\n\n /**\n * Returns true if the supplied capability is present in the token\n * scope under the specified source, otherwise false.\n *\n * @param {string} source the source under which the capability is expected.\n * @param {string} capability the capability being tested.\n * @return {boolean} true if capability is present in the scope, otherwise false.\n */\n hasCapability(source, capability) {\n return this.getCapabilities(source).includes(capability)\n }\n\n /**\n * Checks the token is within the period defined by the `iat` and `exp`\n * date fields.\n *\n * @throws {string} exception if not within valid period.\n */\n checkPeriod() {\n if (!this.#payload) {\n throw 'checkPeriod: no payload'\n }\n\n const {\n iat,\n exp\n } = this.#payload\n\n const now = Date.now()\n if (now < iat - TOKEN_IAT_LEEWAY_MILLIS) {\n throw 'Token is not yet valid'\n }\n\n if (now > exp) {\n throw 'Token has expired'\n }\n }\n\n /**\n * Returns the signed object including the `sig` property as a base64 string.\n *\n * @return {string} the signed object as a base64 string.\n */\n asSignedBase64() {\n return btoa(JSON.stringify(this.getSignedPayload()))\n }\n\n /**\n * Returns a base64 JSON string corresponding to the provided byte array.\n *\n * @param {Uint8Array} bytes to be encoded as a base64 string.\n * @returns {string} the encoded bytes.\n */\n static bytesToBase64(bytes) {\n const binString = Array.from(bytes, (x) => String.fromCodePoint(x)).join('');\n return btoa(binString);\n }\n\n /**\n * Returns the byte array decoded from the base64 string.\n *\n * @param {string} base64\n * @returns {Uint8Array} the decoded bytes.\n */\n static base64ToBytes(base64) {\n const binString = atob(base64);\n return Uint8Array.from(binString, (m) => m.codePointAt(0) ?? 0);\n }\n\n /**\n * Returns a new token using the x-tabserver-token header on the\n * request.\n *\n * @param {Request} request the request.\n * @return {Token | null} the token, or null if no header is present.\n * @throws {string} exception if token cannot be constructed.\n */\n static from(request) {\n const tokenBase64 = request.headers.get(Token.TOKEN_HEADER)\n if (tokenBase64) {\n return new Token(tokenBase64)\n }\n\n return null\n }\n\n /**\n * Converts an object whose string keys map to token objects\n * into URL-friendly search params suitable for use in a\n * query string or fragment.\n *\n * The string keys are normally the audience hostname, or the\n * special key 'party' which holds the identity token whose\n * `aud` and `sub` are the same.\n *\n * @param {TokenSet} tokenSet the set of tokens keyed by string.\n * @returns {string}\n */\n static toSearchString(tokenSet) {\n const searchParams = new URLSearchParams()\n for (const key in tokenSet) {\n const token = tokenSet[key]\n const tokenBase64 = token.asSignedBase64()\n searchParams.append(key, tokenBase64)\n }\n return searchParams.toString()\n }\n}\n", "/**\n * @import { Token } from './Token.js'\n * @import { Ok, Error } from '../ts/Responses.ts'\n * @import { JSONValue } from '../ts/Assertions.ts'\n */\n\n/**\n * Storage keys are 1 or more slash-separated components\n * using the characters a-z, A-Z, 0-9, hyphen, colon and period.\n *\n * Examples:\n * - acmeSetting\n * - acme.com/applicant/22fc01-b0d3084870b-fcae49ac2-892668\n * - address/line1\n * - url/acme.com\n *\n * The key needs to work as part of a REST/HTTP pathname.\n */\nconst KEY_PATTERN = /^[\\w\\-.:]+(?:\\/[\\w\\-.:]+)*$/\n\n/**\n * As above, but % and _ are both allowed as a wildcards.\n */\nconst KEYLIKE_PATTERN = /^[\\w\\-.:%_]+(?:\\/[\\w\\-.:%_]+)*$/\n\n/**\n * An instance of this class is normally obtained from BrowserApp, which constructs\n * the instance using the party token.\n */\nexport class Storage {\n #token // Party token which must include getitem and setitem capabilities on party:control.\n\n /**\n * @param {Token} token to use for storage requests.\n */\n constructor(token) {\n this.#token = token\n }\n\n /**\n * Gets the item whose key is provided. Keys are in the form\n * 'some[/key]*' without leading or trailing '/'.\n *\n * @param {string} key used to retrieve the item.\n * @return {Promise<Ok<JSONValue> | Error>} ok or error result.\n */\n getItem(key) {\n return getItem(this.#token, key)\n }\n\n /**\n * Gets the list (possibly empty) of items matching the SQL-like\n * argument.\n *\n * @param {string} keylike a key matcher such as 'myApp/%'.\n * @return {Promise<Ok<JSONValue[]>>} a list of values.\n */\n getItemsLike(keylike) {\n return getItemsLike(this.#token, keylike)\n }\n\n /**\n * Stores an item with the supplied key and value.\n *\n * @param {string} key the key to use.\n * @param {JSONValue} value to store.\n * @return {Promise<Ok<true> | Error>} ok if successful, error otherwise.\n */\n setItem(key, value) {\n return setItem(this.#token, key, value)\n }\n\n /**\n * Removes the item with the specified key, if any.\n *\n * @param {string} key the key to remove.\n * @return {Promise<Ok<true> | Error>} ok if successful, error otherwise.\n */\n removeItem(key) {\n return removeItem(this.#token, key)\n }\n}\n\n/**\n * Throws an exception if the key being used is not\n * in a valid format.\n *\n * @param {string} key\n */\nfunction assertValid(key) {\n if (key.match(KEY_PATTERN) == null) {\n throw `DaemonStorage: Invalid key: '${key}`\n }\n}\n\n/**\n * Throws an exception if the key like pattern being used\n * is not in a valid format.\n *\n * @param {string} keylike\n */\nfunction assertValidLike(keylike) {\n if (keylike.match(KEYLIKE_PATTERN) == null) {\n throw `DaemonStorage: Invalid like key: ${keylike}`\n }\n}\n\n/**\n * Returns the value as a JSON string\n *\n * @param {JSONValue} value\n * @return {string} JSON string.\n * @throws {string} exception if the value is not JSON.\n */\nfunction serialise(value) {\n try {\n return JSON.stringify(value)\n }\n catch (_e) {\n throw `DaemonStorage: Invalid value`\n }\n}\n\n/**\n * Sets an item in daemon storage.\n *\n * The value must be an object or primitive\n * which is serialised before saving.\n *\n * @param {Token} token to use.\n * @param {string} key the item key.\n * @param {any} value the serialisable value for the item.\n * @return {Promise<Ok<true> | Error>} ok or error object.\n */\nexport async function setItem(token, key, value) {\n assertValid(key)\n const body = serialise(value)\n const url = `${token.getAud()}/storage/${key}`\n const response = await fetch(url, {\n method: 'PUT',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64(),\n 'Content-Type': 'application/json'\n },\n body\n })\n return response.json()\n}\n\n/**\n * Gets an item from daemon storage.\n *\n * Returns an 'ok' object with the value if present, otherwise\n * returns an 'error' object.\n *\n * @param {Token} token to use.\n * @param {string} key the item key to retrieve.\n * @return {Promise<Ok<JSONValue> | Error>} ok object with value, or error.\n */\nexport async function getItem(token, key) {\n assertValid(key)\n const url = `${token.getAud()}/storage/${key}`\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64()\n }\n })\n return response.json()\n}\n\n/**\n * Gets zero or more items from daemon storage, whose\n * keys match the keyLike pattern. This is normally\n * something like 'my/prefix/%' which gets all values\n * with that prefix.\n *\n * You can use both '%' and '_' as wildcards for string\n * of any length and single character respectively.\n *\n * @param {Token} token to use.\n * @param {string} keylike the item keylike pattern to match.\n * @return {Promise<Ok<JSONValue[]>>} ok object with list, or error.\n */\nexport async function getItemsLike(token, keylike) {\n assertValidLike(keylike)\n const url = new URL(`${token.getAud()}/storage`)\n url.searchParams.append('like', keylike)\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64()\n }\n })\n return await response.json()\n}\n\n/**\n * Removes an item from daemon storage.\n *\n * @param {Token} token to use.\n * @param {string} key the item key to remove.\n * @return {Promise<Ok<true> | Error>} ok or error object.\n */\nexport async function removeItem(token, key) {\n assertValid(key)\n const url =`${token.getAud()}/storage/${key}`\n const response = await fetch(url, {\n method: 'DELETE',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64()\n }\n })\n return response.json()\n}\n", "const Char = {\n Ash: '\u00E6',\n Hash: '#'\n}\n\nconst Encoded = {\n Ash: encodeURIComponent('\u00E6')\n}\n\nexport class ParamExtractor {\n params = new URLSearchParams()\n\n /**\n * @param {string} hash the hash string, with or without leading '#'.\n * @returns {string} the cleaned hash string.\n */\n extractHash(hash) {\n hash = hash.startsWith(Char.Hash) ? hash.substring(1) : hash\n const i = hash.indexOf(Encoded.Ash)\n if (i === -1) {\n return hash\n }\n\n const params = new URLSearchParams(hash.slice(i))\n this.extract(params)\n return hash.slice(0, i)\n }\n\n /**\n * @param {string} query the query string.\n * @returns {string} the cleaned query string.\n */\n extractQuery(query) {\n const params = new URLSearchParams(query)\n this.extract(params)\n return params.toString()\n }\n\n /**\n * Extracts \u00E6-prefixed params into this instance,\n * then deletes them from the supplied searchparams\n * object.\n *\n * @param {URLSearchParams} params\n */\n extract(params) {\n const keys = Array.from(params.keys())\n for (const key of keys) {\n if (key.startsWith(Char.Ash)) {\n this.params.set(key.substring(1), params.get(key) ?? '')\n params.delete(key)\n }\n }\n }\n\n getParams() {\n return this.params\n }\n}\n", "import { Token } from './Token.js'\nimport { Storage } from './Storage.js'\nimport { getAppId } from './Digest.js'\nimport { ParamExtractor } from './ParamExtractor.js'\n\n/**\n * @module\n * Provides convenience support for WebDaemon apps delivered\n * through browsers.\n *\n * Use the singleton pattern, providing app name on first call\n * to getInstance.\n */\n\nconst PARTY_PARAM = 'party'\nconst PREFIX_PARAM = 'prefix'\n\nexport class BrowserApp {\n\n /** @type {BrowserApp | null} */\n static #instance\n\n #appName\n #partyKey\n #tokensKey\n #appUrlKey\n\n /**\n * Gets the singleton instance of BrowserApp. On invocations\n * after the first, the app name must be omitted or match\n * the first-used app name.\n *\n * @param {string=} appName\n * @return {Promise<BrowserApp>} instance of browser app.\n */\n static async getInstance(appName = undefined) {\n if (\n !appName &&\n !BrowserApp.#instance\n ) {\n throw `Must provide app name`\n }\n\n if (\n appName &&\n BrowserApp.#instance &&\n appName !== BrowserApp.#instance.#appName\n ) {\n throw `Cannot change app name from ${BrowserApp.#instance.#appName} to ${appName}`\n }\n\n if (appName && !BrowserApp.#instance) {\n BrowserApp.#instance = new BrowserApp(appName)\n await BrowserApp.#instance.init()\n }\n\n if (!BrowserApp.#instance) {\n throw `No instance of BrowserApp`\n }\n\n return BrowserApp.#instance\n }\n\n /**\n * Constructs a BrowserApp instance with the app name. This should be\n * unique per origin. Spaces are _NOT_ allowed in this name.\n *\n * @param {string} appName a unique name for this app per origin.\n */\n constructor(appName) {\n this.#appName = appName\n this.#partyKey = `${appName}Party`\n this.#tokensKey = `${appName}Tokens`\n this.#appUrlKey = `${appName}AppUrl`\n }\n\n /**\n * Initialises the instance with the party name and tokens passed\n * in the #fragment and ?query string search parameters.\n *\n * - party is passed in the special parameter 'party'.\n * - tokens are passed in the remaining parameters keyed by host name.\n *\n * The #fragment starting at the first '\u00E6' (if present) is used and\n * any params whose name starts with the special character '\u00E6' are extracted.\n *\n * Any fragment prior to this is retained in the browser location.\n *\n * Any query params whose name starts with the special character '\u00E6' are\n * extracted.\n *\n * The window location is replaced with the 'clean' version that\n * no longer includes the tokens.\n *\n * @return {Promise<BrowserApp>} instance promise.\n * @throws {string} error if party token `src` does not match our window location.\n */\n async init() {\n const ourUrl = new URL(globalThis.location.href)\n\n const extractor = new ParamExtractor()\n const newHash = extractor.extractHash(ourUrl.hash)\n const newSearch = extractor.extractQuery(ourUrl.search)\n const daemonParams = extractor.getParams()\n\n if (daemonParams.has(PARTY_PARAM)) {\n sessionStorage[this.#tokensKey] = daemonParams.toString()\n const identityToken = this.getToken()\n await identityToken.verifySignatory()\n sessionStorage[this.#partyKey] = identityToken.getParty()\n\n const srcUrl = identityToken.getSourceUrl()\n if (srcUrl.origin !== ourUrl.origin || srcUrl.pathname !== ourUrl.pathname) {\n throw `Party token is for different app: ${srcUrl}`\n }\n }\n\n ourUrl.hash = newHash\n ourUrl.search = newSearch\n sessionStorage[this.#appUrlKey] = ourUrl.toString()\n globalThis.history.replaceState(null, '', ourUrl.toString())\n return this\n }\n\n /**\n * Returns true if this app is an orphan, i.e. has not been launched from\n * a D\u00E6mon shell.\n *\n * Otherwise returns false.\n *\n * @return {boolean} true if an orphan without D\u00E6mon, otherwise false.\n */\n isOrphan() {\n return (\n sessionStorage[this.#partyKey] === undefined ||\n sessionStorage[this.#tokensKey] === undefined\n )\n }\n\n /**\n * Returns the app name. This is normally used to disambiguate properties\n * of this app from others, such as those sharing a web origin and therefore\n * sharing localStorage or sessionStorage.\n *\n * Note that this is a client-side name not known to the tabserver.\n *\n * @return {string} the app name.\n */\n getAppName() {\n return this.#appName\n }\n\n /**\n * Returns the app url, obtained from window.location at time of app init,\n * or the empty string if not initialised.\n *\n * @return {string} the app url, or the empty string.\n */\n getAppUrl() {\n return sessionStorage[this.#appUrlKey]\n }\n\n /**\n * Returns the party hostname, being the D\u00E6mon that launched this app.\n *\n * @return {string} the party (D\u00E6mon) hostname.\n */\n getParty() {\n return sessionStorage[this.#partyKey]\n }\n\n /**\n * Returns the party origin, being the protocol (http: or https:)\n * and the party hostname.\n *\n * @return {string} the party origin.\n */\n getPartyOrigin() {\n return `${globalThis.location.protocol}//${this.getParty()}`\n }\n\n /**\n * Returns the default prefix associated with the app url if specified,\n * or this app if not. The default prefix is simply the app ID being a\n * short digest of the app URL, excluding any # fragment.\n *\n * @param {string | URL | null} appUrl for which the default prefix is required (optional).\n * @return {Promise<string>} the default prefix for the app.\n */\n getDefaultPrefix(appUrl = null) {\n if (!appUrl) {\n appUrl = this.getAppUrl()\n }\n\n const cleanUrl = new URL(appUrl)\n cleanUrl.hash = ''\n return getAppId(cleanUrl)\n }\n\n /**\n * Returns the likely prefix for this app.\n *\n * This is the value of the prefix parameter if present,\n * or the default prefix for the app if not.\n *\n * @return {Promise<string>} prefix obtained from prefix= param, or computed from the app URL.\n */\n getPrefix() {\n if (this.hasParam(PREFIX_PARAM)) {\n return Promise.resolve(this.getParam(PREFIX_PARAM) ?? '')\n }\n return this.getDefaultPrefix()\n }\n\n /**\n * Returns the likely url for the named tab on this app's agent.\n *\n * This will be wrong if the YAML file has a `prefix=` specified which\n * is not set to the value of a provided `prefix=` parameter.\n *\n * @param {string} tab name, e.g. 'v1'.\n * @returns {Promise<string>} the url of this tab on this app.\n */\n async getAgentUrl(tab) {\n const origin = this.getPartyOrigin()\n const prefix = await this.getPrefix()\n return `${origin}/tab/${prefix}/${tab}`\n }\n\n /**\n * Returns the base64-encoded token for the supplied party. If no\n * party is specified, returns the token for the launching party.\n *\n * @param {string} party the party, or the launching party if not specified.\n * @return {string} the base64-encoded token for the party.\n */\n getTokenBase64(party = PARTY_PARAM) {\n const tokens = sessionStorage[this.#tokensKey]\n const searchParams = new URLSearchParams(tokens)\n const tokenBase64 = searchParams.get(party)\n if (!tokenBase64) {\n throw `No token for ${party}, check 'audience' in YML`\n }\n return tokenBase64\n }\n\n\n /**\n * Gets the token for a party either as a base64-encoded string, or as a\n * Token object depending on the asBase64 argument.\n *\n * If the party is not passed or is null, the default is the launching party.\n * If the asBase64 is not passed, the default is true.\n *\n * @param {string=} party the audience for the token. Defaults to the identity token 'party'.\n * @return {Token} the pre-generated token for the given party.\n */\n getToken(party = PARTY_PARAM) {\n const tokenBase64 = this.getTokenBase64(party)\n return new Token(tokenBase64)\n }\n\n /**\n * Gets the value of the named parameter, or null if no value is supplied.\n *\n * The parameter name is case insensitive.\n *\n * The parameter is specified as:\n * - an attribute on the webdaemon meta tag, or\n * - a search parameter in the query string of the URL\n *\n * where the query string parameter takes priority if present.\n *\n * @param {string} name\n * @return {string | null} parameter value or null if not present.\n */\n getParam(name) {\n\n // Return a matching search parameter if present.\n const searchParams = new URL(globalThis.location.href).searchParams\n for (const [paramName, value] of searchParams) {\n if (name.toLowerCase() == paramName.toLowerCase()) {\n return value\n }\n }\n\n // Return meta element attribute if present, or null.\n const metaElement = document.querySelector('link[rel=webdaemon]')\n return metaElement?.getAttribute(name) ?? null\n }\n\n /**\n * Returns true if the named parameter is present, even if with empty\n * or null value.\n *\n * @param {string} name\n * @return {boolean} true if the parameter exists, otherwise false.\n */\n hasParam(name) {\n const searchParams = new URL(globalThis.location.href).searchParams\n if (searchParams.has(name)) {\n return true\n }\n\n const metaElement = document.querySelector('link[rel=webdaemon]')\n return metaElement?.hasAttribute(name) ?? false\n }\n\n /**\n * Returns the currently raised launch alert for this app,\n * or null if none.\n *\n * @typedef {import('./Alert.js').AlertEntry} AlertEntry\n *\n * @return { Promise<AlertEntry | null> } raised launch alert, or null if none.\n * @throws {string} exception if alert catalog cannot be retrieved.\n */\n async getLaunchAlert() {\n const origin = this.getPartyOrigin()\n const url = new URL(`${origin}/catalog`)\n url.searchParams.append('alert', '')\n const response = await fetch(url, {\n headers: {\n 'X-Tabserver-Token': this.getTokenBase64(),\n 'Accept': 'application/json'\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n const alert = json.ok.alert.find(\n /** @param {AlertEntry} alert */\n alert => (\n alert.type == 'LAUNCH' &&\n alert.sourceUrl == new URL(this.getAppUrl())\n )\n )\n return alert || null\n }\n\n /**\n * Resolves the currently raised launch alert for this app, if any.\n *\n * @throws {string} exception if alert cannot be resolved.\n */\n async resolveLaunchAlert() {\n const origin = this.getPartyOrigin()\n const url = new URL(`${origin}/resolve`)\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'X-Tabserver-Token': this.getTokenBase64(),\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n type: 'LAUNCH',\n source: this.getAppUrl()\n })\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n }\n\n /**\n * Resolves the currently raised launch alert for this app, if any.\n *\n * @throws {string} exception if alert cannot be resolved.\n */\n async rejectLaunchAlert() {\n const origin = this.getPartyOrigin()\n const url = new URL(`${origin}/reject`)\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'X-Tabserver-Token': this.getTokenBase64(),\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n type: 'LAUNCH',\n source: this.getAppUrl()\n })\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n }\n\n /**\n * Gets a Storage instance using the party token.\n *\n * @returns {Storage} instance.\n */\n getStorage() {\n return new Storage(this.getToken())\n }\n}\n", "import { BrowserApp } from './BrowserApp.js'\n\n/**\n * @import { JSONValue } from '../ts/Assertions.ts'\n */\n\n/**\n * Returns the currently raised launch alert for this app,\n * or null if none.\n *\n * @typedef {Object} AlertEntry\n * @property {URL} sourceUrl - The URL of the alert source.\n * @property {string} sourceTitle - The title of the alert source.\n * @property {string} type - The alert type.\n * @property {JSONValue} value - The value associated with the alert.\n * @property {Date} created - When the alert was created.\n * @property {Date|null} expiry - When the alert expires or null if it doesn't expire.\n *\n * @return { Promise<AlertEntry | null> } raised launch alert, or null if none.\n * @throws {string} exception if alert catalog cannot be retrieved.\n */\nexport async function getLaunchAlert() {\n const app = await BrowserApp.getInstance()\n const origin = app.getPartyOrigin()\n const url = new URL(`${origin}/catalog`)\n url.searchParams.append('alert', '')\n const response = await fetch(url, {\n headers: {\n 'X-Tabserver-Token': app.getTokenBase64(),\n 'Accept': 'application/json'\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n const alert = json.ok.alert.find(\n /** @param {AlertEntry} alert */\n alert => (\n alert.type == 'LAUNCH' &&\n alert.sourceUrl == new URL(app.getAppUrl())\n )\n )\n return alert || null\n}\n", "import { Token } from './Token.js'\n\n/**\n * This helper provides the parent capability for a website to\n * check and activate a daemon, and to create a device offer for\n * subsequent claim by consumer devices or browsers.\n *\n * The helper can operate either browser- or server-side.\n *\n * Usage\n * =====\n *\n * // Create the helper for a child daemon.\n * const helper = new ParentHelper()\n *\n * // Initialise with the child daemon being parented (aud), the\n * // source (src), the private key used to sign the token\n * // and the signatory url (iss).\n * await initWithKey(child, source, privateJwk, iss)\n *\n * // Succeeds if the child daemon is activated.\n * await helper.checkActivate()\n *\n * // Returns the claim code and check state of a new device offer.\n * await helper.fetchOffer()\n *\n * @typedef {Object} JsonWebKey\n * @typedef {string} TokenBase64\n *\n * @typedef {Object} OfferOptions\n * @property {'TRANSIENT' | 'PERMANENT'} [type] the type of the device.\n * @property {number} [ttl] the time-to-live of device (transient only), in seconds.\n * @property {number} [expiry] the number of seconds before the offer expires.\n * @property {Object} [payload] the optional offer payload.\n */\n\nexport class ParentHelper {\n /** @type {JsonWebKey | undefined} */\n #privateJwk // Signs the token used for activate and offer.\n\n /** @type {string | undefined} */\n #protocol // Protocol in use (http: or https:).\n\n /** @type {string | undefined} */\n #child // The host name of the daemon being parented.\n\n /** @type {string | undefined} */\n #iss // Callback-supplied URL for the pubicly visible issuer object.\n\n /** @type {string | undefined} */\n #src // The HTML source URL, which must match origin: header iff present in daemon request.\n\n /** @type {Token | undefined} */\n #token // The token used for check activate and offer calls to the daemon.\n\n /** @type {string | undefined} */\n #claimCode // The claim code returned by fetchOffer.\n\n /**\n * Initialises the helper with details already known to the caller.\n *\n * The private key is used to sign generated tokens, where the issUrl\n * references the public key used to verify the token.\n *\n * This method is used by tab runners wanting to parent daemons.\n *\n * @param {string} child\n * @param {string} src # Note can be party:control.\n * @param {JsonWebKey} privateJwk\n * @param {string} iss\n */\n async initWithKey(child, src, privateJwk, iss) {\n this.#protocol = src.startsWith('https:') ? 'https:' : 'http:'\n this.#child = child\n this.#src = src\n this.#privateJwk = privateJwk\n this.#iss = iss\n\n await this.#buildToken()\n }\n\n /**\n * Uses the token to check activate the daemon. An already\n * activated daemon with this parent is retained, or a new\n * daemon is created so long as the sub of the pre-generated\n * token matches a parent record in the provider.\n *\n * The `isNew` attribute in the ok result can be used to\n * determine if the daemon is newly activated or already\n * active.s\n *\n * @returns boolean true if newly activated, otherwise false.\n * @throws {string} exception if daemon cannot be activated.\n */\n async checkActivate() {\n if (!this.#token) {\n throw `checkActivate: No token`\n }\n const url = new URL(`${this.#token.getAud()}/activate`)\n const token = this.#token.asSignedBase64()\n const body = {}\n let response\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n 'x-tabserver-token': token,\n 'content-type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n\n const {\n ok,\n error\n } = await response.json()\n\n if (error) {\n throw error\n }\n\n if (ok.isNew) {\n return true\n }\n\n return false\n }\n catch (e) {\n console.error(e)\n throw `Cannot activate ${this.#token.getParty()}`\n }\n }\n\n /**\n * Uses the token to create a device offer on the daemon with default\n * type 'TRANSIENT' and ttl of 300s.\n *\n * @typedef {Object} Offer\n * @property {string} claimCode\n * @property {string} checkState\n *\n * @param {'NO_CHECK' | 'DO_CHECK'} flow to use. For QR offers, use checked flow.\n * @param {OfferOptions | null} options to use when making the offer, if any.\n * @return {Promise<Offer>} offer claimCode and checkState ('NO_CHECK' or 'AWAIT_CHECK').\n */\n async makeOffer(flow, options = null) {\n const {\n type = 'TRANSIENT',\n ttl = 300,\n expiry = 30,\n payload\n } = options ?? {}\n\n if (!this.#token) {\n throw `makeOffer: No token`\n }\n const url = new URL(`${this.#token.getAud()}/offer`)\n const token = this.#token.asSignedBase64()\n const body = {\n role: 'party',\n type,\n ttl,\n description: `Offered by ${this.#token.getCounterparty()}`,\n payload,\n expiry: new Date(Date.now() + 1000 * expiry),\n flow\n }\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'x-tabserver-token': token,\n 'content-type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n const {\n claimCode,\n _checkState\n } = json.ok\n\n this.#claimCode = claimCode\n\n return json.ok\n }\n\n /**\n * Returns the check state for the offer. This is used when\n * awaiting claim.\n *\n * @return {Promise<string>}\n */\n async checkState() {\n if (!this.#token || !this.#claimCode) {\n throw 'checkState: No token or claimCode'\n }\n const url = new URL(`${this.#token.getAud()}/offer/check`)\n url.searchParams.append('claimCode', this.#claimCode)\n const token = this.#token.asSignedBase64()\n const response = await fetch(url, {\n headers: {\n 'x-tabserver-token': token\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n /**\n * Result has `claimCode` and `checkState`.\n */\n return json.ok.checkState\n }\n\n /**\n * Posts the check code that confirms the offering device has\n * got the expected claiming device, and returns the resulting check state.\n *\n * @typedef {Object} ConfirmResult\n * @property {string} checkState\n *\n * @param {string} checkCode the check code obtained from the claiming device.\n * @return {Promise<ConfirmResult>} the check state following confirmation.\n */\n async confirmClaim(checkCode) {\n if (!this.#token || !this.#claimCode) {\n throw 'confirmClaim: No token or claimCode'\n }\n\n const url = new URL(`${this.#token.getAud()}/offer/confirm`)\n const token = await this.#token.asSignedBase64()\n const body = {\n claimCode: this.#claimCode,\n checkCode\n }\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'x-tabserver-token': token,\n 'content-type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n return json.ok\n }\n\n /**\n * Returns the daemon name.\n *\n * @return {string} daemon name, e.g. 'daemon.once.id'.\n */\n getChild() {\n if (!this.#child) {\n throw 'getChild: no child'\n }\n return this.#child\n }\n\n /**\n * Returns the daemon origin, suitable for redirection or claiming\n * a code.\n *\n * @return {string} child daemon origin e.g. 'https://daemon.once.id'.\n */\n getChildOrigin() {\n if (!this.#iss || !this.#child) {\n throw 'getDaemonUrl: no issUrl or daemon'\n }\n return `${this.#protocol}//${this.#child}`\n }\n\n /**\n * Builds the token used in the check activate and offer calls toh\n * the daemon.\n *\n * @return {Promise<void>}\n */\n async #buildToken() {\n if (!this.#iss || !this.#child || !this.#privateJwk) {\n throw 'buildToken: missing iss, child or privateJwk'\n }\n\n const iss = this.#iss\n const aud = this.getChildOrigin()\n const sub = new URL(this.#iss).origin\n\n // The `src` attribute is origin and pathname only.\n let src\n if (this.#src) {\n const srcUrl = new URL(this.#src)\n src = `${srcUrl.origin}${srcUrl.pathname}`\n }\n else {\n src = 'party:control'\n }\n\n const payload = {\n iss,\n aud,\n sub,\n src,\n scope: {\n 'party:control': 'offer'\n },\n iat: Date.now(),\n exp: Date.now() + 1000 * 60 * 3\n }\n\n const token = new Token(payload)\n await token.signWith(Promise.resolve(this.#privateJwk))\n this.#token = token\n }\n}\n", "/**\n * Utility class to generate a QR code img element which is placed\n * under a parent element.\n *\n * Relies upon a script element in the page:\n *\n * <script src='https://unpkg.com/qrcode-generator@1.4.4/qrcode.js'>\n */\nconst QrType = 0 // Auto detection by library.\nconst QrEcc = 'L'\n\nexport const ImgClass = 'qrcode'\n\nexport class QrCode {\n #img\n #content\n\n /**\n * Constructor takes img element and\n * the string to show in the QR code.\n * @param {HTMLImageElement} img the image element to use.\n * @param {string} content the content of the qr code.\n */\n constructor(img, content) {\n this.#img = img\n this.#content = content\n }\n\n /**\n * Returns a promise that is resolved when the image loads\n * successfully, or rejected if the image load fails.\n */\n generate() {\n const qr = qrcode(QrType, QrEcc)\n qr.addData(this.#content)\n qr.make()\n const dataUrl = qr.createDataURL()\n this.#img.classList.add(ImgClass)\n this.#img.src = dataUrl\n return new Promise((resolve, reject) => {\n this.#img.onload = resolve\n this.#img.onerror = reject\n })\n }\n}\n", "/**\n * Use this type to reference a plain object with\n * random string keys.\n */\nexport interface Plain {\n // deno-lint-ignore no-explicit-any\n [key: string]: any\n}\n\n/**\n * Any JSON-serialisable type.\n */\nexport type JSONPrimitive = string | number | boolean | null\nexport type JSONArray = JSONValue[]\nexport interface JSONObject {\n [key: string]: JSONValue\n}\nexport type JSONValue = JSONPrimitive | JSONObject | JSONArray\n\n\n/**\n * Type predicates to be used in type-narrowing assertions, e.g.\n *\n * assert(notNull(someValue))\n * ... now the type of someValue does not include null ...\n *\n * or\n * if (isOk(returnValue)) {\n * ...in this block we know returnValue.ok is present and of the correct type.\n * }\n *\n * @param value the value to be tested.\n * @returns true if not null, otherwise false.\n */\nexport function notNull<T>(value: T | null): value is T {\n return value !== null\n}\n\nexport function isDefined<T>(value: T | undefined): value is T {\n return value !== undefined\n}\n", "import { Plain } from './Assertions.ts'\n\nexport interface Ok<T> {\n ok: T\n}\n\nexport interface Error {\n error: string\n}\n\n/**\n * Returns an ok response or an error response corresponding\n * to the standard ok or error return value.\n *\n * If the error string starts with a space-separated status code,\n * then that is used as the status code for the response.\n *\n * For example:\n *\n * {error: 'Ordinary Error'}\n * is sent as is with status 200.\n * {error: '401 Authentication Error'}\n * is sent as {error: 'Authentication Error'} with status 401\n */\nexport function response<T>(rvalue: Ok<T> | Error, extraHeaders?: Plain) {\n\n if ('ok' in rvalue) {\n const body = JSON.stringify(rvalue)\n return new Response(body, {\n status: 200,\n headers: {\n ...extraHeaders,\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n }\n })\n }\n\n const error = rvalue.error\n const [, status, message] = error.match(/^(\\d{3})\\s(.+)/) ?? [null, '200', error]\n const body = JSON.stringify({\n error: message\n })\n return new Response(body, {\n status: Number(status),\n headers: {\n ...extraHeaders,\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*'\n }\n })\n}\n\n/**\n * Returns a 404 error response, including\n * the standard JSON error return value.\n * @deprecated Use response instead using a status-prefixed error message.\n */\nexport function response404<T>(json: Error = {\n error: '404 Not Found'\n}) {\n const body = JSON.stringify(json)\n return new Response(body, {\n status: 404,\n headers: {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n }\n })\n}\n\n/**\n * Returns a 302 permanent redirect response.\n * @param url the url to redirect to.\n * @returns Response object.\n */\nexport function response302(url: URL, extraHeaders: Plain = []): Response {\n return new Response(null, {\n status: 302,\n headers: {\n 'Location': url.toString(),\n 'Access-Control-Allow-Origin': '*',\n ...extraHeaders\n }\n })\n}\n\n/**\n * Simple build-a-cookie support.\n *\n * If the `expires` attribute is not defined, it's a session cookie.\n * Otherwise it's a permanent cookie. To delete a cookie, use the\n * `expires` attribute set to `new Date(0)`.\n */\n\nexport interface CookiePayload {\n name: string\n value: string\n domain?: string\n sameSite?: 'Strict' | 'Lax' | 'None'\n path?: string\n expires?: Date\n secure?: boolean\n httpOnly?: boolean\n}\n\nexport type CookieString = string\n\nexport function buildCookie(payload: CookiePayload): CookieString {\n const {\n name,\n value,\n domain,\n sameSite,\n path = '/',\n expires,\n secure = true,\n httpOnly = true\n } = payload\n\n const builder: string[] = []\n builder.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`)\n if (domain) builder.push(`Domain=${domain}`)\n if (sameSite) builder.push(`SameSite=${sameSite}`)\n builder.push(`Path=${path}`)\n if (expires) builder.push(`Expires=${expires.toUTCString()}`)\n if (secure) builder.push(`Secure`)\n if (httpOnly) builder.push('HttpOnly')\n\n const cookie = builder.join('; ')\n return cookie\n}\n", "import { response } from './Responses.ts'\nimport { Plain, JSONValue } from './Assertions.ts'\nimport { Token, Scope } from '../js/Token.js'\nimport { Storage } from '../js/Storage.js'\n\n// Daemon configuration looks like this.\nexport interface DaemonConfig {\n system: {\n protocol: 'http:' | 'https:'\n party: string\n issuer: string\n source: string\n sourcePrefix: string\n tab: string,\n privateJwk: JsonWebKey\n }\n user: Plain\n}\n\n// System requests are on the /daemon/... path.\nexport const DAEMON_PREFIX = 'daemon'\n\n// Daemon lifecycle path components that come after DAEMON_PREFIX.\nexport const CONFIG_PATH = 'config'\nexport const EVENT_PATH = 'event'\n\n// Event names.\nconst Ev = {\n Config: 'config'\n}\n\n// SessionStorage keys for lifecycle objects.\nconst CONFIG_KEY = 'config'\n\n// Default time-to-live for a third party token we generate is 1 hour.\nconst DEFAULT_TTL_MILLIS = 1000 * 60 * 60\n\ninterface TabEvent {\n type: string,\n payload: JSONValue\n}\n\n/**\n * Encapsulates the lifecycle event requests that occur on\n * runner start and termination and when alerts are resolved\n * or rejected.\n *\n * The Lifecycle object also provides static methods to retrieve\n * configuration items, a public and private key pair, and also to\n * produce the signed token that is required in requests from this party\n * to third parties.\n */\nexport class Lifecycle extends EventTarget {\n static #instance: Lifecycle = new Lifecycle()\n\n // Expose event names to importers.\n static Ev = Ev\n\n static getInstance() {\n return this.#instance\n }\n\n /**\n * All system requests are under the /daemon/ path.\n *\n * @param {Request} request to be tested.\n * @returns {boolean} true if the request is a lifecycle request.\n */\n static shouldHandle(request: Request): boolean {\n return (\n Lifecycle.isConfig(request) ||\n Lifecycle.isEvent(request)\n )\n }\n\n static isConfig(request: Request): boolean {\n const url = new URL(request.url)\n return (\n request.method == 'POST' &&\n url.pathname == `/${DAEMON_PREFIX}/${CONFIG_PATH}` &&\n request.headers.get('Content-Type') == 'application/json'\n )\n }\n\n static isEvent(request: Request): boolean {\n const url = new URL(request.url)\n return (\n request.method == 'POST' &&\n url.pathname == `/${DAEMON_PREFIX}/${EVENT_PATH}` &&\n request.headers.get('Content-Type') == 'application/json'\n )\n }\n\n /**\n * Handles a lifecycle request.\n *\n * @param {Request} request to be handled.\n * @returns {Response} response for caller.\n */\n async handler(request: Request): Promise<Response> {\n if (Lifecycle.isConfig(request)) {\n return await this.handleConfig(request)\n }\n if (Lifecycle.isEvent(request)) {\n return await this.handleEvent(request)\n }\n return response({\n error: 'Invalid daemon request'\n })\n }\n\n /**\n * Saves the lifecycle config object provided in the request body.\n *\n * Fires a 'config' lifecycle event.\n *\n * @param {Request} request containing the configuration json.\n * @return {Response} ok response.\n */\n async handleConfig(request: Request): Promise<Response> {\n const configJson = await request.json()\n sessionStorage.setItem(CONFIG_KEY, JSON.stringify(configJson))\n this.dispatchEvent(new CustomEvent(Ev.Config, {\n detail: configJson\n }))\n return response({\n ok: true\n })\n }\n\n /**\n * Fires a lifecycle event upon receiving an event request.\n *\n * @param {Request} request containing the event.\n * @return {Response} ok response.\n */\n async handleEvent(request: Request): Promise<Response> {\n const {\n type,\n payload\n } = await request.json()\n this.dispatchEvent(new CustomEvent(type, {\n detail: payload\n }))\n return response({\n ok: true\n })\n }\n\n /**\n * Returns the configuration object stored on initialisation.\n * @return {DaemonConfig} the daemon configuration object.\n * @throws {string} exception if config is not yet initialised.\n */\n static getConfig(): DaemonConfig {\n const json = sessionStorage.getItem(CONFIG_KEY)\n if (!json) {\n throw 'DaemonConfig not ready'\n }\n return JSON.parse(json)\n }\n\n /**\n * Generates if necessary and returns a private key for\n * use in signing tokens.\n *\n * @returns {JSONWebKey} the private key for this session.\n */\n static getPrivateKey(): Promise<JsonWebKey> {\n const {\n system: {\n privateJwk\n }\n } = Lifecycle.getConfig()\n return Promise.resolve(privateJwk)\n }\n\n /**\n * Returns a token suitable for the `x-tabserver-token` header\n * in a request to the supplied party with the required\n * scope.\n *\n * If party is omitted, it defaults to this party.\n * If\n *\n * @param {Scope} scope map of string source -> space-separated capabilities.\n * @param {string=} party the party host name to whom the request will be sent.\n * @param { src=} src the HTML page from which the request is (actually or notionally) made.\n * @param {number=} ttlMillis the lifetime of this token.\n * @returns {Token} the signed token.\n * @throws {string} if configuration not yet initialised.\n *\n */\n static async getTokenFor(\n scope: Scope,\n party: string | null = null,\n src: string | null = null,\n ttlMillis: number = DEFAULT_TTL_MILLIS\n ): Promise<Token> {\n const config = Lifecycle.getConfig()\n if (!config) {\n throw 'Lifecycle configuration not yet available'\n }\n\n // Extract protocol, our party, issuer and source from system config.\n const {\n system: {\n protocol,\n party: us,\n issuer,\n source\n }\n } = config\n\n // Token src excludes query params.\n const appSourceUrl = new URL(source)\n\n const aud = party ? `${protocol}//${party}`: `${protocol}//${us}`\n const sub = `${protocol}//${us}`\n const now = Date.now()\n\n const payload = {\n iss: issuer,\n aud,\n sub,\n src: src ?? `${appSourceUrl.origin}${appSourceUrl.pathname}`,\n scope,\n iat: now,\n exp: now + ttlMillis\n }\n\n const token = new Token(payload)\n await token.signWith(Lifecycle.getPrivateKey())\n return token\n }\n\n static async getStorage(): Promise<Storage> {\n const token = await Lifecycle.getStorageToken()\n return new Storage(token)\n }\n\n /**\n * Returns a token for our party with the setitem and getitem\n * capabilities on the party control source.\n */\n static getStorageToken(): Promise<Token> {\n const {\n system: {\n party,\n }\n } = Lifecycle.getConfig()\n\n const scope = {\n [Token.Source.PARTY_CONTROL]: `${Token.Capability.GET_ITEM} ${Token.Capability.SET_ITEM}`\n }\n\n return Lifecycle.getTokenFor(scope, party)\n }\n}\n", "import { Plain } from \"./Assertions.ts\";\n\n// Standard HTTP headers.\nconst X_FORWARDED_HOST = 'x-forwarded-host'\nconst X_FORWARDED_PROTO = 'x-forwarded-proto'\n\n// Non-standard HTTP header used by tabserver.\nconst X_FORWARDED_PATHNAME = 'x-forwarded-pathname'\n\n/**\n * Returns the request protocol. This is the first of the following:\n *\n * 1. The `x-forwarded-proto` header, if present, with trailing colon.\n * 2. The request URL protocol, ditto.\n *\n * @param request the request.\n * @returns {string} the protocol with trailing colon.\n */\nexport function getClientProtocol(request: Request): string {\n if (request.headers.has(X_FORWARDED_PROTO)) {\n return (request.headers.get(X_FORWARDED_PROTO) as string) + ':'\n }\n\n return new URL(request.url).protocol\n}\n\n/**\n * Returns the request host name. This is the first of the following:\n *\n * 1. The `x-forwarded-host` header, if present.\n * 2. The request URL hostname (including port, if present)\n *\n * @param request the request.\n * @returns the protocol.\n */\nexport function getClientHost(request: Request): string {\n if (request.headers.has(X_FORWARDED_HOST)) {\n return request.headers.get(X_FORWARDED_HOST) || ''\n }\n\n return new URL(request.url).host\n}\n\n/**\n * Returns the request pathname. This is the first of the following:\n *\n * 1. The `x-forwarded-pathname` header, if present.\n * 2. The request URL pathname.\n */\nexport function getClientPathname(request: Request): string {\n if (request.headers.has(X_FORWARDED_PATHNAME)) {\n return request.headers.get(X_FORWARDED_PATHNAME) || ''\n }\n\n return new URL(request.url).pathname\n}\n\n/**\n * Returns the client request protocol, host and pathname components\n * only.\n *\n * @param request the request.\n * @return {URL} the protocol://host/pathname\n */\nexport function getClientUrlPhp(request: Request): URL {\n const protocol = getClientProtocol(request)\n const host = getClientHost(request)\n const pathname = getClientPathname(request)\n return new URL(`${protocol}//${host}${pathname}`)\n}\n\n/**\n * Returns the URL as requested by the client taking account\n * of the x-forwarded protocol and host.\n *\n * @param request the request.\n * @returns the URL.\n */\nexport function getClientUrl(request: Request): URL {\n const originalUrl = new URL(request.url)\n const newUrl = getClientUrlPhp(request)\n newUrl.search = originalUrl.search\n newUrl.hash = originalUrl.hash\n return newUrl\n}\n\n/**\n * Returns the pathname portion of the request URL, excluding\n * query and fragment components.\n *\n * @param {request} the request.\n * @returns {string} the pathname which starts with a forward slash.\n */\nexport function getPathname(request: Request): string {\n const url = new URL(request.url)\n return url.pathname\n}\n\n/**\n * Returns the string value of a named cookie, or null if it is not\n * present in the request.\n *\n * @param {Request} request the request.\n * @param {string} cookieName the name of the cookie\n * @returns {string} the cookie value, or null if not present.\n */\nexport function getCookie(request: Request, cookieName: string): string | null {\n const cookies = getCookies(request)\n if (cookieName in cookies) {\n return cookies[cookieName]\n }\n return null\n}\n\n/**\n * Returns a map of request cookie names to values, which can be empty.\n *\n * @param {Request} request the request.\n * @returns {Plain} the plain object with zero or more cookie keys.\n */\nexport function getCookies(request: Request): Plain {\n const cookies: Plain = {}\n const headerValue = request.headers.get('Cookie')\n if (headerValue) {\n for (const keyVal of headerValue.split('; ')) {\n const [key, value] = keyVal.split('=')\n cookies[key] = value\n }\n }\n return cookies\n}\n"],
5
- "mappings": "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",
6
- "names": ["shortSafeDigest", "message", "length", "msgUint8", "hashBuffer", "hashArray", "byteString", "base64Safe", "shortHexDigest", "hashHex", "b", "getAppId", "url", "DEFAULT_ALGORITHM", "KeyPair", "#algorithm", "#keypair", "algorithm", "publicKey", "privateKey", "spkiKey", "pkcs8Key", "ALGORITHM", "MATCH_ORIGIN", "TOKEN_IAT_LEEWAY_MILLIS", "Token", "_Token", "#payload", "#signatureBase64", "#audUrl", "#subUrl", "#srcUrl", "#signatory", "source", "payload", "iss", "aud", "sub", "src", "scope", "iat", "exp", "privateJwkPromise", "privateJwk", "privateKey", "toSign", "messageBuffer", "bufferSource", "signature", "signatureBase64", "issuer", "json", "publicJwkPromise", "publicJwk", "publicKey", "toCheck", "sigBuffer", "sources", "capability", "now", "bytes", "binString", "x", "base64", "m", "request", "tokenBase64", "tokenSet", "searchParams", "key", "KEY_PATTERN", "KEYLIKE_PATTERN", "Storage", "#token", "token", "key", "getItem", "keylike", "getItemsLike", "value", "setItem", "removeItem", "assertValid", "assertValidLike", "serialise", "body", "url", "Char", "Encoded", "ParamExtractor", "hash", "i", "params", "query", "keys", "key", "PARTY_PARAM", "PREFIX_PARAM", "BrowserApp", "_BrowserApp", "#instance", "#appName", "#partyKey", "#tokensKey", "#appUrlKey", "appName", "ourUrl", "extractor", "ParamExtractor", "newHash", "newSearch", "daemonParams", "identityToken", "srcUrl", "appUrl", "cleanUrl", "getAppId", "tab", "origin", "prefix", "party", "tokens", "tokenBase64", "Token", "name", "searchParams", "paramName", "value", "url", "json", "alert", "Storage", "getLaunchAlert", "app", "BrowserApp", "origin", "url", "json", "alert", "ParentHelper", "#privateJwk", "#protocol", "#child", "#iss", "#src", "#token", "#claimCode", "child", "src", "privateJwk", "iss", "#buildToken", "url", "token", "body", "response", "ok", "error", "e", "flow", "options", "type", "ttl", "expiry", "payload", "json", "claimCode", "_checkState", "checkCode", "aud", "sub", "srcUrl", "Token", "QrEcc", "ImgClass", "QrCode", "#img", "#content", "img", "content", "qr", "dataUrl", "resolve", "reject", "notNull", "value", "isDefined", "response", "rvalue", "extraHeaders", "body", "error", "status", "message", "response404", "json", "response302", "url", "buildCookie", "payload", "name", "value", "domain", "sameSite", "path", "expires", "secure", "httpOnly", "builder", "DAEMON_PREFIX", "CONFIG_PATH", "EVENT_PATH", "Ev", "CONFIG_KEY", "DEFAULT_TTL_MILLIS", "Lifecycle", "_Lifecycle", "#instance", "request", "url", "response", "configJson", "type", "payload", "json", "privateJwk", "scope", "party", "src", "ttlMillis", "config", "protocol", "us", "issuer", "source", "appSourceUrl", "aud", "sub", "now", "token", "Token", "Storage", "X_FORWARDED_HOST", "X_FORWARDED_PROTO", "X_FORWARDED_PATHNAME", "getClientProtocol", "request", "getClientHost", "getClientPathname", "getClientUrlPhp", "protocol", "host", "pathname", "getClientUrl", "originalUrl", "newUrl", "getPathname", "getCookie", "cookieName", "cookies", "getCookies", "headerValue", "keyVal", "key", "value"]
4
+ "sourcesContent": ["/**\n * Generates a SHA-1 digest of the supplied string, and returns\n * it as a base64 string optionally truncated to the first 'length'\n * characters.\n *\n * @param {string} message to digest.\n * @return {Promise<string>} base64-encoded digest.\n */\nexport async function shortSafeDigest(message, length = 0) {\n const msgUint8 = new TextEncoder().encode(message)\n if (!crypto.subtle) {\n throw 'Requires secure origin (localhost or https:)'\n }\n const hashBuffer = await crypto.subtle.digest(\"SHA-1\", msgUint8)\n const hashArray = new Uint8Array(hashBuffer)\n const byteString = String.fromCodePoint(...hashArray)\n const base64 = btoa(byteString)\n const base64Safe = base64\n .replaceAll('+','-')\n .replaceAll('/','_')\n .replaceAll('=','')\n return length ? base64Safe.substring(0, length) : base64Safe\n}\n\n/**\n * Generates a SHA-1 digest of the supplied string, and returns\n * it as a hex string optionally truncated to the first 'length'\n * characters.\n *\n * @param {string} message to digest.\n * @return {Promise<string>} hex-encoded digest.\n */\nexport async function shortHexDigest(message, length = 0) {\n const msgUint8 = new TextEncoder().encode(message)\n if (!crypto.subtle) {\n throw 'Requires secure origin (localhost or https:)'\n }\n const hashBuffer = await crypto.subtle.digest(\"SHA-1\", msgUint8)\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n const hashHex = hashArray\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n\n return length ? hashHex.substring(0, length) : hashHex\n}\n\n/**\n * Returns the standard app id for the supplied app URL.\n *\n * This is the shortSafeDigest of the URL truncated to 8 base64 chars,\n * i.e. distributed over a 48-bit space which seems reasonable for the\n * number of apps on a given daemon.\n *\n * @param {string | URL} url of the app whose id is to be computed.s\n * @return {Promise<string>} default id for the app, commonly used in prefixes.\n */\nexport async function getAppId(url) {\n const appId = await shortSafeDigest(String(url), 8)\n return appId\n}\n", "const DEFAULT_ALGORITHM = {\n name: 'RSASSA-PKCS1-v1_5',\n modulusLength: 2048,\n publicExponent: new Uint8Array([0x01, 0x00, 0x01]),\n hash: 'SHA-256'\n}\n\nexport class KeyPair {\n #algorithm\n\n /** @type {CryptoKeyPair | undefined} */\n #keypair\n\n constructor(algorithm = DEFAULT_ALGORITHM) {\n this.#algorithm = algorithm\n if (!crypto.subtle) {\n throw 'Crypto.subtle requires secure environment'\n }\n }\n\n async generate() {\n this.#keypair = await crypto.subtle.generateKey(\n this.#algorithm,\n true,\n ['sign', 'verify']\n )\n }\n\n async publicJwk() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const publicKey = this.#keypair.publicKey\n const publicJwk = await crypto.subtle.exportKey('jwk', publicKey)\n return publicJwk\n }\n\n async privateJwk() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const privateKey = this.#keypair.privateKey\n const privateJwk = await crypto.subtle.exportKey('jwk', privateKey)\n return privateJwk\n }\n\n async publicPem() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const publicKey = this.#keypair.publicKey\n const spkiKey = await crypto.subtle.exportKey('spki', publicKey)\n const base64Key = btoa(String.fromCharCode(...new Uint8Array(spkiKey)))\n const base64KeyLines = base64Key.match(/.{1,64}/g)?.join('\\n') ?? ''\n const publicPem = `-----BEGIN PUBLIC KEY-----\\n${base64KeyLines}\\n-----END PUBLIC KEY-----`\n return publicPem\n }\n\n async privatePem() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const privateKey = this.#keypair.privateKey\n const pkcs8Key = await crypto.subtle.exportKey('pkcs8', privateKey)\n const base64Key = btoa(String.fromCharCode(...new Uint8Array(pkcs8Key)))\n const base64KeyLines = base64Key.match(/.{1,64}/g)?.join('\\n') ?? ''\n const privatePem = `-----BEGIN PRIVATE KEY-----\\n${base64KeyLines}\\n-----END PRIVATE KEY-----`\n return privatePem\n }\n}\n", "/**\n * Encapsulates the functionality associated with a bearer token normally\n * passed in a request `x-tabserver-token` header as a base64-encoded\n * JSON object.\n *\n * Note the type definition annotations which are helpful when using this\n * Javascript class in Typescript.\n *\n * Example token:\n *\n * {\n * \"iss\": \"http://foo.daemon/some/public.jwk\",\n * \"aud\": \"https://bar.daemon\",\n * \"sub\": \"https://foo.daemon\",\n * \"src\": \"https://some.com\",\n * \"scope\": {\n * \"http://localhost:9000/helloWorld1.html\": \"read write\",\n * \"party:control\": \"grant revoke\"\n * }\n * \"iat\": 1698576567000,\n * \"exp\": 1698576344000,\n * \"sig\": \"long base64 string\"\n * }\n *\n * Generally:\n * iss - the issuer URL references the public JWK used to verify the sig.\n * aud - the party handling the request, expressed as an origin.\n * sub - the counterparty making the request, expressed as an origin.\n * src - the source HTML file making the request, protocol://host/path only.\n * scope - a map of requested source -> scope pairs.\n * A scope is a space-separated list of capabilities.\n * iat - issued at time in absolute millis.\n * exp - expiry time, ditto.\n * sig - the base64 signature, verified by the JWK pointed to by iss.\n *\n * Normally the sub field must match the host portion of the iss URL. But\n * the possiblity remains open for an intermediary trusted by the party to\n * hold the public keys of counterparties.\n */\n\n/**\n * @typedef {Object.<string, string>} Scope\n */\n\n/**\n * @typedef TokenPayload\n * @property {string} iss // URL of public key of counterparty making request.\n * @property {string} aud // Party origin handling the request.\n * @property {string} sub // Counterparty origin making the request.\n * @property {string} src // Source HTML document context of the request excluding fragment and query.\n * @property {Scope} scope // Map of scope (space-separated capabilities) keyed by source URL.\n * @property {number} iat // Issued at time.\n * @property {number} exp // Expiry time.\n * @property {string} sig // Signature if signed.\n */\n\n/**\n * @typedef {Object} Signatory the object to be served on the iss URL.\n * @property {string?} src which is 'party:control' if the shell, or source URL if a runner.\n * @property {JsonWebKey} jwk\n */\n\n/**\n * @typedef TokenSet\n * @type {Object<string, Token>}\n */\n\n/** Must match algorithm in Keypair.js */\nconst ALGORITHM = {\n name: 'RSASSA-PKCS1-v1_5',\n hash: 'SHA-256'\n}\n\n/** Matches an origin with protocol and no path. */\nconst MATCH_ORIGIN = /https?:\\/\\/[^\\/]+$/\n\n/**\n * Allow a 30s leeway to allow for clock difference when checking\n * token issued at time. We allow our clock to be up to 30s behind\n * the system that generated a token.\n */\nconst TOKEN_IAT_LEEWAY_MILLIS = 1000 * 30\n\nexport class Token {\n\n // Standard sources.\n static Source = {\n PARTY_CONTROL: 'party:control' // Party control subject, a pseudo-source with scope granted to counterparties.\n }\n\n // Standard counterparty pattern.\n static Counterparty = {\n PUBLIC: 'public' // Public counterparty, matching requests with no host specified in sub.\n }\n\n // Standard scope labels.\n static Capability = {\n OPEN: 'open', // Capability to open source files and start tab runners.\n CLOSE: 'close', // Capability to close source files and stop tab runners.\n GRANT: 'grant', // Capability to create relation records.\n REVOKE: 'revoke', // Capability to delete relation records.\n OFFER: 'offer', // Capability to create a party offer for a device to claim.\n RETRACT: 'retract', // Capability to delete a party offer before it is claimed.\n DELETE: 'delete', // Capability to delete devices.\n CATALOG: 'catalog', // Capability to get sources, devices, alerts etc.\n ALIAS: 'alias', // Capability to set the alias for a counterparty host.\n UNALIAS: 'unalias', // Capability to unset the alias for a counterparty host.\n GET_ITEM: 'getitem', // Capability to get a storage item.\n SET_ITEM: 'setitem', // Capability to set (and remove) a storage item.\n ALERT: 'alert', // Capability to post and delete alerts.\n LOG: 'log', // Capability to view logs.\n READ: 'read', // Capability to read from drive.\n WRITE: 'write' // Capability to write to drive.\n }\n\n static DEFAULT_EXPIRY_MILLIS = 1000 * 60 * 60 * 24\n static TOKEN_HEADER = 'x-tabserver-token'\n static SCOPE_SEPARATOR = /[\\s,;\\|]+/ // Specification per String.split() function.\n\n /** @type{TokenPayload | null} */\n #payload = null\n\n /** @type{string | null} */\n #signatureBase64 = null\n\n /** @type{URL} */\n #audUrl\n\n /** @type{URL} */\n #subUrl\n\n /** @type{URL} */\n #srcUrl\n\n /** @type {Signatory | undefined} */\n #signatory // Set upon successful verification of signature.\n\n\n /**\n * Constructor takes either a signed base64 token, or a payload object.\n *\n * If successful, the payload and optionally the signature fields are\n * populated.\n *\n * @param {object | string} source a payload, or a base64 token\n */\n constructor(source) {\n let payload = null\n\n if (typeof source == 'object') {\n payload = source\n }\n else if (typeof source == 'string') {\n try {\n payload = JSON.parse(atob(source))\n }\n catch (_e) {\n throw 'Invalid token format'\n }\n }\n else {\n throw `Cannot construct token from ${typeof source}`\n }\n\n // Separate out the signature from the payload.\n this.#signatureBase64 = payload.sig\n delete payload.sig\n this.#payload = payload\n\n const {\n iss,\n aud,\n sub,\n src,\n scope,\n iat,\n exp\n } = payload\n\n // Check payload is complete.\n if ((!iss || !aud || !sub || !src || !scope || !iat || !exp)) {\n throw 'Token must include iss, aud, sub, src, scope, iat and exp'\n }\n\n // Check aud, sub and src fields are origins (i.e. proto://host.name)\n if (\n !aud.match(MATCH_ORIGIN) ||\n !sub.match(MATCH_ORIGIN)\n ) {\n throw 'The aud and sub attributes must be origins'\n }\n\n this.#audUrl = new URL(aud)\n this.#subUrl = new URL(sub)\n this.#srcUrl = new URL(src)\n\n // The `src` attribute must not have query or fragment.\n if (this.#srcUrl.search || this.#srcUrl.hash) {\n throw 'The src attribute must have no query or fragment component.'\n }\n\n this.#payload = payload\n }\n\n /**\n * Generates the signature for the object using the private key provided by the\n * supplied callback and stores the result.\n *\n * @param {Promise<JsonWebKey>} privateJwkPromise the private key used for signing.\n * @return {Promise<string>} resolved with the base64 signature if signed successfully.\n */\n async signWith(privateJwkPromise) {\n if (this.#payload == null) {\n throw 'No payload to sign'\n }\n\n const privateJwk = await privateJwkPromise\n const privateKey = await crypto.subtle.importKey('jwk', privateJwk, ALGORITHM, true, ['sign'])\n const toSign = JSON.stringify(this.#payload)\n const messageBuffer = new TextEncoder().encode(toSign).buffer\n const bufferSource = new Uint8Array(messageBuffer)\n const signature = await crypto.subtle.sign(ALGORITHM, privateKey, bufferSource)\n const signatureBase64 = Token.bytesToBase64(new Uint8Array(signature))\n this.#signatureBase64 = signatureBase64\n return signatureBase64\n }\n\n /**\n * Retrieves the signatory using the `iss` field, checks the signature\n * and if valid sets the signatory property in this instance.\n *\n * @throws {string} if signatory cannot be retrieved or signature doesn't match.\n */\n async verifySignatory() {\n await this.fetchSignatory()\n if (!this.#signatory) {\n throw 'verifySignatory: no signatory'\n }\n await this.checkSignature(Promise.resolve(this.#signatory.jwk))\n }\n\n /**\n * Fetches the signatory from the `iss` URL, setting the instance\n * property.\n *\n * The signatory comprises the `src` representing the app that has\n * signed the token and the public `jwk`.\n *\n * Caching may be introduced to reduce latency and bandwidth use.\n *\n * @throws {string} error if fetch fails or returns an error response.\n */\n async fetchSignatory() {\n const issuer = this.getIssuer()\n try {\n const response = await fetch(issuer, {\n headers: {\n 'content-type': 'application/json'\n }\n })\n\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n this.#signatory = json.ok\n }\n catch (_e) {\n throw `Failed to read signatory at ${issuer}`\n }\n }\n\n /**\n * Checks the signature using the public key provided by the supplied callback,\n * and stores the result.\n *\n * @param {Promise<JsonWebKey>} publicJwkPromise the public key used to verify the signature.\n * @throws {string} exception if signature cannot be verified or the public key is null or error.\n */\n async checkSignature(publicJwkPromise) {\n if (!this.#payload || !this.#signatureBase64) {\n throw 'checkSignature: no payload or signatureBase64'\n }\n\n const publicJwk = await publicJwkPromise\n if (!publicJwk || 'error' in publicJwk) {\n throw `Missing or invalid public JWK at issuer ${this.getIssuer()}`\n }\n const publicKey = await crypto.subtle.importKey('jwk', publicJwk, ALGORITHM, true, ['verify'])\n const toCheck = JSON.stringify(this.#payload)\n const messageBuffer = new TextEncoder().encode(toCheck)\n const sigBuffer = Token.base64ToBytes(this.#signatureBase64)\n const isVerified = await crypto.subtle.verify(ALGORITHM, publicKey, sigBuffer, messageBuffer)\n if (!isVerified) {\n throw `Signature rejected by issuer ${this.getIssuer()}`\n }\n }\n\n /**\n * Returns the payload with the `sig` property added.\n *\n * @return {TokenPayload} the payload with `sig` property.\n */\n getSignedPayload() {\n if (!this.#signatureBase64 || !this.#payload) {\n throw 'getSignedPayload: no signatureBase64 or payload'\n }\n\n return {\n ...this.#payload,\n sig: this.#signatureBase64\n }\n }\n\n /**\n * Returns the payload whether signed or not.\n *\n * @return {TokenPayload} the payload object without signature.\n */\n getPayload() {\n if (!this.#payload) {\n throw 'getPayload: no payload'\n }\n return this.#payload\n }\n\n /**\n * Returns the `aud` field as a string.\n *\n * @return {string} the `aud` field value.\n */\n getAud() {\n return this.#audUrl.origin\n }\n\n\n /**\n * Returns the `aud` host which is the party name handling the request.\n *\n * @return {string} the party name.\n */\n getParty() {\n return this.#audUrl.host\n }\n\n /**\n * Returns the `sub` field as a string.\n *\n * @return {string} the `sub` field value.\n */\n getSub() {\n return this.#subUrl.origin\n }\n\n /**\n * Returns the `src` field as a string.\n *\n * @return {string} the `src` field value.\n */\n getSrc() {\n return String(this.#srcUrl)\n }\n\n /**\n * Returns the `sub` host, which is the counterparty name making the\n * request.\n *\n * @return {string} the counterparty name.\n */\n getCounterparty() {\n return this.#subUrl.host\n }\n\n /**\n * Returns the source of the signatory who signed on behalf of the\n * counterparty. This is only available once the token is\n * verified, as it is provided by the `iss` endpoint.\n *\n * @returns {string} source of the verified signatory.\n */\n getSignatorySrc() {\n if (!this.#signatory) {\n throw 'getSignatorySrc: no signatory'\n }\n\n // For backward compatibility, a missing `src` is\n // substituted.\n // https://github.com/sparkl/tabserver/issues/161\n return this.#signatory?.src ?? String(this.#srcUrl)\n }\n\n /**\n * Returns the `src` URL.\n *\n * @return {URL} the source URL.\n */\n getSourceUrl() {\n return this.#srcUrl\n }\n\n /**\n * Returns the `issuer` field as a URL. The counterparty is inferred from\n * the host name which generally matches the sub field.\n *\n * @return {URL} the issuer URL.\n */\n getIssuer() {\n return new URL(this.getPayload().iss)\n }\n\n /**\n * Returns the list of zero or more sources for which scope is requested.\n *\n * @return {string[]} a list of source URL strings.\n */\n getSources() {\n if (!this.#payload) {\n throw 'getSources: no payload'\n }\n\n const sources = []\n for (const source in this.#payload.scope) {\n sources.push(source)\n }\n return sources\n }\n\n /**\n * Returns the scope for the source as an array of capability tokens.\n *\n * These can be separated by any mix of space, comma, semicolon\n * or vertical bar.\n *\n * @param {string} source whose capabilities are returned.\n * @return {string[]} zero or more scope tokens.\n */\n getCapabilities(source) {\n if (!this.#payload) {\n throw 'getCapabilities: no payload'\n }\n\n const scope = this.#payload.scope\n if (! (source in scope)) {\n throw `Source '${source}' not in scope`\n }\n\n return scope[source].split(Token.SCOPE_SEPARATOR)\n }\n\n /**\n * Returns true if the supplied capability is present in the token\n * scope under the specified source, otherwise false.\n *\n * @param {string} source the source under which the capability is expected.\n * @param {string} capability the capability being tested.\n * @return {boolean} true if capability is present in the scope, otherwise false.\n */\n hasCapability(source, capability) {\n return this.getCapabilities(source).includes(capability)\n }\n\n /**\n * Checks the token is within the period defined by the `iat` and `exp`\n * date fields.\n *\n * @throws {string} exception if not within valid period.\n */\n checkPeriod() {\n if (!this.#payload) {\n throw 'checkPeriod: no payload'\n }\n\n const {\n iat,\n exp\n } = this.#payload\n\n const now = Date.now()\n if (now < iat - TOKEN_IAT_LEEWAY_MILLIS) {\n throw 'Token is not yet valid'\n }\n\n if (now > exp) {\n throw 'Token has expired'\n }\n }\n\n /**\n * Returns the signed object including the `sig` property as a base64 string.\n *\n * @return {string} the signed object as a base64 string.\n */\n asSignedBase64() {\n return btoa(JSON.stringify(this.getSignedPayload()))\n }\n\n /**\n * Returns a base64 JSON string corresponding to the provided byte array.\n *\n * @param {Uint8Array} bytes to be encoded as a base64 string.\n * @returns {string} the encoded bytes.\n */\n static bytesToBase64(bytes) {\n const binString = Array.from(bytes, (x) => String.fromCodePoint(x)).join('');\n return btoa(binString);\n }\n\n /**\n * Returns the byte array decoded from the base64 string.\n *\n * @param {string} base64\n * @returns {Uint8Array} the decoded bytes.\n */\n static base64ToBytes(base64) {\n const binString = atob(base64);\n return Uint8Array.from(binString, (m) => m.codePointAt(0) ?? 0);\n }\n\n /**\n * Returns a new token using the x-tabserver-token header on the\n * request.\n *\n * @param {Request} request the request.\n * @return {Token | null} the token, or null if no header is present.\n * @throws {string} exception if token cannot be constructed.\n */\n static from(request) {\n const tokenBase64 = request.headers.get(Token.TOKEN_HEADER)\n if (tokenBase64) {\n return new Token(tokenBase64)\n }\n\n return null\n }\n\n /**\n * Converts an object whose string keys map to token objects\n * into URL-friendly search params suitable for use in a\n * query string or fragment.\n *\n * The string keys are normally the audience hostname, or the\n * special key 'party' which holds the identity token whose\n * `aud` and `sub` are the same.\n *\n * @param {TokenSet} tokenSet the set of tokens keyed by string.\n * @returns {string}\n */\n static toSearchString(tokenSet) {\n const searchParams = new URLSearchParams()\n for (const key in tokenSet) {\n const token = tokenSet[key]\n const tokenBase64 = token.asSignedBase64()\n searchParams.append(key, tokenBase64)\n }\n return searchParams.toString()\n }\n}\n", "/**\n * @import { Token } from './Token.js'\n * @import { Ok, Error } from '../ts/Responses.ts'\n * @import { JSONValue } from '../ts/Assertions.ts'\n */\n\n/**\n * Storage keys are 1 or more slash-separated components\n * using the characters a-z, A-Z, 0-9, hyphen, colon and period.\n *\n * Examples:\n * - acmeSetting\n * - acme.com/applicant/22fc01-b0d3084870b-fcae49ac2-892668\n * - address/line1\n * - url/acme.com\n *\n * The key needs to work as part of a REST/HTTP pathname.\n */\nconst KEY_PATTERN = /^[\\w\\-.:]+(?:\\/[\\w\\-.:]+)*$/\n\n/**\n * As above, but % and _ are both allowed as a wildcards.\n */\nconst KEYLIKE_PATTERN = /^[\\w\\-.:%_]+(?:\\/[\\w\\-.:%_]+)*$/\n\n/**\n * An instance of this class is normally obtained from BrowserApp, which constructs\n * the instance using the party token.\n */\nexport class Storage {\n #token // Party token which must include getitem and setitem capabilities on party:control.\n\n /**\n * @param {Token} token to use for storage requests.\n */\n constructor(token) {\n this.#token = token\n }\n\n /**\n * Gets the item whose key is provided. Keys are in the form\n * 'some[/key]*' without leading or trailing '/'.\n *\n * @param {string} key used to retrieve the item.\n * @return {Promise<Ok<JSONValue> | Error>} ok or error result.\n */\n getItem(key) {\n return getItem(this.#token, key)\n }\n\n /**\n * Gets the list (possibly empty) of items matching the SQL-like\n * argument.\n *\n * @param {string} keylike a key matcher such as 'myApp/%'.\n * @return {Promise<Ok<JSONValue[]>>} a list of values.\n */\n getItemsLike(keylike) {\n return getItemsLike(this.#token, keylike)\n }\n\n /**\n * Stores an item with the supplied key and value.\n *\n * @param {string} key the key to use.\n * @param {JSONValue} value to store.\n * @return {Promise<Ok<true> | Error>} ok if successful, error otherwise.\n */\n setItem(key, value) {\n return setItem(this.#token, key, value)\n }\n\n /**\n * Removes the item with the specified key, if any.\n *\n * @param {string} key the key to remove.\n * @return {Promise<Ok<true> | Error>} ok if successful, error otherwise.\n */\n removeItem(key) {\n return removeItem(this.#token, key)\n }\n}\n\n/**\n * Throws an exception if the key being used is not\n * in a valid format.\n *\n * @param {string} key\n */\nfunction assertValid(key) {\n if (key.match(KEY_PATTERN) == null) {\n throw `DaemonStorage: Invalid key: '${key}`\n }\n}\n\n/**\n * Throws an exception if the key like pattern being used\n * is not in a valid format.\n *\n * @param {string} keylike\n */\nfunction assertValidLike(keylike) {\n if (keylike.match(KEYLIKE_PATTERN) == null) {\n throw `DaemonStorage: Invalid like key: ${keylike}`\n }\n}\n\n/**\n * Returns the value as a JSON string\n *\n * @param {JSONValue} value\n * @return {string} JSON string.\n * @throws {string} exception if the value is not JSON.\n */\nfunction serialise(value) {\n try {\n return JSON.stringify(value)\n }\n catch (_e) {\n throw `DaemonStorage: Invalid value`\n }\n}\n\n/**\n * Sets an item in daemon storage.\n *\n * The value must be an object or primitive\n * which is serialised before saving.\n *\n * @param {Token} token to use.\n * @param {string} key the item key.\n * @param {any} value the serialisable value for the item.\n * @return {Promise<Ok<true> | Error>} ok or error object.\n */\nexport async function setItem(token, key, value) {\n assertValid(key)\n const body = serialise(value)\n const url = `${token.getAud()}/storage/${key}`\n const response = await fetch(url, {\n method: 'PUT',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64(),\n 'Content-Type': 'application/json'\n },\n body\n })\n return response.json()\n}\n\n/**\n * Gets an item from daemon storage.\n *\n * Returns an 'ok' object with the value if present, otherwise\n * returns an 'error' object.\n *\n * @param {Token} token to use.\n * @param {string} key the item key to retrieve.\n * @return {Promise<Ok<JSONValue> | Error>} ok object with value, or error.\n */\nexport async function getItem(token, key) {\n assertValid(key)\n const url = `${token.getAud()}/storage/${key}`\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64()\n }\n })\n return response.json()\n}\n\n/**\n * Gets zero or more items from daemon storage, whose\n * keys match the keyLike pattern. This is normally\n * something like 'my/prefix/%' which gets all values\n * with that prefix.\n *\n * You can use both '%' and '_' as wildcards for string\n * of any length and single character respectively.\n *\n * @param {Token} token to use.\n * @param {string} keylike the item keylike pattern to match.\n * @return {Promise<Ok<JSONValue[]>>} ok object with list, or error.\n */\nexport async function getItemsLike(token, keylike) {\n assertValidLike(keylike)\n const url = new URL(`${token.getAud()}/storage`)\n url.searchParams.append('like', keylike)\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64()\n }\n })\n return await response.json()\n}\n\n/**\n * Removes an item from daemon storage.\n *\n * @param {Token} token to use.\n * @param {string} key the item key to remove.\n * @return {Promise<Ok<true> | Error>} ok or error object.\n */\nexport async function removeItem(token, key) {\n assertValid(key)\n const url =`${token.getAud()}/storage/${key}`\n const response = await fetch(url, {\n method: 'DELETE',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64()\n }\n })\n return response.json()\n}\n", "const Char = {\n Ash: '\u00E6',\n Hash: '#'\n}\n\nconst Encoded = {\n Ash: encodeURIComponent('\u00E6')\n}\n\nexport class ParamExtractor {\n params = new URLSearchParams()\n\n /**\n * @param {string} hash the hash string, with or without leading '#'.\n * @returns {string} the cleaned hash string.\n */\n extractHash(hash) {\n hash = hash.startsWith(Char.Hash) ? hash.substring(1) : hash\n const i = hash.indexOf(Encoded.Ash)\n if (i === -1) {\n return hash\n }\n\n const params = new URLSearchParams(hash.slice(i))\n this.extract(params)\n return hash.slice(0, i)\n }\n\n /**\n * @param {string} query the query string.\n * @returns {string} the cleaned query string.\n */\n extractQuery(query) {\n const params = new URLSearchParams(query)\n this.extract(params)\n return params.toString()\n }\n\n /**\n * Extracts \u00E6-prefixed params into this instance,\n * then deletes them from the supplied searchparams\n * object.\n *\n * @param {URLSearchParams} params\n */\n extract(params) {\n const keys = Array.from(params.keys())\n for (const key of keys) {\n if (key.startsWith(Char.Ash)) {\n this.params.set(key.substring(1), params.get(key) ?? '')\n params.delete(key)\n }\n }\n }\n\n getParams() {\n return this.params\n }\n}\n", "import { Token } from './Token.js'\nimport { Storage } from './Storage.js'\nimport { getAppId } from './Digest.js'\nimport { ParamExtractor } from './ParamExtractor.js'\n\n/**\n * @module\n * Provides convenience support for WebDaemon apps delivered\n * through browsers.\n *\n * Use the singleton pattern, providing app name on first call\n * to getInstance.\n */\n\nconst PARTY_PARAM = 'party'\nconst PREFIX_PARAM = 'prefix'\nconst LAST_KNOWN_DAEMON = 'lastKnownDaemon' // In app's localStorage.\n\nexport class BrowserApp {\n\n /** @type {BrowserApp | null} */\n static #instance\n\n #appName\n #partyKey\n #tokensKey\n #appUrlKey\n\n /**\n * Gets the singleton instance of BrowserApp. On invocations\n * after the first, the app name must be omitted or match\n * the first-used app name.\n *\n * @param {string=} appName\n * @return {Promise<BrowserApp>} instance of browser app.\n */\n static async getInstance(appName = undefined) {\n if (\n !appName &&\n !BrowserApp.#instance\n ) {\n throw `Must provide app name`\n }\n\n if (\n appName &&\n BrowserApp.#instance &&\n appName !== BrowserApp.#instance.#appName\n ) {\n throw `Cannot change app name from ${BrowserApp.#instance.#appName} to ${appName}`\n }\n\n if (appName && !BrowserApp.#instance) {\n BrowserApp.#instance = new BrowserApp(appName)\n await BrowserApp.#instance.init()\n }\n\n if (!BrowserApp.#instance) {\n throw `No instance of BrowserApp`\n }\n\n return BrowserApp.#instance\n }\n\n /**\n * Constructs a BrowserApp instance with the app name. This should be\n * unique per origin. Spaces are _NOT_ allowed in this name.\n *\n * @param {string} appName a unique name for this app per origin.\n */\n constructor(appName) {\n this.#appName = appName\n this.#partyKey = `${appName}Party`\n this.#tokensKey = `${appName}Tokens`\n this.#appUrlKey = `${appName}AppUrl`\n }\n\n /**\n * Initialises the instance with the party name and tokens passed\n * in the #fragment and ?query string search parameters.\n *\n * - party is passed in the special parameter 'party'.\n * - tokens are passed in the remaining parameters keyed by host name.\n *\n * The #fragment starting at the first '\u00E6' (if present) is used and\n * any params whose name starts with the special character '\u00E6' are extracted.\n *\n * Any fragment prior to this is retained in the browser location.\n *\n * Any query params whose name starts with the special character '\u00E6' are\n * extracted.\n *\n * The window location is replaced with the 'clean' version that\n * no longer includes the tokens.\n *\n * @return {Promise<BrowserApp>} instance promise.\n * @throws {string} error if party token `src` does not match our window location.\n */\n async init() {\n const ourUrl = new URL(globalThis.location.href)\n\n const extractor = new ParamExtractor()\n const newHash = extractor.extractHash(ourUrl.hash)\n const newSearch = extractor.extractQuery(ourUrl.search)\n const daemonParams = extractor.getParams()\n\n if (daemonParams.has(PARTY_PARAM)) {\n sessionStorage[this.#tokensKey] = daemonParams.toString()\n const identityToken = this.getToken()\n await identityToken.verifySignatory()\n sessionStorage[this.#partyKey] = identityToken.getParty()\n localStorage.setItem(LAST_KNOWN_DAEMON, identityToken.getParty())\n\n const srcUrl = identityToken.getSourceUrl()\n if (srcUrl.origin !== ourUrl.origin || srcUrl.pathname !== ourUrl.pathname) {\n throw `Party token is for different app: ${srcUrl}`\n }\n }\n\n ourUrl.hash = newHash\n ourUrl.search = newSearch\n sessionStorage[this.#appUrlKey] = ourUrl.toString()\n globalThis.history.replaceState(null, '', ourUrl.toString())\n return this\n }\n\n /**\n * Returns true if this app is an orphan, i.e. has not been launched from\n * a D\u00E6mon shell.\n *\n * Otherwise returns false.\n *\n * @return {boolean} true if an orphan without D\u00E6mon, otherwise false.\n */\n isOrphan() {\n return (\n sessionStorage[this.#partyKey] === undefined ||\n sessionStorage[this.#tokensKey] === undefined\n )\n }\n\n /**\n * Returns the app name. This is normally used to disambiguate properties\n * of this app from others, such as those sharing a web origin and therefore\n * sharing localStorage or sessionStorage.\n *\n * Note that this is a client-side name not known to the tabserver.\n *\n * @return {string} the app name.\n */\n getAppName() {\n return this.#appName\n }\n\n /**\n * Returns the app url, obtained from window.location at time of app init,\n * or the empty string if not initialised.\n *\n * @return {string} the app url, or the empty string.\n */\n getAppUrl() {\n return sessionStorage[this.#appUrlKey]\n }\n\n /**\n * Returns the party hostname, being the D\u00E6mon that launched this app.\n *\n * @return {string} the party (D\u00E6mon) hostname.\n */\n getParty() {\n return sessionStorage[this.#partyKey]\n }\n\n /**\n * Returns the party origin, being the protocol (http: or https:)\n * and the party hostname.\n *\n * @return {string} the party origin.\n */\n getPartyOrigin() {\n return `${globalThis.location.protocol}//${this.getParty()}`\n }\n\n /**\n * Returns the default prefix associated with the app url if specified,\n * or this app if not. The default prefix is simply the app ID being a\n * short digest of the app URL, excluding any # fragment.\n *\n * @param {string | URL | null} appUrl for which the default prefix is required (optional).\n * @return {Promise<string>} the default prefix for the app.\n */\n getDefaultPrefix(appUrl = null) {\n if (!appUrl) {\n appUrl = this.getAppUrl()\n }\n\n const cleanUrl = new URL(appUrl)\n cleanUrl.hash = ''\n return getAppId(cleanUrl)\n }\n\n /**\n * Returns the likely prefix for this app.\n *\n * This is the value of the prefix parameter if present,\n * or the default prefix for the app if not.\n *\n * @return {Promise<string>} prefix obtained from prefix= param, or computed from the app URL.\n */\n getPrefix() {\n if (this.hasParam(PREFIX_PARAM)) {\n return Promise.resolve(this.getParam(PREFIX_PARAM) ?? '')\n }\n return this.getDefaultPrefix()\n }\n\n /**\n * Returns the likely url for the named tab on this app's agent.\n *\n * This will be wrong if the YAML file has a `prefix=` specified which\n * is not set to the value of a provided `prefix=` parameter.\n *\n * @param {string} tab name, e.g. 'v1'.\n * @returns {Promise<string>} the url of this tab on this app.\n */\n async getAgentUrl(tab) {\n const origin = this.getPartyOrigin()\n const prefix = await this.getPrefix()\n return `${origin}/tab/${prefix}/${tab}`\n }\n\n /**\n * Returns the base64-encoded token for the supplied party. If no\n * party is specified, returns the token for the launching party.\n *\n * @param {string} party the party, or the launching party if not specified.\n * @return {string} the base64-encoded token for the party.\n */\n getTokenBase64(party = PARTY_PARAM) {\n const tokens = sessionStorage[this.#tokensKey]\n const searchParams = new URLSearchParams(tokens)\n const tokenBase64 = searchParams.get(party)\n if (!tokenBase64) {\n throw `No token for ${party}, check 'audience' in YML`\n }\n return tokenBase64\n }\n\n\n /**\n * Gets the token for a party either as a base64-encoded string, or as a\n * Token object depending on the asBase64 argument.\n *\n * If the party is not passed or is null, the default is the launching party.\n * If the asBase64 is not passed, the default is true.\n *\n * @param {string=} party the audience for the token. Defaults to the identity token 'party'.\n * @return {Token} the pre-generated token for the given party.\n */\n getToken(party = PARTY_PARAM) {\n const tokenBase64 = this.getTokenBase64(party)\n return new Token(tokenBase64)\n }\n\n /**\n * Gets the value of the named parameter, or null if no value is supplied.\n *\n * The parameter name is case insensitive.\n *\n * The parameter is specified as:\n * - an attribute on the webdaemon meta tag, or\n * - a search parameter in the query string of the URL\n *\n * where the query string parameter takes priority if present.\n *\n * @param {string} name\n * @return {string | null} parameter value or null if not present.\n */\n getParam(name) {\n\n // Return a matching search parameter if present.\n const searchParams = new URL(globalThis.location.href).searchParams\n for (const [paramName, value] of searchParams) {\n if (name.toLowerCase() == paramName.toLowerCase()) {\n return value\n }\n }\n\n // Return meta element attribute if present, or null.\n const metaElement = document.querySelector('link[rel=webdaemon]')\n return metaElement?.getAttribute(name) ?? null\n }\n\n /**\n * Returns true if the named parameter is present, even if with empty\n * or null value.\n *\n * @param {string} name\n * @return {boolean} true if the parameter exists, otherwise false.\n */\n hasParam(name) {\n const searchParams = new URL(globalThis.location.href).searchParams\n if (searchParams.has(name)) {\n return true\n }\n\n const metaElement = document.querySelector('link[rel=webdaemon]')\n return metaElement?.hasAttribute(name) ?? false\n }\n\n /**\n * Returns the currently raised launch alert for this app,\n * or null if none.\n *\n * @typedef {import('./Alert.js').AlertEntry} AlertEntry\n *\n * @return { Promise<AlertEntry | null> } raised launch alert, or null if none.\n * @throws {string} exception if alert catalog cannot be retrieved.\n */\n async getLaunchAlert() {\n const origin = this.getPartyOrigin()\n const url = new URL(`${origin}/catalog`)\n url.searchParams.append('alert', '')\n const response = await fetch(url, {\n headers: {\n 'X-Tabserver-Token': this.getTokenBase64(),\n 'Accept': 'application/json'\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n const alert = json.ok.alert.find(\n /** @param {AlertEntry} alert */\n alert => (\n alert.type == 'LAUNCH' &&\n alert.sourceUrl == new URL(this.getAppUrl())\n )\n )\n return alert || null\n }\n\n /**\n * Resolves the currently raised launch alert for this app, if any.\n *\n * @throws {string} exception if alert cannot be resolved.\n */\n async resolveLaunchAlert() {\n const origin = this.getPartyOrigin()\n const url = new URL(`${origin}/resolve`)\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'X-Tabserver-Token': this.getTokenBase64(),\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n type: 'LAUNCH',\n source: this.getAppUrl()\n })\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n }\n\n /**\n * Resolves the currently raised launch alert for this app, if any.\n *\n * @throws {string} exception if alert cannot be resolved.\n */\n async rejectLaunchAlert() {\n const origin = this.getPartyOrigin()\n const url = new URL(`${origin}/reject`)\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'X-Tabserver-Token': this.getTokenBase64(),\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n type: 'LAUNCH',\n source: this.getAppUrl()\n })\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n }\n\n /**\n * Gets a Storage instance using the party token.\n *\n * @returns {Storage} instance.\n */\n getStorage() {\n return new Storage(this.getToken())\n }\n}\n", "import { BrowserApp } from './BrowserApp.js'\n\n/**\n * @import { JSONValue } from '../ts/Assertions.ts'\n */\n\n/**\n * Returns the currently raised launch alert for this app,\n * or null if none.\n *\n * @typedef {Object} AlertEntry\n * @property {URL} sourceUrl - The URL of the alert source.\n * @property {string} sourceTitle - The title of the alert source.\n * @property {string} type - The alert type.\n * @property {JSONValue} value - The value associated with the alert.\n * @property {Date} created - When the alert was created.\n * @property {Date|null} expiry - When the alert expires or null if it doesn't expire.\n *\n * @return { Promise<AlertEntry | null> } raised launch alert, or null if none.\n * @throws {string} exception if alert catalog cannot be retrieved.\n */\nexport async function getLaunchAlert() {\n const app = await BrowserApp.getInstance()\n const origin = app.getPartyOrigin()\n const url = new URL(`${origin}/catalog`)\n url.searchParams.append('alert', '')\n const response = await fetch(url, {\n headers: {\n 'X-Tabserver-Token': app.getTokenBase64(),\n 'Accept': 'application/json'\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n const alert = json.ok.alert.find(\n /** @param {AlertEntry} alert */\n alert => (\n alert.type == 'LAUNCH' &&\n alert.sourceUrl == new URL(app.getAppUrl())\n )\n )\n return alert || null\n}\n", "import { Token } from './Token.js'\n\n/**\n * This helper provides the parent capability for a website to\n * check and activate a daemon, and to create a device offer for\n * subsequent claim by consumer devices or browsers.\n *\n * The helper can operate either browser- or server-side.\n *\n * Usage\n * =====\n *\n * // Create the helper for a child daemon.\n * const helper = new ParentHelper()\n *\n * // Initialise with the child daemon being parented (aud), the\n * // source (src), the private key used to sign the token\n * // and the signatory url (iss).\n * await initWithKey(child, source, privateJwk, iss)\n *\n * // Succeeds if the child daemon is activated.\n * await helper.checkActivate()\n *\n * // Returns the claim code and check state of a new device offer.\n * await helper.fetchOffer()\n *\n * @typedef {Object} JsonWebKey\n * @typedef {string} TokenBase64\n *\n * @typedef {Object} OfferOptions\n * @property {'TRANSIENT' | 'PERMANENT'} [type] the type of the device.\n * @property {number} [ttl] the time-to-live of device (transient only), in seconds.\n * @property {number} [expiry] the number of seconds before the offer expires.\n * @property {Object} [payload] the optional offer payload.\n */\n\nexport class ParentHelper {\n /** @type {JsonWebKey | undefined} */\n #privateJwk // Signs the token used for activate and offer.\n\n /** @type {string | undefined} */\n #protocol // Protocol in use (http: or https:).\n\n /** @type {string | undefined} */\n #child // The host name of the daemon being parented.\n\n /** @type {string | undefined} */\n #iss // Callback-supplied URL for the pubicly visible issuer object.\n\n /** @type {string | undefined} */\n #src // The HTML source URL, which must match origin: header iff present in daemon request.\n\n /** @type {Token | undefined} */\n #token // The token used for check activate and offer calls to the daemon.\n\n /** @type {string | undefined} */\n #claimCode // The claim code returned by fetchOffer.\n\n /**\n * Initialises the helper with details already known to the caller.\n *\n * The private key is used to sign generated tokens, where the issUrl\n * references the public key used to verify the token.\n *\n * This method is used by tab runners wanting to parent daemons.\n *\n * @param {string} child\n * @param {string} src # Note can be party:control.\n * @param {JsonWebKey} privateJwk\n * @param {string} iss\n */\n async initWithKey(child, src, privateJwk, iss) {\n this.#protocol = src.startsWith('https:') ? 'https:' : 'http:'\n this.#child = child\n this.#src = src\n this.#privateJwk = privateJwk\n this.#iss = iss\n\n await this.#buildToken()\n }\n\n /**\n * Uses the token to check activate the daemon. An already\n * activated daemon with this parent is retained, or a new\n * daemon is created so long as the sub of the pre-generated\n * token matches a parent record in the provider.\n *\n * The `isNew` attribute in the ok result can be used to\n * determine if the daemon is newly activated or already\n * active.s\n *\n * @returns boolean true if newly activated, otherwise false.\n * @throws {string} exception if daemon cannot be activated.\n */\n async checkActivate() {\n if (!this.#token) {\n throw `checkActivate: No token`\n }\n const url = new URL(`${this.#token.getAud()}/activate`)\n const token = this.#token.asSignedBase64()\n const body = {}\n let response\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n 'x-tabserver-token': token,\n 'content-type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n\n const {\n ok,\n error\n } = await response.json()\n\n if (error) {\n throw error\n }\n\n if (ok.isNew) {\n return true\n }\n\n return false\n }\n catch (e) {\n console.error(e)\n throw `Cannot activate ${this.#token.getParty()}`\n }\n }\n\n /**\n * Uses the token to create a device offer on the daemon with default\n * type 'TRANSIENT' and ttl of 300s.\n *\n * @typedef {Object} Offer\n * @property {string} claimCode\n * @property {string} checkState\n *\n * @param {'NO_CHECK' | 'DO_CHECK'} flow to use. For QR offers, use checked flow.\n * @param {OfferOptions | null} options to use when making the offer, if any.\n * @return {Promise<Offer>} offer claimCode and checkState ('NO_CHECK' or 'AWAIT_CHECK').\n */\n async makeOffer(flow, options = null) {\n const {\n type = 'TRANSIENT',\n ttl = 300,\n expiry = 30,\n payload\n } = options ?? {}\n\n if (!this.#token) {\n throw `makeOffer: No token`\n }\n const url = new URL(`${this.#token.getAud()}/offer`)\n const token = this.#token.asSignedBase64()\n const body = {\n role: 'party',\n type,\n ttl,\n description: `Offered by ${this.#token.getCounterparty()}`,\n payload,\n expiry: new Date(Date.now() + 1000 * expiry),\n flow\n }\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'x-tabserver-token': token,\n 'content-type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n const {\n claimCode,\n _checkState\n } = json.ok\n\n this.#claimCode = claimCode\n\n return json.ok\n }\n\n /**\n * Returns the check state for the offer. This is used when\n * awaiting claim.\n *\n * @return {Promise<string>}\n */\n async checkState() {\n if (!this.#token || !this.#claimCode) {\n throw 'checkState: No token or claimCode'\n }\n const url = new URL(`${this.#token.getAud()}/offer/check`)\n url.searchParams.append('claimCode', this.#claimCode)\n const token = this.#token.asSignedBase64()\n const response = await fetch(url, {\n headers: {\n 'x-tabserver-token': token\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n /**\n * Result has `claimCode` and `checkState`.\n */\n return json.ok.checkState\n }\n\n /**\n * Posts the check code that confirms the offering device has\n * got the expected claiming device, and returns the resulting check state.\n *\n * @typedef {Object} ConfirmResult\n * @property {string} checkState\n *\n * @param {string} checkCode the check code obtained from the claiming device.\n * @return {Promise<ConfirmResult>} the check state following confirmation.\n */\n async confirmClaim(checkCode) {\n if (!this.#token || !this.#claimCode) {\n throw 'confirmClaim: No token or claimCode'\n }\n\n const url = new URL(`${this.#token.getAud()}/offer/confirm`)\n const token = await this.#token.asSignedBase64()\n const body = {\n claimCode: this.#claimCode,\n checkCode\n }\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'x-tabserver-token': token,\n 'content-type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n return json.ok\n }\n\n /**\n * Returns the daemon name.\n *\n * @return {string} daemon name, e.g. 'daemon.once.id'.\n */\n getChild() {\n if (!this.#child) {\n throw 'getChild: no child'\n }\n return this.#child\n }\n\n /**\n * Returns the daemon origin, suitable for redirection or claiming\n * a code.\n *\n * @return {string} child daemon origin e.g. 'https://daemon.once.id'.\n */\n getChildOrigin() {\n if (!this.#iss || !this.#child) {\n throw 'getDaemonUrl: no issUrl or daemon'\n }\n return `${this.#protocol}//${this.#child}`\n }\n\n /**\n * Builds the token used in the check activate and offer calls toh\n * the daemon.\n *\n * @return {Promise<void>}\n */\n async #buildToken() {\n if (!this.#iss || !this.#child || !this.#privateJwk) {\n throw 'buildToken: missing iss, child or privateJwk'\n }\n\n const iss = this.#iss\n const aud = this.getChildOrigin()\n const sub = new URL(this.#iss).origin\n\n // The `src` attribute is origin and pathname only.\n let src\n if (this.#src) {\n const srcUrl = new URL(this.#src)\n src = `${srcUrl.origin}${srcUrl.pathname}`\n }\n else {\n src = 'party:control'\n }\n\n const payload = {\n iss,\n aud,\n sub,\n src,\n scope: {\n 'party:control': 'offer'\n },\n iat: Date.now(),\n exp: Date.now() + 1000 * 60 * 3\n }\n\n const token = new Token(payload)\n await token.signWith(Promise.resolve(this.#privateJwk))\n this.#token = token\n }\n}\n", "/**\n * Utility class to generate a QR code img element which is placed\n * under a parent element.\n *\n * Relies upon a script element in the page:\n *\n * <script src='https://unpkg.com/qrcode-generator@1.4.4/qrcode.js'>\n */\nconst QrType = 0 // Auto detection by library.\nconst QrEcc = 'L'\n\nexport const ImgClass = 'qrcode'\n\nexport class QrCode {\n #img\n #content\n\n /**\n * Constructor takes img element and\n * the string to show in the QR code.\n * @param {HTMLImageElement} img the image element to use.\n * @param {string} content the content of the qr code.\n */\n constructor(img, content) {\n this.#img = img\n this.#content = content\n }\n\n /**\n * Returns a promise that is resolved when the image loads\n * successfully, or rejected if the image load fails.\n */\n generate() {\n const qr = qrcode(QrType, QrEcc)\n qr.addData(this.#content)\n qr.make()\n const dataUrl = qr.createDataURL()\n this.#img.classList.add(ImgClass)\n this.#img.src = dataUrl\n return new Promise((resolve, reject) => {\n this.#img.onload = resolve\n this.#img.onerror = reject\n })\n }\n}\n", "/**\n * Use this type to reference a plain object with\n * random string keys.\n */\nexport interface Plain {\n // deno-lint-ignore no-explicit-any\n [key: string]: any\n}\n\n/**\n * Any JSON-serialisable type.\n */\nexport type JSONPrimitive = string | number | boolean | null\nexport type JSONArray = JSONValue[]\nexport interface JSONObject {\n [key: string]: JSONValue\n}\nexport type JSONValue = JSONPrimitive | JSONObject | JSONArray\n\n\n/**\n * Type predicates to be used in type-narrowing assertions, e.g.\n *\n * assert(notNull(someValue))\n * ... now the type of someValue does not include null ...\n *\n * or\n * if (isOk(returnValue)) {\n * ...in this block we know returnValue.ok is present and of the correct type.\n * }\n *\n * @param value the value to be tested.\n * @returns true if not null, otherwise false.\n */\nexport function notNull<T>(value: T | null): value is T {\n return value !== null\n}\n\nexport function isDefined<T>(value: T | undefined): value is T {\n return value !== undefined\n}\n", "import { Plain } from './Assertions.ts'\n\nexport interface Ok<T> {\n ok: T\n}\n\nexport interface Error {\n error: string\n}\n\n/**\n * Returns an ok response or an error response corresponding\n * to the standard ok or error return value.\n *\n * If the error string starts with a space-separated status code,\n * then that is used as the status code for the response.\n *\n * For example:\n *\n * {error: 'Ordinary Error'}\n * is sent as is with status 200.\n * {error: '401 Authentication Error'}\n * is sent as {error: 'Authentication Error'} with status 401\n */\nexport function response<T>(rvalue: Ok<T> | Error, extraHeaders?: Plain) {\n\n if ('ok' in rvalue) {\n const body = JSON.stringify(rvalue)\n return new Response(body, {\n status: 200,\n headers: {\n ...extraHeaders,\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n }\n })\n }\n\n const error = rvalue.error\n const [, status, message] = error.match(/^(\\d{3})\\s(.+)/) ?? [null, '200', error]\n const body = JSON.stringify({\n error: message\n })\n return new Response(body, {\n status: Number(status),\n headers: {\n ...extraHeaders,\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*'\n }\n })\n}\n\n/**\n * Returns a 404 error response, including\n * the standard JSON error return value.\n * @deprecated Use response instead using a status-prefixed error message.\n */\nexport function response404<T>(json: Error = {\n error: '404 Not Found'\n}) {\n const body = JSON.stringify(json)\n return new Response(body, {\n status: 404,\n headers: {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n }\n })\n}\n\n/**\n * Returns a 302 permanent redirect response.\n * @param url the url to redirect to.\n * @returns Response object.\n */\nexport function response302(url: URL, extraHeaders: Plain = []): Response {\n return new Response(null, {\n status: 302,\n headers: {\n 'Location': url.toString(),\n 'Access-Control-Allow-Origin': '*',\n ...extraHeaders\n }\n })\n}\n\n/**\n * Simple build-a-cookie support.\n *\n * If the `expires` attribute is not defined, it's a session cookie.\n * Otherwise it's a permanent cookie. To delete a cookie, use the\n * `expires` attribute set to `new Date(0)`.\n */\n\nexport interface CookiePayload {\n name: string\n value: string\n domain?: string\n sameSite?: 'Strict' | 'Lax' | 'None'\n path?: string\n expires?: Date\n secure?: boolean\n httpOnly?: boolean\n}\n\nexport type CookieString = string\n\nexport function buildCookie(payload: CookiePayload): CookieString {\n const {\n name,\n value,\n domain,\n sameSite,\n path = '/',\n expires,\n secure = true,\n httpOnly = true\n } = payload\n\n const builder: string[] = []\n builder.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`)\n if (domain) builder.push(`Domain=${domain}`)\n if (sameSite) builder.push(`SameSite=${sameSite}`)\n builder.push(`Path=${path}`)\n if (expires) builder.push(`Expires=${expires.toUTCString()}`)\n if (secure) builder.push(`Secure`)\n if (httpOnly) builder.push('HttpOnly')\n\n const cookie = builder.join('; ')\n return cookie\n}\n", "import { response } from './Responses.ts'\nimport { Plain, JSONValue } from './Assertions.ts'\nimport { Token, Scope } from '../js/Token.js'\nimport { Storage } from '../js/Storage.js'\n\n// Daemon configuration looks like this.\nexport interface DaemonConfig {\n system: {\n protocol: 'http:' | 'https:'\n party: string\n issuer: string\n source: string\n sourcePrefix: string\n tab: string,\n privateJwk: JsonWebKey\n }\n user: Plain\n}\n\n// System requests are on the /daemon/... path.\nexport const DAEMON_PREFIX = 'daemon'\n\n// Daemon lifecycle path components that come after DAEMON_PREFIX.\nexport const CONFIG_PATH = 'config'\nexport const EVENT_PATH = 'event'\n\n// Event names.\nconst Ev = {\n Config: 'config'\n}\n\n// SessionStorage keys for lifecycle objects.\nconst CONFIG_KEY = 'config'\n\n// Default time-to-live for a third party token we generate is 1 hour.\nconst DEFAULT_TTL_MILLIS = 1000 * 60 * 60\n\ninterface TabEvent {\n type: string,\n payload: JSONValue\n}\n\n/**\n * Encapsulates the lifecycle event requests that occur on\n * runner start and termination and when alerts are resolved\n * or rejected.\n *\n * The Lifecycle object also provides static methods to retrieve\n * configuration items, a public and private key pair, and also to\n * produce the signed token that is required in requests from this party\n * to third parties.\n */\nexport class Lifecycle extends EventTarget {\n static #instance: Lifecycle = new Lifecycle()\n\n // Expose event names to importers.\n static Ev = Ev\n\n static getInstance() {\n return this.#instance\n }\n\n /**\n * All system requests are under the /daemon/ path.\n *\n * @param {Request} request to be tested.\n * @returns {boolean} true if the request is a lifecycle request.\n */\n static shouldHandle(request: Request): boolean {\n return (\n Lifecycle.isConfig(request) ||\n Lifecycle.isEvent(request)\n )\n }\n\n static isConfig(request: Request): boolean {\n const url = new URL(request.url)\n return (\n request.method == 'POST' &&\n url.pathname == `/${DAEMON_PREFIX}/${CONFIG_PATH}` &&\n request.headers.get('Content-Type') == 'application/json'\n )\n }\n\n static isEvent(request: Request): boolean {\n const url = new URL(request.url)\n return (\n request.method == 'POST' &&\n url.pathname == `/${DAEMON_PREFIX}/${EVENT_PATH}` &&\n request.headers.get('Content-Type') == 'application/json'\n )\n }\n\n /**\n * Handles a lifecycle request.\n *\n * @param {Request} request to be handled.\n * @returns {Response} response for caller.\n */\n async handler(request: Request): Promise<Response> {\n if (Lifecycle.isConfig(request)) {\n return await this.handleConfig(request)\n }\n if (Lifecycle.isEvent(request)) {\n return await this.handleEvent(request)\n }\n return response({\n error: 'Invalid daemon request'\n })\n }\n\n /**\n * Saves the lifecycle config object provided in the request body.\n *\n * Fires a 'config' lifecycle event.\n *\n * @param {Request} request containing the configuration json.\n * @return {Response} ok response.\n */\n async handleConfig(request: Request): Promise<Response> {\n const configJson = await request.json()\n sessionStorage.setItem(CONFIG_KEY, JSON.stringify(configJson))\n this.dispatchEvent(new CustomEvent(Ev.Config, {\n detail: configJson\n }))\n return response({\n ok: true\n })\n }\n\n /**\n * Fires a lifecycle event upon receiving an event request.\n *\n * @param {Request} request containing the event.\n * @return {Response} ok response.\n */\n async handleEvent(request: Request): Promise<Response> {\n const {\n type,\n payload\n } = await request.json()\n this.dispatchEvent(new CustomEvent(type, {\n detail: payload\n }))\n return response({\n ok: true\n })\n }\n\n /**\n * Returns the configuration object stored on initialisation.\n * @return {DaemonConfig} the daemon configuration object.\n * @throws {string} exception if config is not yet initialised.\n */\n static getConfig(): DaemonConfig {\n const json = sessionStorage.getItem(CONFIG_KEY)\n if (!json) {\n throw 'DaemonConfig not ready'\n }\n return JSON.parse(json)\n }\n\n /**\n * Generates if necessary and returns a private key for\n * use in signing tokens.\n *\n * @returns {JSONWebKey} the private key for this session.\n */\n static getPrivateKey(): Promise<JsonWebKey> {\n const {\n system: {\n privateJwk\n }\n } = Lifecycle.getConfig()\n return Promise.resolve(privateJwk)\n }\n\n /**\n * Returns a token suitable for the `x-tabserver-token` header\n * in a request to the supplied party with the required\n * scope.\n *\n * If party is omitted, it defaults to this party.\n * If\n *\n * @param {Scope} scope map of string source -> space-separated capabilities.\n * @param {string=} party the party host name to whom the request will be sent.\n * @param { src=} src the HTML page from which the request is (actually or notionally) made.\n * @param {number=} ttlMillis the lifetime of this token.\n * @returns {Token} the signed token.\n * @throws {string} if configuration not yet initialised.\n *\n */\n static async getTokenFor(\n scope: Scope,\n party: string | null = null,\n src: string | null = null,\n ttlMillis: number = DEFAULT_TTL_MILLIS\n ): Promise<Token> {\n const config = Lifecycle.getConfig()\n if (!config) {\n throw 'Lifecycle configuration not yet available'\n }\n\n // Extract protocol, our party, issuer and source from system config.\n const {\n system: {\n protocol,\n party: us,\n issuer,\n source\n }\n } = config\n\n // Token src excludes query params.\n const appSourceUrl = new URL(source)\n\n const aud = party ? `${protocol}//${party}`: `${protocol}//${us}`\n const sub = `${protocol}//${us}`\n const now = Date.now()\n\n const payload = {\n iss: issuer,\n aud,\n sub,\n src: src ?? `${appSourceUrl.origin}${appSourceUrl.pathname}`,\n scope,\n iat: now,\n exp: now + ttlMillis\n }\n\n const token = new Token(payload)\n await token.signWith(Lifecycle.getPrivateKey())\n return token\n }\n\n static async getStorage(): Promise<Storage> {\n const token = await Lifecycle.getStorageToken()\n return new Storage(token)\n }\n\n /**\n * Returns a token for our party with the setitem and getitem\n * capabilities on the party control source.\n */\n static getStorageToken(): Promise<Token> {\n const {\n system: {\n party,\n }\n } = Lifecycle.getConfig()\n\n const scope = {\n [Token.Source.PARTY_CONTROL]: `${Token.Capability.GET_ITEM} ${Token.Capability.SET_ITEM}`\n }\n\n return Lifecycle.getTokenFor(scope, party)\n }\n}\n", "import { Plain } from \"./Assertions.ts\";\n\n// Standard HTTP headers.\nconst X_FORWARDED_HOST = 'x-forwarded-host'\nconst X_FORWARDED_PROTO = 'x-forwarded-proto'\n\n// Non-standard HTTP header used by tabserver.\nconst X_FORWARDED_PATHNAME = 'x-forwarded-pathname'\n\n/**\n * Returns the request protocol. This is the first of the following:\n *\n * 1. The `x-forwarded-proto` header, if present, with trailing colon.\n * 2. The request URL protocol, ditto.\n *\n * @param request the request.\n * @returns {string} the protocol with trailing colon.\n */\nexport function getClientProtocol(request: Request): string {\n if (request.headers.has(X_FORWARDED_PROTO)) {\n return (request.headers.get(X_FORWARDED_PROTO) as string) + ':'\n }\n\n return new URL(request.url).protocol\n}\n\n/**\n * Returns the request host name. This is the first of the following:\n *\n * 1. The `x-forwarded-host` header, if present.\n * 2. The request URL hostname (including port, if present)\n *\n * @param request the request.\n * @returns the protocol.\n */\nexport function getClientHost(request: Request): string {\n if (request.headers.has(X_FORWARDED_HOST)) {\n return request.headers.get(X_FORWARDED_HOST) || ''\n }\n\n return new URL(request.url).host\n}\n\n/**\n * Returns the request pathname. This is the first of the following:\n *\n * 1. The `x-forwarded-pathname` header, if present.\n * 2. The request URL pathname.\n */\nexport function getClientPathname(request: Request): string {\n if (request.headers.has(X_FORWARDED_PATHNAME)) {\n return request.headers.get(X_FORWARDED_PATHNAME) || ''\n }\n\n return new URL(request.url).pathname\n}\n\n/**\n * Returns the client request protocol, host and pathname components\n * only.\n *\n * @param request the request.\n * @return {URL} the protocol://host/pathname\n */\nexport function getClientUrlPhp(request: Request): URL {\n const protocol = getClientProtocol(request)\n const host = getClientHost(request)\n const pathname = getClientPathname(request)\n return new URL(`${protocol}//${host}${pathname}`)\n}\n\n/**\n * Returns the URL as requested by the client taking account\n * of the x-forwarded protocol and host.\n *\n * @param request the request.\n * @returns the URL.\n */\nexport function getClientUrl(request: Request): URL {\n const originalUrl = new URL(request.url)\n const newUrl = getClientUrlPhp(request)\n newUrl.search = originalUrl.search\n newUrl.hash = originalUrl.hash\n return newUrl\n}\n\n/**\n * Returns the pathname portion of the request URL, excluding\n * query and fragment components.\n *\n * @param {request} the request.\n * @returns {string} the pathname which starts with a forward slash.\n */\nexport function getPathname(request: Request): string {\n const url = new URL(request.url)\n return url.pathname\n}\n\n/**\n * Returns the string value of a named cookie, or null if it is not\n * present in the request.\n *\n * @param {Request} request the request.\n * @param {string} cookieName the name of the cookie\n * @returns {string} the cookie value, or null if not present.\n */\nexport function getCookie(request: Request, cookieName: string): string | null {\n const cookies = getCookies(request)\n if (cookieName in cookies) {\n return cookies[cookieName]\n }\n return null\n}\n\n/**\n * Returns a map of request cookie names to values, which can be empty.\n *\n * @param {Request} request the request.\n * @returns {Plain} the plain object with zero or more cookie keys.\n */\nexport function getCookies(request: Request): Plain {\n const cookies: Plain = {}\n const headerValue = request.headers.get('Cookie')\n if (headerValue) {\n for (const keyVal of headerValue.split('; ')) {\n const [key, value] = keyVal.split('=')\n cookies[key] = value\n }\n }\n return cookies\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["shortSafeDigest", "message", "length", "msgUint8", "hashBuffer", "hashArray", "byteString", "base64Safe", "shortHexDigest", "hashHex", "b", "getAppId", "url", "DEFAULT_ALGORITHM", "KeyPair", "#algorithm", "#keypair", "algorithm", "publicKey", "privateKey", "spkiKey", "pkcs8Key", "ALGORITHM", "MATCH_ORIGIN", "TOKEN_IAT_LEEWAY_MILLIS", "Token", "_Token", "#payload", "#signatureBase64", "#audUrl", "#subUrl", "#srcUrl", "#signatory", "source", "payload", "iss", "aud", "sub", "src", "scope", "iat", "exp", "privateJwkPromise", "privateJwk", "privateKey", "toSign", "messageBuffer", "bufferSource", "signature", "signatureBase64", "issuer", "json", "publicJwkPromise", "publicJwk", "publicKey", "toCheck", "sigBuffer", "sources", "capability", "now", "bytes", "binString", "x", "base64", "m", "request", "tokenBase64", "tokenSet", "searchParams", "key", "KEY_PATTERN", "KEYLIKE_PATTERN", "Storage", "#token", "token", "key", "getItem", "keylike", "getItemsLike", "value", "setItem", "removeItem", "assertValid", "assertValidLike", "serialise", "body", "url", "Char", "Encoded", "ParamExtractor", "hash", "i", "params", "query", "keys", "key", "PARTY_PARAM", "PREFIX_PARAM", "LAST_KNOWN_DAEMON", "BrowserApp", "_BrowserApp", "#instance", "#appName", "#partyKey", "#tokensKey", "#appUrlKey", "appName", "ourUrl", "extractor", "ParamExtractor", "newHash", "newSearch", "daemonParams", "identityToken", "srcUrl", "appUrl", "cleanUrl", "getAppId", "tab", "origin", "prefix", "party", "tokens", "tokenBase64", "Token", "name", "searchParams", "paramName", "value", "url", "json", "alert", "Storage", "getLaunchAlert", "app", "BrowserApp", "origin", "url", "json", "alert", "ParentHelper", "#privateJwk", "#protocol", "#child", "#iss", "#src", "#token", "#claimCode", "child", "src", "privateJwk", "iss", "#buildToken", "url", "token", "body", "response", "ok", "error", "e", "flow", "options", "type", "ttl", "expiry", "payload", "json", "claimCode", "_checkState", "checkCode", "aud", "sub", "srcUrl", "Token", "QrEcc", "ImgClass", "QrCode", "#img", "#content", "img", "content", "qr", "dataUrl", "resolve", "reject", "notNull", "value", "isDefined", "response", "rvalue", "extraHeaders", "body", "error", "status", "message", "response404", "json", "response302", "url", "buildCookie", "payload", "name", "value", "domain", "sameSite", "path", "expires", "secure", "httpOnly", "builder", "DAEMON_PREFIX", "CONFIG_PATH", "EVENT_PATH", "Ev", "CONFIG_KEY", "DEFAULT_TTL_MILLIS", "Lifecycle", "_Lifecycle", "#instance", "request", "url", "response", "configJson", "type", "payload", "json", "privateJwk", "scope", "party", "src", "ttlMillis", "config", "protocol", "us", "issuer", "source", "appSourceUrl", "aud", "sub", "now", "token", "Token", "Storage", "X_FORWARDED_HOST", "X_FORWARDED_PROTO", "X_FORWARDED_PATHNAME", "getClientProtocol", "request", "getClientHost", "getClientPathname", "getClientUrlPhp", "protocol", "host", "pathname", "getClientUrl", "originalUrl", "newUrl", "getPathname", "getCookie", "cookieName", "cookies", "getCookies", "headerValue", "keyVal", "key", "value"]
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BrowserApp.d.ts","sourceRoot":"","sources":["../../../js/BrowserApp.js"],"names":[],"mappings":"AAiBA;IAEE,gCAAgC;IAChC,yBADW,UAAU,GAAG,IAAI,CACZ;IAOhB;;;;;;;OAOG;IACH,6BAHW,MAAM,YAAC,GACN,OAAO,CAAC,UAAU,CAAC,CA4B9B;IAED;;;;;OAKG;IACH,qBAFW,MAAM,EAOhB;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAHY,OAAO,CAAC,UAAU,CAAC,CA4B9B;IAED;;;;;;;QAOI;IACJ,YAFY,OAAO,CAOlB;IAED;;;;;;;;OAQG;IACH,cAFY,MAAM,CAIjB;IAED;;;;;OAKG;IACH,aAFY,MAAM,CAIjB;IAED;;;;OAIG;IACH,YAFY,MAAM,CAIjB;IAED;;;;;OAKG;IACH,kBAFY,MAAM,CAIjB;IAED;;;;;;;OAOG;IACH,0BAHW,MAAM,GAAG,GAAG,GAAG,IAAI,GAClB,OAAO,CAAC,MAAM,CAAC,CAU1B;IAED;;;;;;;OAOG;IACH,aAFY,OAAO,CAAC,MAAM,CAAC,CAO1B;IAED;;;;;;;;OAQG;IACH,iBAHW,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,CAM3B;IAED;;;;;;OAMG;IACH,uBAHW,MAAM,GACL,MAAM,CAUjB;IAGD;;;;;;;;;OASG;IACH,iBAHW,MAAM,YAAC,GACN,KAAK,CAKhB;IAED;;;;;;;;;;;;;OAaG;IACH,eAHW,MAAM,GACL,MAAM,GAAG,IAAI,CAexB;IAED;;;;;;OAMG;IACH,eAHW,MAAM,GACL,OAAO,CAUlB;IAED;;;;;;;;OAQG;IACH,kBAHa,OAAO,CAAC,kCAAa,IAAI,CAAC,CA0BtC;IAED;;;;OAIG;IACH,oCAkBC;IAED;;;;OAIG;IACH,mCAkBC;IAED;;;;OAIG;IACH,cAFa,OAAO,CAInB;;CACF;sBAhZqB,YAAY;wBACV,cAAc"}
1
+ {"version":3,"file":"BrowserApp.d.ts","sourceRoot":"","sources":["../../../js/BrowserApp.js"],"names":[],"mappings":"AAkBA;IAEE,gCAAgC;IAChC,yBADW,UAAU,GAAG,IAAI,CACZ;IAOhB;;;;;;;OAOG;IACH,6BAHW,MAAM,YAAC,GACN,OAAO,CAAC,UAAU,CAAC,CA4B9B;IAED;;;;;OAKG;IACH,qBAFW,MAAM,EAOhB;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAHY,OAAO,CAAC,UAAU,CAAC,CA6B9B;IAED;;;;;;;QAOI;IACJ,YAFY,OAAO,CAOlB;IAED;;;;;;;;OAQG;IACH,cAFY,MAAM,CAIjB;IAED;;;;;OAKG;IACH,aAFY,MAAM,CAIjB;IAED;;;;OAIG;IACH,YAFY,MAAM,CAIjB;IAED;;;;;OAKG;IACH,kBAFY,MAAM,CAIjB;IAED;;;;;;;OAOG;IACH,0BAHW,MAAM,GAAG,GAAG,GAAG,IAAI,GAClB,OAAO,CAAC,MAAM,CAAC,CAU1B;IAED;;;;;;;OAOG;IACH,aAFY,OAAO,CAAC,MAAM,CAAC,CAO1B;IAED;;;;;;;;OAQG;IACH,iBAHW,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,CAM3B;IAED;;;;;;OAMG;IACH,uBAHW,MAAM,GACL,MAAM,CAUjB;IAGD;;;;;;;;;OASG;IACH,iBAHW,MAAM,YAAC,GACN,KAAK,CAKhB;IAED;;;;;;;;;;;;;OAaG;IACH,eAHW,MAAM,GACL,MAAM,GAAG,IAAI,CAexB;IAED;;;;;;OAMG;IACH,eAHW,MAAM,GACL,OAAO,CAUlB;IAED;;;;;;;;OAQG;IACH,kBAHa,OAAO,CAAC,kCAAa,IAAI,CAAC,CA0BtC;IAED;;;;OAIG;IACH,oCAkBC;IAED;;;;OAIG;IACH,mCAkBC;IAED;;;;OAIG;IACH,cAFa,OAAO,CAInB;;CACF;sBAlZqB,YAAY;wBACV,cAAc"}
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "files": [
7
7
  "dist"
8
8
  ],
9
- "version": "14.3.0",
9
+ "version": "15.1.0",
10
10
  "description": "Web Daemon",
11
11
  "module": "./dist/index.js",
12
12
  "keywords": [