@clearly-hub/browser 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -131,7 +131,6 @@ store; it's split over several cookies because tokens are large.
131
131
 
132
132
  - Setting up your own workspaces or tenants: do it by hand, with plans that need approval, or in
133
133
  `onAnswer` for `SELECT_SUBSCRIPTION`.
134
- - Child hubs in `session().hubs`: Clearly.HUB only returns root hubs today.
135
134
  - Other server languages: follow the Build checklist.
136
135
 
137
136
  ## Working on this repo
@@ -1,4 +1,4 @@
1
- "use strict";(()=>{var C=Object.defineProperty;var v=(r,t,n)=>t in r?C(r,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):r[t]=n;var l=(r,t,n)=>v(r,typeof t!="symbol"?t+"":t,n);var x=["SELECT_SUBSCRIPTION","DATA_PICKER","REGISTER_RESOURCE","SELECT_HUB"];function g(r){return x.includes(r)}function S(r,t){if(r!=="REGISTER_RESOURCE")return;let{url:n,format:i}=t??{};if(typeof n!="string"||!n||typeof i!="string"||!i)return'REGISTER_RESOURCE needs data with a url and a format, for example { url, format: "geojson" }';let o;try{o=new URL(n).hostname}catch{return`REGISTER_RESOURCE: "${n}" is not a URL`}if(!o.includes("."))return"REGISTER_RESOURCE needs a url with a domain Clearly.HUB accepts, not localhost"}var E="/clearly-hub";var c={signIn:"/sign-in",callback:"/callback",signOut:"/sign-out",session:"/session",open:"/open",answer:"/answer",result:"/result"},b="clearly-hub-answers";var d="clearly-hub-answer",y=480,h=class{constructor(t={}){l(this,"basePath",E);l(this,"display","popup");l(this,"redirectResult");this.configure(t)}configure(t){return t.basePath&&(this.basePath=t.basePath.replace(/\/+$/,"")),t.display&&(this.display=t.display),this}async session(){let t=await fetch(this.url(c.session),{credentials:"same-origin"});if(!t.ok)throw new Error(`Clearly.HUB session failed (${t.status})`);return t.json()}signIn({returnTo:t=R()}={}){window.location.assign(this.url(c.signIn,{returnTo:t}))}signOut(){window.location.assign(this.url(c.signOut))}open(t,n={}){let i=S(t,n.data);if(i)return Promise.reject(new Error(`Clearly.HUB: ${i}`));let o=crypto.randomUUID(),e=n.display??this.display,s={action:t,ref:o,returnTo:n.returnTo??R(),data:n.data?JSON.stringify(n.data):void 0};if(e==="popup"){let a=window.open(this.url(c.open,{...s,display:"popup"}),"clearly-hub-screen",`width=${y},height=${screen.availHeight},left=${screen.availWidth-y}`);if(a)return this.waitForPopup(a,o,t)}return window.location.assign(this.url(c.open,{...s,display:"redirect"})),new Promise(()=>{})}result(){return this.redirectResult??(this.redirectResult=fetch(this.url(c.result),{credentials:"same-origin"}).then(t=>t.ok?t.json():null).then(t=>t?.result??null).catch(()=>null)),this.redirectResult}waitForPopup(t,n,i){return new Promise(o=>{let e=new BroadcastChannel(b),s=!1,a=u=>{s||(s=!0,e.close(),window.clearInterval(f),window.dispatchEvent(new CustomEvent(d,{detail:u})),o(u))};e.onmessage=u=>{u.data?.ref===n&&a(u.data.result)};let f=window.setInterval(()=>{t.closed&&(window.clearInterval(f),window.setTimeout(()=>a({action:i,result:"CANCELLED"}),500))},400)})}url(t,n={}){let i=new URLSearchParams;for(let[e,s]of Object.entries(n))s!==void 0&&i.set(e,s);let o=i.toString();return`${this.basePath}${t}${o?`?${o}`:""}`}};function R(){return`${window.location.pathname}${window.location.search}${window.location.hash}`}var m={"sign-in":"Sign in with Clearly.HUB","sign-out":"Sign out",SELECT_SUBSCRIPTION:"Choose a plan in Clearly.HUB",DATA_PICKER:"Pick data from Clearly.HUB",REGISTER_RESOURCE:"Save to Clearly.HUB",SELECT_HUB:"Choose a hub"},T=`
1
+ "use strict";(()=>{var C=Object.defineProperty;var T=(r,t,n)=>t in r?C(r,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):r[t]=n;var l=(r,t,n)=>T(r,typeof t!="symbol"?t+"":t,n);var x=["SELECT_SUBSCRIPTION","DATA_PICKER","REGISTER_RESOURCE","SELECT_HUB"];function b(r){return x.includes(r)}function E(r,t){if(r!=="REGISTER_RESOURCE")return;let{url:n,format:i}=t??{};if(typeof n!="string"||!n||typeof i!="string"||!i)return'REGISTER_RESOURCE needs data with a url and a format, for example { url, format: "geojson" }';let o;try{o=new URL(n).hostname}catch{return`REGISTER_RESOURCE: "${n}" is not a URL`}if(!o.includes("."))return"REGISTER_RESOURCE needs a url with a domain Clearly.HUB accepts, not localhost"}var S="/clearly-hub";var c={signIn:"/sign-in",callback:"/callback",signOut:"/sign-out",session:"/session",open:"/open",answer:"/answer",result:"/result"},y="clearly-hub-answers";var d="clearly-hub-answer",R=480,h=class{constructor(t={}){l(this,"basePath",S);l(this,"display","popup");l(this,"redirectResult");this.configure(t)}configure(t){return t.basePath&&(this.basePath=t.basePath.replace(/\/+$/,"")),t.display&&(this.display=t.display),this}async session(){let t=await fetch(this.url(c.session),{credentials:"same-origin"});if(!t.ok)throw new Error(`Clearly.HUB session failed (${t.status})`);return t.json()}signIn({returnTo:t=m()}={}){window.location.assign(this.url(c.signIn,{returnTo:t}))}signOut(){window.location.assign(this.url(c.signOut))}open(t,n={}){let i=E(t,n.data);if(i)return Promise.reject(new Error(`Clearly.HUB: ${i}`));let o=crypto.randomUUID(),e=n.display??this.display,s={action:t,ref:o,returnTo:n.returnTo??m(),data:n.data?JSON.stringify(n.data):void 0};if(e==="popup"){let a=window.open(this.url(c.open,{...s,display:"popup"}),"clearly-hub-screen",`width=${R},height=${screen.availHeight},left=${screen.availWidth-R}`);if(a)return this.waitForPopup(a,o,t,n.eventTarget??window)}return window.location.assign(this.url(c.open,{...s,display:"redirect"})),new Promise(()=>{})}result(){return this.redirectResult??(this.redirectResult=fetch(this.url(c.result),{credentials:"same-origin"}).then(t=>t.ok?t.json():null).then(t=>t?.result??null).catch(()=>null)),this.redirectResult}waitForPopup(t,n,i,o){return new Promise(e=>{let s=new BroadcastChannel(y),a=!1,f=u=>{a||(a=!0,s.close(),window.clearInterval(g),o.dispatchEvent(new CustomEvent(d,{detail:u,bubbles:!0,composed:!0})),e(u))};s.onmessage=u=>{u.data?.ref===n&&f(u.data.result)};let g=window.setInterval(()=>{t.closed&&(window.clearInterval(g),window.setTimeout(()=>f({action:i,result:"CANCELLED"}),500))},400)})}url(t,n={}){let i=new URLSearchParams;for(let[e,s]of Object.entries(n))s!==void 0&&i.set(e,s);let o=i.toString();return`${this.basePath}${t}${o?`?${o}`:""}`}};function m(){return`${window.location.pathname}${window.location.search}${window.location.hash}`}var A={"sign-in":"Sign in with Clearly.HUB","sign-out":"Sign out",SELECT_SUBSCRIPTION:"Choose a plan in Clearly.HUB",DATA_PICKER:"Pick data from Clearly.HUB",REGISTER_RESOURCE:"Save to Clearly.HUB",SELECT_HUB:"Choose a hub"},O=`
2
2
  :host { display: inline-block; }
3
3
  button {
4
4
  --color: var(--clearly-hub-color, #1fa69d);
@@ -20,4 +20,4 @@
20
20
  button:focus-visible { outline: 2px solid var(--color); outline-offset: 2px; }
21
21
  button:disabled { opacity: 0.6; cursor: default; }
22
22
  svg { width: 1.25em; height: 1.25em; flex: none; fill: currentColor; }
23
- `,O='<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M21 6.5c-1.66 0-3 1.34-3 3 0 .07 0 .14.01.21l-2.03.68c-.64-1.21-1.82-2.09-3.22-2.32V5.91C14.04 5.57 15 4.4 15 3c0-1.66-1.34-3-3-3S9 1.34 9 3c0 1.4.96 2.57 2.25 2.91v2.16c-1.4.23-2.58 1.11-3.22 2.32l-2.04-.68C6 9.64 6 9.57 6 9.5c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3c1.06 0 1.98-.55 2.52-1.37l2.03.68c-.2 1.29.17 2.66 1.09 3.69l-1.41 1.77Q6.66 17 6 17c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3c0-.68-.22-1.3-.6-1.8l1.41-1.77c1.36.76 3.02.75 4.37 0l1.41 1.77c-.37.5-.59 1.12-.59 1.8 0 1.66 1.34 3 3 3s3-1.34 3-3-1.34-3-3-3c-.44 0-.85.09-1.23.26l-1.41-1.77c.93-1.04 1.29-2.4 1.09-3.69l2.03-.68c.53.82 1.46 1.37 2.52 1.37 1.66 0 3-1.34 3-3S22.66 6.5 21 6.5m-18 4c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1M6 21c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1m5-18c0-.55.45-1 1-1s1 .45 1 1-.45 1-1 1-1-.45-1-1m1 12c-1.38 0-2.5-1.12-2.5-2.5S10.62 10 12 10s2.5 1.12 2.5 2.5S13.38 15 12 15m6 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1m3-8.5c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1"/></svg>';function A(r,t="clearly-hub-button"){if(typeof customElements>"u"||customElements.get(t))return;class n extends HTMLElement{constructor(){super();l(this,"button");let e=this.attachShadow({mode:"open"});e.innerHTML=`<style>${T}</style><button part="button" type="button"></button>`,this.button=e.querySelector("button"),this.button.addEventListener("click",()=>this.run())}connectedCallback(){for(let e of["action","display","label","variant","size","data"])if(Object.prototype.hasOwnProperty.call(this,e)){let s=this[e];delete this[e],this[e]=s}this.render()}attributeChangedCallback(){this.render()}get action(){let e=this.getAttribute("action")??"sign-in";return e in m?e:"sign-in"}set action(e){this.setOrRemove("action",e)}get display(){return this.getAttribute("display")==="redirect"?"redirect":void 0}set display(e){this.setOrRemove("display",e)}get label(){return this.getAttribute("label")}set label(e){this.setOrRemove("label",e)}get variant(){return this.getAttribute("variant")}set variant(e){this.setOrRemove("variant",e)}get size(){return this.getAttribute("size")}set size(e){this.setOrRemove("size",e)}get data(){let e=this.getAttribute("data");return e?JSON.parse(e):void 0}set data(e){this.setOrRemove("data",typeof e=="string"||e==null?e:JSON.stringify(e))}setOrRemove(e,s){s==null?this.removeAttribute(e):this.setAttribute(e,s)}render(){let e=this.label??m[this.action];this.button.innerHTML=`${O}<span></span>`,this.button.querySelector("span").textContent=e}async run(){let e=this.action;if(e==="sign-in")return r.signIn();if(e==="sign-out")return r.signOut();if(!g(e))return;this.button.disabled=!0;let s;try{s=await r.open(e,{display:this.display,data:this.data})}catch(a){console.error(a),s={action:e,result:"FAILURE",error:a.message}}finally{this.button.disabled=!1}this.dispatchEvent(new CustomEvent(d,{detail:s,bubbles:!0,composed:!0}))}}l(n,"observedAttributes",["action","label"]),customElements.define(t,n)}var p=new h;A(p);typeof window<"u"&&queueMicrotask(()=>{p.result().then(r=>{r&&window.dispatchEvent(new CustomEvent(d,{detail:r}))})});var w=document.currentScript;p.configure({basePath:w?.dataset.basePath,display:w?.dataset.display==="redirect"?"redirect":void 0});window.clearlyHub=p;})();
23
+ `,U='<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M21 6.5c-1.66 0-3 1.34-3 3 0 .07 0 .14.01.21l-2.03.68c-.64-1.21-1.82-2.09-3.22-2.32V5.91C14.04 5.57 15 4.4 15 3c0-1.66-1.34-3-3-3S9 1.34 9 3c0 1.4.96 2.57 2.25 2.91v2.16c-1.4.23-2.58 1.11-3.22 2.32l-2.04-.68C6 9.64 6 9.57 6 9.5c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3c1.06 0 1.98-.55 2.52-1.37l2.03.68c-.2 1.29.17 2.66 1.09 3.69l-1.41 1.77Q6.66 17 6 17c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3c0-.68-.22-1.3-.6-1.8l1.41-1.77c1.36.76 3.02.75 4.37 0l1.41 1.77c-.37.5-.59 1.12-.59 1.8 0 1.66 1.34 3 3 3s3-1.34 3-3-1.34-3-3-3c-.44 0-.85.09-1.23.26l-1.41-1.77c.93-1.04 1.29-2.4 1.09-3.69l2.03-.68c.53.82 1.46 1.37 2.52 1.37 1.66 0 3-1.34 3-3S22.66 6.5 21 6.5m-18 4c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1M6 21c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1m5-18c0-.55.45-1 1-1s1 .45 1 1-.45 1-1 1-1-.45-1-1m1 12c-1.38 0-2.5-1.12-2.5-2.5S10.62 10 12 10s2.5 1.12 2.5 2.5S13.38 15 12 15m6 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1m3-8.5c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1"/></svg>';function w(r,t="clearly-hub-button"){if(typeof customElements>"u"||customElements.get(t))return;class n extends HTMLElement{constructor(){super();l(this,"button");let e=this.attachShadow({mode:"open"});e.innerHTML=`<style>${O}</style><button part="button" type="button"></button>`,this.button=e.querySelector("button"),this.button.addEventListener("click",()=>this.run())}connectedCallback(){for(let e of["action","display","label","variant","size","data"])if(Object.prototype.hasOwnProperty.call(this,e)){let s=this[e];delete this[e],this[e]=s}this.render()}attributeChangedCallback(){this.render()}get action(){let e=this.getAttribute("action")??"sign-in";return e in A?e:"sign-in"}set action(e){this.setOrRemove("action",e)}get display(){return this.getAttribute("display")==="redirect"?"redirect":void 0}set display(e){this.setOrRemove("display",e)}get label(){return this.getAttribute("label")}set label(e){this.setOrRemove("label",e)}get variant(){return this.getAttribute("variant")}set variant(e){this.setOrRemove("variant",e)}get size(){return this.getAttribute("size")}set size(e){this.setOrRemove("size",e)}get data(){let e=this.getAttribute("data");return e?JSON.parse(e):void 0}set data(e){this.setOrRemove("data",typeof e=="string"||e==null?e:JSON.stringify(e))}setOrRemove(e,s){s==null?this.removeAttribute(e):this.setAttribute(e,s)}render(){let e=this.label??A[this.action];this.button.innerHTML=`${U}<span></span>`,this.button.querySelector("span").textContent=e}async run(){let e=this.action;if(e==="sign-in")return r.signIn();if(e==="sign-out")return r.signOut();if(b(e)){this.button.disabled=!0;try{await r.open(e,{display:this.display,data:this.data,eventTarget:this})}catch(s){console.error(s);let a={action:e,result:"FAILURE",error:s.message};this.dispatchEvent(new CustomEvent(d,{detail:a,bubbles:!0,composed:!0}))}finally{this.button.disabled=!1}}}}l(n,"observedAttributes",["action","label"]),customElements.define(t,n)}var p=new h;w(p);typeof window<"u"&&queueMicrotask(()=>{p.result().then(r=>{r&&window.dispatchEvent(new CustomEvent(d,{detail:r}))})});var v=document.currentScript;p.configure({basePath:v?.dataset.basePath,display:v?.dataset.display==="redirect"?"redirect":void 0});window.clearlyHub=p;})();
package/dist/index.d.ts CHANGED
@@ -47,6 +47,7 @@ interface OpenOptions<A extends ScreenAction> {
47
47
  display?: ScreenDisplay;
48
48
  data?: A extends keyof ScreenData ? ScreenData[A] : never;
49
49
  returnTo?: string;
50
+ eventTarget?: EventTarget;
50
51
  }
51
52
  declare const ANSWER_EVENT = "clearly-hub-answer";
52
53
  declare class ClearlyHubClient {
package/dist/index.js CHANGED
@@ -90,7 +90,7 @@ var ClearlyHubClient = class {
90
90
  "clearly-hub-screen",
91
91
  `width=${POPUP_WIDTH},height=${screen.availHeight},left=${screen.availWidth - POPUP_WIDTH}`
92
92
  );
93
- if (popup) return this.waitForPopup(popup, ref, action);
93
+ if (popup) return this.waitForPopup(popup, ref, action, options.eventTarget ?? window);
94
94
  }
95
95
  window.location.assign(this.url(ROUTES.open, { ...query, display: "redirect" }));
96
96
  return new Promise(() => {
@@ -102,7 +102,7 @@ var ClearlyHubClient = class {
102
102
  this.redirectResult ?? (this.redirectResult = fetch(this.url(ROUTES.result), { credentials: "same-origin" }).then((response) => response.ok ? response.json() : null).then((message) => message?.result ?? null).catch(() => null));
103
103
  return this.redirectResult;
104
104
  }
105
- waitForPopup(popup, ref, action) {
105
+ waitForPopup(popup, ref, action, eventTarget) {
106
106
  return new Promise((resolve) => {
107
107
  const channel = new BroadcastChannel(ANSWER_CHANNEL);
108
108
  let isDone = false;
@@ -111,7 +111,9 @@ var ClearlyHubClient = class {
111
111
  isDone = true;
112
112
  channel.close();
113
113
  window.clearInterval(closedCheck);
114
- window.dispatchEvent(new CustomEvent(ANSWER_EVENT, { detail: result }));
114
+ eventTarget.dispatchEvent(
115
+ new CustomEvent(ANSWER_EVENT, { detail: result, bubbles: true, composed: true })
116
+ );
115
117
  resolve(result);
116
118
  };
117
119
  channel.onmessage = (event) => {
@@ -254,21 +256,21 @@ function defineButton(client, tagName = "clearly-hub-button") {
254
256
  if (action === "sign-out") return client.signOut();
255
257
  if (!isScreenAction(action)) return;
256
258
  this.button.disabled = true;
257
- let result;
258
259
  try {
259
- result = await client.open(action, {
260
+ await client.open(action, {
260
261
  display: this.display,
261
- data: this.data
262
+ data: this.data,
263
+ eventTarget: this
262
264
  });
263
265
  } catch (error) {
264
266
  console.error(error);
265
- result = { action, result: "FAILURE", error: error.message };
267
+ const result = { action, result: "FAILURE", error: error.message };
268
+ this.dispatchEvent(
269
+ new CustomEvent(ANSWER_EVENT, { detail: result, bubbles: true, composed: true })
270
+ );
266
271
  } finally {
267
272
  this.button.disabled = false;
268
273
  }
269
- this.dispatchEvent(
270
- new CustomEvent(ANSWER_EVENT, { detail: result, bubbles: true, composed: true })
271
- );
272
274
  }
273
275
  }
274
276
  __publicField(ClearlyHubButton, "observedAttributes", ["action", "label"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clearly-hub/browser",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Clearly.HUB in the browser: sign-in, hosted screens and the session, talking to @clearly-hub/node",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Future Insight Group BV",