@clearly-hub/browser 0.1.0 → 0.1.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
@@ -43,7 +43,8 @@ Hono, Bun or Deno. Mount it at `/clearly-hub/*`.
43
43
  **3. Add the script and a button:**
44
44
 
45
45
  ```html
46
- <script src="https://cdn.jsdelivr.net/npm/@clearly-hub/browser/dist/clearly-hub.js"></script>
46
+ <!-- @0.1: follows 0.1.x fixes, not a breaking 0.2 -->
47
+ <script src="https://cdn.jsdelivr.net/npm/@clearly-hub/browser@0.1/dist/clearly-hub.js"></script>
47
48
 
48
49
  <clearly-hub-button action="sign-in"></clearly-hub-button>
49
50
  <clearly-hub-button action="DATA_PICKER"></clearly-hub-button>
@@ -1,4 +1,4 @@
1
- "use strict";(()=>{var R=Object.defineProperty;var C=(r,e,n)=>e in r?R(r,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):r[e]=n;var a=(r,e,n)=>C(r,typeof e!="symbol"?e+"":e,n);var x=["SELECT_SUBSCRIPTION","DATA_PICKER","REGISTER_RESOURCE","SELECT_HUB"];function f(r){return x.includes(r)}var S="/clearly-hub";var c={signIn:"/sign-in",callback:"/callback",signOut:"/sign-out",session:"/session",open:"/open",answer:"/answer",result:"/result"},g="clearly-hub-answers";var u="clearly-hub-answer",E=480,b=class{constructor(e={}){a(this,"basePath",S);a(this,"display","popup");a(this,"redirectResult");this.configure(e)}configure(e){return e.basePath&&(this.basePath=e.basePath.replace(/\/+$/,"")),e.display&&(this.display=e.display),this}async session(){let e=await fetch(this.url(c.session),{credentials:"same-origin"});if(!e.ok)throw new Error(`Clearly.HUB session failed (${e.status})`);return e.json()}signIn({returnTo:e=y()}={}){window.location.assign(this.url(c.signIn,{returnTo:e}))}signOut(){window.location.assign(this.url(c.signOut))}open(e,n={}){let i=crypto.randomUUID(),o=n.display??this.display,t={action:e,ref:i,returnTo:n.returnTo??y(),data:n.data?JSON.stringify(n.data):void 0};if(o==="popup"){let s=window.open(this.url(c.open,{...t,display:"popup"}),"clearly-hub-screen",`width=${E},height=${screen.availHeight},left=${screen.availWidth-E}`);if(s)return this.waitForPopup(s,i,e)}return window.location.assign(this.url(c.open,{...t,display:"redirect"})),new Promise(()=>{})}result(){return this.redirectResult??(this.redirectResult=fetch(this.url(c.result),{credentials:"same-origin"}).then(e=>e.ok?e.json():null).then(e=>e?.result??null).catch(()=>null)),this.redirectResult}waitForPopup(e,n,i){return new Promise(o=>{let t=new BroadcastChannel(g),s=!1,d=l=>{s||(s=!0,t.close(),window.clearInterval(h),window.dispatchEvent(new CustomEvent(u,{detail:l})),o(l))};t.onmessage=l=>{l.data?.ref===n&&d(l.data.result)};let h=window.setInterval(()=>{e.closed&&(window.clearInterval(h),window.setTimeout(()=>d({action:i,result:"CANCELLED"}),500))},400)})}url(e,n={}){let i=new URLSearchParams;for(let[t,s]of Object.entries(n))s!==void 0&&i.set(t,s);let o=i.toString();return`${this.basePath}${e}${o?`?${o}`:""}`}};function y(){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"},v=`
1
+ "use strict";(()=>{var C=Object.defineProperty;var x=(r,e,t)=>e in r?C(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var l=(r,e,t)=>x(r,typeof e!="symbol"?e+"":e,t);var T=["SELECT_SUBSCRIPTION","DATA_PICKER","REGISTER_RESOURCE","SELECT_HUB"];function E(r){return T.includes(r)}function S(r,e){if(r!=="REGISTER_RESOURCE")return;let{url:t,format:s}=e??{};if(typeof t!="string"||!t||typeof s!="string"||!s)return'REGISTER_RESOURCE needs data with a url and a format, for example { url, format: "geojson" }';if(!t.startsWith("https://"))return"REGISTER_RESOURCE needs a public https url: Clearly.HUB refuses other addresses"}var b="/clearly-hub";var u={signIn:"/sign-in",callback:"/callback",signOut:"/sign-out",session:"/session",open:"/open",answer:"/answer",result:"/result"},g="clearly-hub-answers";var p="clearly-hub-answer",y=480,f=class{constructor(e={}){l(this,"basePath",b);l(this,"display","popup");l(this,"redirectResult");this.configure(e)}configure(e){return e.basePath&&(this.basePath=e.basePath.replace(/\/+$/,"")),e.display&&(this.display=e.display),this}async session(){let e=await fetch(this.url(u.session),{credentials:"same-origin"});if(!e.ok)throw new Error(`Clearly.HUB session failed (${e.status})`);return e.json()}signIn({returnTo:e=A()}={}){window.location.assign(this.url(u.signIn,{returnTo:e}))}signOut(){window.location.assign(this.url(u.signOut))}open(e,t={}){let s=S(e,t.data);if(s)return Promise.reject(new Error(`Clearly.HUB: ${s}`));let o=crypto.randomUUID(),n=t.display??this.display,i={action:e,ref:o,returnTo:t.returnTo??A(),data:t.data?JSON.stringify(t.data):void 0};if(n==="popup"){let c=window.open(this.url(u.open,{...i,display:"popup"}),"clearly-hub-screen",`width=${y},height=${screen.availHeight},left=${screen.availWidth-y}`);if(c)return this.waitForPopup(c,o,e)}return window.location.assign(this.url(u.open,{...i,display:"redirect"})),new Promise(()=>{})}result(){return this.redirectResult??(this.redirectResult=fetch(this.url(u.result),{credentials:"same-origin"}).then(e=>e.ok?e.json():null).then(e=>e?.result??null).catch(()=>null)),this.redirectResult}waitForPopup(e,t,s){return new Promise(o=>{let n=new BroadcastChannel(g),i=!1,c=a=>{i||(i=!0,n.close(),window.clearInterval(d),window.dispatchEvent(new CustomEvent(p,{detail:a})),o(a))};n.onmessage=a=>{a.data?.ref===t&&c(a.data.result)};let d=window.setInterval(()=>{e.closed&&(window.clearInterval(d),window.setTimeout(()=>c({action:s,result:"CANCELLED"}),500))},400)})}url(e,t={}){let s=new URLSearchParams;for(let[n,i]of Object.entries(t))i!==void 0&&s.set(n,i);let o=s.toString();return`${this.basePath}${e}${o?`?${o}`:""}`}};function A(){return`${window.location.pathname}${window.location.search}${window.location.hash}`}var R={"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"},v=`
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
- `,T='<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 m(r,e="clearly-hub-button"){if(typeof customElements>"u"||customElements.get(e))return;class n extends HTMLElement{constructor(){super();a(this,"button");let t=this.attachShadow({mode:"open"});t.innerHTML=`<style>${v}</style><button part="button" type="button"></button>`,this.button=t.querySelector("button"),this.button.addEventListener("click",()=>this.run())}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}get action(){let t=this.getAttribute("action")??"sign-in";return t in A?t:"sign-in"}render(){let t=this.getAttribute("label")??A[this.action];this.button.innerHTML=`${T}<span></span>`,this.button.querySelector("span").textContent=t}async run(){let t=this.action;if(t==="sign-in")return r.signIn();if(t==="sign-out")return r.signOut();if(!f(t))return;let s=this.getAttribute("data"),d=this.getAttribute("display")==="redirect"?"redirect":void 0;this.button.disabled=!0;try{let h=await r.open(t,{display:d,data:s?JSON.parse(s):void 0});this.dispatchEvent(new CustomEvent(u,{detail:h,bubbles:!0,composed:!0}))}finally{this.button.disabled=!1}}}a(n,"observedAttributes",["action","label"]),customElements.define(e,n)}var p=new b;m(p);typeof window<"u"&&queueMicrotask(()=>{p.result().then(r=>{r&&window.dispatchEvent(new CustomEvent(u,{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 m(r,e="clearly-hub-button"){if(typeof customElements>"u"||customElements.get(e))return;class t extends HTMLElement{constructor(){super();l(this,"button");let n=this.attachShadow({mode:"open"});n.innerHTML=`<style>${v}</style><button part="button" type="button"></button>`,this.button=n.querySelector("button"),this.button.addEventListener("click",()=>this.run())}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}get action(){let n=this.getAttribute("action")??"sign-in";return n in R?n:"sign-in"}render(){let n=this.getAttribute("label")??R[this.action];this.button.innerHTML=`${U}<span></span>`,this.button.querySelector("span").textContent=n}async run(){let n=this.action;if(n==="sign-in")return r.signIn();if(n==="sign-out")return r.signOut();if(!E(n))return;let i=this.getAttribute("data"),c=this.getAttribute("display")==="redirect"?"redirect":void 0;this.button.disabled=!0;let d;try{d=await r.open(n,{display:c,data:i?JSON.parse(i):void 0})}catch(a){console.error(a),d={action:n,result:"FAILURE",error:a.message}}finally{this.button.disabled=!1}this.dispatchEvent(new CustomEvent(p,{detail:d,bubbles:!0,composed:!0}))}}l(t,"observedAttributes",["action","label"]),customElements.define(e,t)}var h=new f;m(h);typeof window<"u"&&queueMicrotask(()=>{h.result().then(r=>{r&&window.dispatchEvent(new CustomEvent(p,{detail:r}))})});var w=document.currentScript;h.configure({basePath:w?.dataset.basePath,display:w?.dataset.display==="redirect"?"redirect":void 0});window.clearlyHub=h;})();
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ interface ScreenData {
8
8
  format: string;
9
9
  };
10
10
  }
11
+ declare function checkScreenData(action: ScreenAction, data: unknown): string | undefined;
11
12
  interface HubRef {
12
13
  _id: string;
13
14
  name: string;
@@ -135,5 +136,5 @@ declare function defineButton(client: ClearlyHubClient, tagName?: string): void;
135
136
 
136
137
  declare const clearlyHub: ClearlyHubClient;
137
138
 
138
- export { ANSWER_CHANNEL, ANSWER_EVENT, ClearlyHubClient, DEFAULT_AUTH_URL, DEFAULT_BASE_PATH, DEFAULT_HUB_URL, DEFAULT_ISSUER, ROUTES, SCREEN_ACTIONS, clearlyHub, defineButton, isScreenAction };
139
+ export { ANSWER_CHANNEL, ANSWER_EVENT, ClearlyHubClient, DEFAULT_AUTH_URL, DEFAULT_BASE_PATH, DEFAULT_HUB_URL, DEFAULT_ISSUER, ROUTES, SCREEN_ACTIONS, checkScreenData, clearlyHub, defineButton, isScreenAction };
139
140
  export type { AnswerMessage, ClientOptions, HubRef, HubSubscription, OpenOptions, PickedResource, ScreenAction, ScreenAnswers, ScreenData, ScreenDisplay, ScreenResult, Session, SessionUser };
package/dist/index.js CHANGED
@@ -12,6 +12,17 @@ var SCREEN_ACTIONS = [
12
12
  function isScreenAction(value) {
13
13
  return SCREEN_ACTIONS.includes(value);
14
14
  }
15
+ function checkScreenData(action, data) {
16
+ if (action !== "REGISTER_RESOURCE") return void 0;
17
+ const { url, format } = data ?? {};
18
+ if (typeof url !== "string" || !url || typeof format !== "string" || !format) {
19
+ return 'REGISTER_RESOURCE needs data with a url and a format, for example { url, format: "geojson" }';
20
+ }
21
+ if (!url.startsWith("https://")) {
22
+ return "REGISTER_RESOURCE needs a public https url: Clearly.HUB refuses other addresses";
23
+ }
24
+ return void 0;
25
+ }
15
26
 
16
27
  // ../shared/src/protocol.ts
17
28
  var DEFAULT_BASE_PATH = "/clearly-hub";
@@ -57,6 +68,8 @@ var ClearlyHubClient = class {
57
68
  // click handler: browsers only allow popups there. A blocked popup falls
58
69
  // back to a redirect.
59
70
  open(action, options = {}) {
71
+ const dataProblem = checkScreenData(action, options.data);
72
+ if (dataProblem) return Promise.reject(new Error(`Clearly.HUB: ${dataProblem}`));
60
73
  const ref = crypto.randomUUID();
61
74
  const display = options.display ?? this.display;
62
75
  const query = {
@@ -185,17 +198,21 @@ function defineButton(client, tagName = "clearly-hub-button") {
185
198
  const data = this.getAttribute("data");
186
199
  const display = this.getAttribute("display") === "redirect" ? "redirect" : void 0;
187
200
  this.button.disabled = true;
201
+ let result;
188
202
  try {
189
- const result = await client.open(action, {
203
+ result = await client.open(action, {
190
204
  display,
191
205
  data: data ? JSON.parse(data) : void 0
192
206
  });
193
- this.dispatchEvent(
194
- new CustomEvent(ANSWER_EVENT, { detail: result, bubbles: true, composed: true })
195
- );
207
+ } catch (error) {
208
+ console.error(error);
209
+ result = { action, result: "FAILURE", error: error.message };
196
210
  } finally {
197
211
  this.button.disabled = false;
198
212
  }
213
+ this.dispatchEvent(
214
+ new CustomEvent(ANSWER_EVENT, { detail: result, bubbles: true, composed: true })
215
+ );
199
216
  }
200
217
  }
201
218
  __publicField(ClearlyHubButton, "observedAttributes", ["action", "label"]);
package/package.json CHANGED
@@ -1,10 +1,16 @@
1
1
  {
2
2
  "name": "@clearly-hub/browser",
3
- "version": "0.1.0",
3
+ "version": "0.1.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",
7
- "keywords": ["clearly-hub", "oup", "open-urban-platform", "web-component", "sdk"],
7
+ "keywords": [
8
+ "clearly-hub",
9
+ "oup",
10
+ "open-urban-platform",
11
+ "web-component",
12
+ "sdk"
13
+ ],
8
14
  "type": "module",
9
15
  "module": "./dist/index.js",
10
16
  "types": "./dist/index.d.ts",
@@ -31,6 +37,6 @@
31
37
  "prepack": "node ../../scripts/copy-license.mjs && npm run build"
32
38
  },
33
39
  "devDependencies": {
34
- "@clearly-hub/shared": "0.1.0"
40
+ "@clearly-hub/shared": "*"
35
41
  }
36
42
  }