@withone/connect 0.9.0 → 0.11.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/README.md +88 -7
- package/dist/button.d.ts +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/server/index.cjs.js +145 -35
- package/dist/server/index.d.ts +11 -3
- package/dist/server/index.esm.js +145 -36
- package/dist/server/oauth.d.ts +4 -0
- package/dist/server/types.d.ts +41 -3
- package/dist/types.d.ts +2 -4
- package/dist/vue.cjs.js +1 -1
- package/dist/vue.d.ts +0 -9
- package/dist/vue.esm.js +1 -1
- package/dist/wrapper-options.d.ts +0 -1
- package/package.json +1 -1
- package/skills/one-connect/SKILL.md +22 -4
- package/src/button.ts +13 -14
- package/src/server/index.ts +176 -41
- package/src/server/oauth.ts +16 -0
- package/src/server/types.ts +42 -3
- package/src/types.ts +3 -6
- package/src/vue.ts +0 -1
- package/src/wrapper-options.ts +0 -3
package/README.md
CHANGED
|
@@ -87,7 +87,6 @@ import { ConnectButton } from "@withone/connect/react";
|
|
|
87
87
|
<ConnectButton
|
|
88
88
|
authorizeUrl="/api/one/authorize"
|
|
89
89
|
platforms={["stripe", "google-calendar", "gmail"]}
|
|
90
|
-
moreCount={274}
|
|
91
90
|
onSuccess={() => refreshAppState()}
|
|
92
91
|
onError={(message) => showBanner(message)}
|
|
93
92
|
/>
|
|
@@ -113,7 +112,7 @@ import { ConnectButton } from "@withone/connect/vue";
|
|
|
113
112
|
|
|
114
113
|
```html
|
|
115
114
|
<!-- Plain HTML or any other framework: importing the package registers the element -->
|
|
116
|
-
<one-connect-button authorize-url="/api/one/authorize" platforms="stripe, notion"
|
|
115
|
+
<one-connect-button authorize-url="/api/one/authorize" platforms="stripe, notion"></one-connect-button>
|
|
117
116
|
<script type="module">
|
|
118
117
|
import "@withone/connect";
|
|
119
118
|
document.querySelector("one-connect-button").addEventListener("success", () => location.reload());
|
|
@@ -123,8 +122,7 @@ import { ConnectButton } from "@withone/connect/vue";
|
|
|
123
122
|
| Prop (attribute) | What it does |
|
|
124
123
|
|---|---|
|
|
125
124
|
| `authorizeUrl` (`authorize-url`) | Your authorize route. Relative paths resolve against the page. Required. |
|
|
126
|
-
| `platforms` | Connector slugs: `["stripe", "google-calendar"]`. Logos and names come from One. To override either, pass `{ slug, name, imageUrl }`. As an attribute: `"stripe, notion"`. |
|
|
127
|
-
| `moreCount` (`more-count`) | The `+N` chip after the first three logos |
|
|
125
|
+
| `platforms` | Connector slugs: `["stripe", "google-calendar"]`. Logos and names come from One. To override either, pass `{ slug, name, imageUrl }`. As an attribute: `"stripe, notion"`. The first three render as logos; anything beyond them becomes a `+N` chip. |
|
|
128
126
|
| `label` | Button text. Default "Connect your apps". |
|
|
129
127
|
| `connectedLabel` (`connected-label`) | Text after a successful return. Default "Connected". |
|
|
130
128
|
| `variant` | `default` pill · `accent` brand-colored pill · `block` card with a description |
|
|
@@ -168,6 +166,8 @@ export const oneConnect = createOneConnect({
|
|
|
168
166
|
saveTokens: (userId, tokens) => db.oneTokens.upsert(userId, tokens),
|
|
169
167
|
loadTokens: (userId) => db.oneTokens.find(userId),
|
|
170
168
|
clearTokens: (userId) => db.oneTokens.delete(userId),
|
|
169
|
+
// Required when you run more than one server or a worker. See "Tokens" below.
|
|
170
|
+
withLock: (userId, run) => db.withUserLock(userId, run),
|
|
171
171
|
},
|
|
172
172
|
});
|
|
173
173
|
```
|
|
@@ -209,7 +209,7 @@ What the routes do for you: mint `state` and a PKCE verifier, keep them in a per
|
|
|
209
209
|
|
|
210
210
|
## 4 · Using the grant
|
|
211
211
|
|
|
212
|
-
Everything runs on your server through the same client.
|
|
212
|
+
Everything runs on your server through the same client. Every call refreshes the tokens first when they are about to expire (see [Tokens](#5--tokens-storing-refreshing-keeping-alive)).
|
|
213
213
|
|
|
214
214
|
```ts
|
|
215
215
|
// What the grant reaches, each connection with its access
|
|
@@ -233,7 +233,88 @@ const reply = await oneConnect.runAction(userId, {
|
|
|
233
233
|
|
|
234
234
|
`blockedByGrant` is true when One refused the call because it is outside what the user granted. The provider was never called. Do not retry; the user chose that. Anything else on One's `/v1` API: `oneConnect.fetch(userId, "/connections", init)` adds the bearer and the tenancy headers for you.
|
|
235
235
|
|
|
236
|
-
Other calls on the client: `isConnected`, `getAccessToken`, `getTokens`, `refreshTokens`, `disconnect`.
|
|
236
|
+
Other calls on the client: `isConnected`, `getAccessToken`, `getTokens`, `refreshTokens`, `refreshIfExpiring`, `disconnect`.
|
|
237
|
+
|
|
238
|
+
## 5 · Tokens: storing, refreshing, keeping alive
|
|
239
|
+
|
|
240
|
+
Your app owns the tokens. The SDK never stores anything itself: it calls your `tokenStore`, and it refreshes through it.
|
|
241
|
+
|
|
242
|
+
**What One issues**
|
|
243
|
+
|
|
244
|
+
| | Lifetime | On refresh |
|
|
245
|
+
|---|---|---|
|
|
246
|
+
| Access token | The lifetime set on your app: 1 hour by default, or 7, 30, 90 or 365 days | Replaced |
|
|
247
|
+
| Refresh token | 30 days | Replaced, with a fresh 30 days |
|
|
248
|
+
|
|
249
|
+
Every refresh returns a **new pair** and retires the old refresh token. If the old refresh token is ever used again, One treats it as stolen and **revokes the whole grant**. The user then has to connect again.
|
|
250
|
+
|
|
251
|
+
**Store them in your database, encrypted, keyed by your user id**
|
|
252
|
+
|
|
253
|
+
```sql
|
|
254
|
+
create table one_tokens (
|
|
255
|
+
user_id text primary key,
|
|
256
|
+
access_token text not null, -- encrypted
|
|
257
|
+
refresh_token text not null, -- encrypted
|
|
258
|
+
expires_at timestamptz not null -- tokens.expiresAt
|
|
259
|
+
);
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Give the store a lock when more than one process can refresh**
|
|
263
|
+
|
|
264
|
+
Serverless functions, several instances, a background worker: any two of them can decide to refresh the same user at the same moment.
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
without a lock with withLock
|
|
268
|
+
web ──refresh(R1)──► One: here is R2 web ──lock──refresh(R1)──► R2 ──save──unlock
|
|
269
|
+
worker ─refresh(R1)─► One: R1 reused! worker ──wait─────────────────────────────┐
|
|
270
|
+
revoke everything ✗ reload: R2 is fresh, use it ✓
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The SDK takes the lock around every refresh and around the save in the callback. It re-reads the store inside the lock, so the process that waited uses the pair the first one saved instead of spending the old refresh token again. The SDK never takes the lock twice for the same call. Postgres, with a transaction-scoped advisory lock:
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
withLock: async (userId, run) => {
|
|
277
|
+
const client = await pool.connect();
|
|
278
|
+
try {
|
|
279
|
+
await client.query("begin");
|
|
280
|
+
await client.query("select pg_advisory_xact_lock(hashtextextended($1, 0))", [`one-connect:${userId}`]);
|
|
281
|
+
return await run();
|
|
282
|
+
} finally {
|
|
283
|
+
await client.query("commit").catch(() => {});
|
|
284
|
+
client.release();
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
A single long-running process can leave `withLock` out; the SDK already runs one refresh per user at a time inside a process. If your lock has a timeout, make it at least 60 seconds, because it spans one call to One.
|
|
290
|
+
|
|
291
|
+
**Refreshing ahead of time**
|
|
292
|
+
|
|
293
|
+
`getAccessToken`, `runAction`, `listConnections` and `fetch` refresh on their own when the access token has less than a minute left. For work that runs in the background, refresh ahead with `refreshIfExpiring`. It refreshes only when the access token **or** the refresh token expires within the window, and otherwise returns the stored pair without calling One.
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
// Every 10 minutes: users whose access token expires in the next 15 minutes
|
|
297
|
+
// (your table knows expires_at).
|
|
298
|
+
await oneConnect.refreshIfExpiring(userId, { withinMs: 15 * 60_000 });
|
|
299
|
+
|
|
300
|
+
// Once a day, for every connected user: renews refresh tokens before their
|
|
301
|
+
// 30 days run out, so a user who has not been active stays connected.
|
|
302
|
+
await oneConnect.refreshIfExpiring(userId, { withinMs: 7 * 24 * 3_600_000 });
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**When a refresh fails**
|
|
306
|
+
|
|
307
|
+
| `OneConnectError.code` | What happened | Tokens | What to do |
|
|
308
|
+
|---|---|---|---|
|
|
309
|
+
| `refresh_failed` | One declared the grant dead: the user revoked it, the refresh token expired, or it was reused | Cleared | Show "Reconnect", which runs Connect again |
|
|
310
|
+
| `request_failed` | One could not be reached, answered with a server error, or refused your client credentials | **Kept** | Retry later. Check `status` for a 401, which means your client secret is wrong. |
|
|
311
|
+
| `not_connected` | No tokens are stored for this user | — | Show "Connect" |
|
|
312
|
+
|
|
313
|
+
The SDK clears tokens only when One says the grant is dead (`invalid_grant`), or when the refresh token's own expiry has passed. It calls `clearTokens(userId, failed)` with the pair that failed. Just before that, it re-reads the store: if a newer pair has landed (the user reconnected while an old refresh was failing), it keeps and returns the newer pair. To make that check atomic, delete only when the stored refresh token is still `failed.refreshToken`. `disconnect(userId)` calls `clearTokens(userId)` without `failed`, and always deletes.
|
|
314
|
+
|
|
315
|
+
**Asking for more tools later**
|
|
316
|
+
|
|
317
|
+
Edit the app's permission set in the dashboard (Developers → Connect). The next time a user presses the button, One shows them the new tools as "{your app} needs one more connection", with what they already granted pre-selected. Authorizing replaces the user's grant and old tokens at once, and the callback saves the new pair.
|
|
237
318
|
|
|
238
319
|
## What your users see
|
|
239
320
|
|
|
@@ -245,7 +326,7 @@ In *your* dashboard the app lists every user who said yes, what each one granted
|
|
|
245
326
|
|
|
246
327
|
- The client secret is used on your server only, for the code exchange and refresh, over HTTP Basic.
|
|
247
328
|
- The authorization code is single use and expires ten minutes after consent.
|
|
248
|
-
- Refresh tokens rotate on every use. Reusing an old one revokes the whole family, which is why the client
|
|
329
|
+
- Refresh tokens rotate on every use. Reusing an old one revokes the whole family, which is why the client refreshes one user at a time: inside a process on its own, and across processes through `tokenStore.withLock`.
|
|
249
330
|
- The browser half of this package never sees a token. It navigates and reads one query parameter.
|
|
250
331
|
|
|
251
332
|
## Development
|
package/dist/button.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare const mountConnectButton: (container: HTMLElement, options: Conne
|
|
|
8
8
|
* HTMLElement.
|
|
9
9
|
*
|
|
10
10
|
* Attributes: authorize-url (required), app-theme, label, variant,
|
|
11
|
-
* theme, platforms ("stripe, notion" or a JSON array),
|
|
11
|
+
* theme, platforms ("stripe, notion" or a JSON array),
|
|
12
12
|
* description, accent-color, connected-label.
|
|
13
13
|
* Events: "success" and "error" (detail: message), plus the matching
|
|
14
14
|
* onSuccess and onError function properties that React 19, Vue and
|
package/dist/index.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const e="one_connect",t="one_connect_message";let n=!1;const o=o=>{if("undefined"!=typeof window&&!n){const r=function(n){var o;const r=new URLSearchParams(n),i=r.get(e);if("success"!==i&&"error"!==i)return null;const c=null!==(o=r.get(t))&&void 0!==o?o:void 0;return c?{status:i,message:c}:{status:i}}(window.location.search);if(r){n=!0;const i=()=>{(function(n){const o=new URLSearchParams(n);return o.has(e)||o.has(t)})(window.location.search)&&window.history.replaceState(null,"",function(n){const o=new URLSearchParams(n.search);o.delete(e),o.delete(t);const r=o.toString();return`${n.pathname}${r?`?${r}`:""}${n.hash}`}(window.location))};i();for(const e of[50,500,2e3])window.setTimeout(i,e);window.setTimeout(()=>{try{var e,t,n;"success"===r.status?null===(e=o.onSuccess)||void 0===e||e.call(o):null===(t=o.onError)||void 0===t||t.call(o,null!==(n=r.message)&&void 0!==n?n:"The connection was not completed.")}catch{}},0)}}return{open:()=>{"undefined"!=typeof window&&window.location.assign((()=>{try{const e=new URL(o.authorizeUrl,"undefined"==typeof window?void 0:window.location.origin);return o.appTheme&&(e.hash=`one_theme=${o.appTheme}`),e.toString()}catch{return o.authorizeUrl}})())}}};function r(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=typeof e||!e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var o=n.call(e,t);if("object"!=typeof o)return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:t+""}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function c(e){return e.split(/[-_]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function l(e){return e?`https://assets.withone.ai/connectors/${e}.svg`:""}function a(e){var t,n,o,r;const a="string"==typeof e?{slug:e}:e,s=null!==(t=a.slug)&&void 0!==t&&t.trim()?i(a.slug):null!==(n=a.name)&&void 0!==n&&n.trim()?i(a.name):"";return s?{slug:s,name:(null===(o=a.name)||void 0===o?void 0:o.trim())||c(s),imageUrl:(null===(r=a.imageUrl)||void 0===r?void 0:r.trim())||l(s)}:null}function s(e){return(null!=e?e:[]).map(a).filter(e=>null!==e)}function d(e){const t=null==e?void 0:e.trim();if(!t)return[];if(t.startsWith("["))try{const e=JSON.parse(t);return Array.isArray(e)?e.filter(e=>"string"==typeof e||"object"==typeof e&&null!==e):[]}catch{return[]}return t.split(",").map(e=>e.trim()).filter(Boolean)}const p="one-connect-button-styles",u="cubic-bezier(.2,.9,.25,1)",m="#CCFF00",h="#3FE3A5",b="#0A0C0B",f={buttonBackground:b,buttonForeground:"#FFFFFF",surface:"#FFFFFF",foreground:b,muted:"#6B7280",line:"#D1D5DB",shadow:"0 1px 2px rgba(10,12,11,.08), 0 4px 14px rgba(10,12,11,.06)"},w={buttonBackground:"#F2F5F3",buttonForeground:b,surface:"#101312",foreground:"#F2F5F3",muted:"#8A938E",line:"#2A302D",shadow:"0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35)"};const g=(e,t)=>{var n,r,i;!function(){if("undefined"==typeof document||document.getElementById(p))return;const e=document.createElement("style");e.id=p,e.textContent=[`.owcb{font:500 14.5px/1.2 inherit;font-family:inherit;letter-spacing:-.01em;cursor:pointer;border:0;display:inline-flex;align-items:center;gap:12px;padding:12px 16px 12px 14px;border-radius:12px;transition:transform .2s ${u},box-shadow .2s ${u},opacity .2s ${u}}`,".owcb:hover{transform:translateY(-1px)}",".owcb:active{transform:translateY(0)}",`.owcb:focus-visible{outline:2px solid ${h};outline-offset:3px}`,".owcb[disabled]{cursor:default;opacity:.75;transform:none}",".owcb-stack{display:inline-flex;align-items:center;flex-shrink:0}",`.owcb-chip{width:22px;height:22px;border-radius:6px;display:grid;place-items:center;overflow:hidden;margin-left:-7px;transition:margin-left .28s ${u};position:relative;background:#FFFFFF}`,".owcb-chip:first-child{margin-left:0}",".owcb-chip img{width:14px;height:14px;display:block;object-fit:contain}",`.owcb-chip.owcb-more{font-family:ui-monospace,monospace;font-size:9px;font-weight:500;letter-spacing:-.02em;color:${b}}`,`.owcb-chip.owcb-letter{font:600 10px ui-monospace,monospace;color:${b}}`,".owcb:hover .owcb-chip{margin-left:-2px}",".owcb:hover .owcb-chip:first-child{margin-left:0}",".owcb-label{white-space:nowrap}",`.owcb-arrow{opacity:.5;transition:transform .2s ${u},opacity .2s ${u}}`,".owcb:hover .owcb-arrow{transform:translateX(2px);opacity:.8}",".owcb-spinner{width:16px;height:16px;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;opacity:.7;animation:owcb-spin .7s linear infinite}","@keyframes owcb-spin{to{transform:rotate(360deg)}}",".owcb.owcb-block{display:flex;flex-direction:column;align-items:stretch;width:100%;max-width:420px;text-align:left;padding:16px;border-radius:14px;gap:0}",".owcb-block-top{display:flex;align-items:center;justify-content:space-between;gap:12px}",".owcb-block-title{font-size:15px;font-weight:600;letter-spacing:-.01em}",".owcb-block-sub{font-size:13px;line-height:1.5;margin-top:6px;font-weight:400}",".owcb-block-foot{margin-top:14px;padding-top:12px;display:flex;align-items:center;gap:6px;font-size:11.5px;font-weight:400}","@media (prefers-reduced-motion:reduce){.owcb,.owcb-chip,.owcb-arrow{transition:none}.owcb-spinner{animation-duration:1.4s}}"].join("\n"),document.head.appendChild(e)}();const c="dark"===t.theme?w:f,l=null!==(n=t.variant)&&void 0!==n?n:"default",a=null!==(r=t.label)&&void 0!==r?r:"Connect your apps",d=null!==(i=t.connectedLabel)&&void 0!==i?i:"Connected";let g="idle",v=!1;const x=o({...t.connect,onSuccess:()=>{var e,n;E("connected"),null===(e=(n=t.connect).onSuccess)||void 0===e||e.call(n)},onError:e=>{var n,o;E("idle"),null===(n=(o=t.connect).onError)||void 0===n||n.call(o,e)}}),y=document.createElement("button");y.type="button",y.className="block"===l?"owcb owcb-block":"owcb",y.addEventListener("click",()=>{"connecting"!==g&&(E("connecting"),x.open())});const C=()=>"connecting"===g?"Connecting…":"connected"===g?d:a,k=()=>{var e,n;const o="accent"===l,r="block"===l,i=r?c.surface:o?null!==(e=t.accentColor)&&void 0!==e?e:m:c.buttonBackground,a=r?c.foreground:o?b:c.buttonForeground,d=r?c.surface:i;y.style.background="connected"!==g||r?i:h,y.style.color="connected"!==g||r?a:b,y.style.boxShadow=r?`inset 0 0 0 1px ${c.line}`:"connected"===g?"none":o?`0 1px 2px rgba(10,12,11,.1), 0 6px 20px ${null!==(n=t.accentColor)&&void 0!==n?n:m}38`:c.shadow,y.disabled="connecting"===g,y.innerHTML="";const p=function(e,t){var n;const o=s(e.platforms);if(0===o.length&&!e.moreCount)return null;const r=document.createElement("span");r.className="owcb-stack",r.setAttribute("aria-hidden","true");for(const e of o.slice(0,3)){const n=document.createElement("span");n.className="owcb-chip",n.title=e.name,n.style.boxShadow=`0 0 0 1.5px ${t}`;const o=document.createElement("img");o.alt="",o.src=e.imageUrl,o.addEventListener("error",()=>{o.remove(),n.classList.add("owcb-letter"),n.textContent=e.name.charAt(0).toUpperCase()}),n.appendChild(o),r.appendChild(n)}const i=(null!==(n=e.moreCount)&&void 0!==n?n:0)+Math.max(0,o.length-3);if(i>0){const e=document.createElement("span");e.className="owcb-chip owcb-more",e.style.boxShadow=`0 0 0 1.5px ${t}`,e.textContent=`+${i}`,r.appendChild(e)}return r}(t,d);if(r){const e=document.createElement("span");e.className="owcb-block-top";const n=document.createElement("span");if(n.className="owcb-block-title",n.textContent=C(),e.appendChild(n),"connecting"===g){const t=document.createElement("span");t.className="owcb-spinner",e.appendChild(t)}else p&&e.appendChild(p);if(y.appendChild(e),t.description&&"idle"===g){const e=document.createElement("span");e.className="owcb-block-sub",e.style.color=c.muted,e.textContent=t.description,y.appendChild(e)}const o=document.createElement("span");return o.className="owcb-block-foot",o.style.color=c.muted,o.style.borderTop=`1px solid ${c.line}`,o.innerHTML=`<span style="font-size:11px;color:${c.muted}">Secured by</span><span style="display:inline-flex;color:${c.foreground}"><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.4" style="flex-shrink:0"><circle cx="12" cy="12" r="8.5"/></svg></span><span style="font-size:12px;font-weight:600;letter-spacing:-0.02em;color:${c.foreground}">one</span>`,void y.appendChild(o)}if("connecting"===g){const e=document.createElement("span");e.className="owcb-spinner",y.appendChild(e)}else if("connected"===g){const e=document.createElement("span");e.innerHTML='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px"><path d="M3 8.5l3.5 3.5L13 5"/></svg>',e.style.display="inline-flex",y.appendChild(e)}else p&&y.appendChild(p);const u=document.createElement("span");if(u.className="owcb-label",u.textContent=C(),y.appendChild(u),"idle"===g){const e=document.createElement("span");e.innerHTML='<svg class="owcb-arrow" width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3l5 5-5 5"/></svg>',e.style.display="inline-flex",y.appendChild(e)}},E=e=>{v||(g=e,k())};return k(),e.appendChild(y),{setState:E,destroy:()=>{v=!0,y.remove()}}};function v(){if("undefined"!=typeof window&&"undefined"!=typeof customElements&&!customElements.get("one-connect-button")){class e extends HTMLElement{constructor(...e){super(...e),r(this,"onSuccess",null),r(this,"onError",null),r(this,"handle",null)}connectedCallback(){this.mount()}disconnectedCallback(){var e;null===(e=this.handle)||void 0===e||e.destroy(),this.handle=null}attributeChangedCallback(){this.isConnected&&this.handle&&this.mount()}mount(){var e,t,n,o,r;null===(e=this.handle)||void 0===e||e.destroy(),this.handle=null;const i=this.getAttribute("authorize-url");if(!i)return;const c=this.getAttribute("more-count"),l=c?parseInt(c,10):NaN;this.handle=g(this,{connect:{authorizeUrl:i,appTheme:x(this,"app-theme",["light","dark"]),onSuccess:()=>{var e;null===(e=this.onSuccess)||void 0===e||e.call(this),this.dispatchEvent(new CustomEvent("success"))},onError:e=>{var t;null===(t=this.onError)||void 0===t||t.call(this,e),this.dispatchEvent(new CustomEvent("error",{detail:e}))}},label:null!==(t=this.getAttribute("label"))&&void 0!==t?t:void 0,variant:x(this,"variant",["default","accent","block"]),theme:x(this,"theme",["light","dark"]),platforms:d(this.getAttribute("platforms")),moreCount:Number.isFinite(l)?l:void 0,description:null!==(n=this.getAttribute("description"))&&void 0!==n?n:void 0,accentColor:null!==(o=this.getAttribute("accent-color"))&&void 0!==o?o:void 0,connectedLabel:null!==(r=this.getAttribute("connected-label"))&&void 0!==r?r:void 0})}}r(e,"observedAttributes",["authorize-url","app-theme","label","variant","theme","platforms","more-count","description","accent-color","connected-label"]),customElements.define("one-connect-button",e)}}function x(e,t,n){const o=e.getAttribute(t);return o&&n.includes(o)?o:void 0}v(),exports.mountConnectButton=g,exports.normalizePlatform=a,exports.normalizePlatforms=s,exports.optionsFromProps=function(e,t=e){return{connect:{authorizeUrl:e.authorizeUrl,appTheme:e.appTheme,onSuccess:()=>{var e;return null===(e=t.onSuccess)||void 0===e?void 0:e.call(t)},onError:e=>{var n;return null===(n=t.onError)||void 0===n?void 0:n.call(t,e)}},label:e.label,variant:e.variant,theme:e.theme,platforms:e.platforms,moreCount:e.moreCount,description:e.description,accentColor:e.accentColor,connectedLabel:e.connectedLabel}},exports.parsePlatformsAttribute=d,exports.propsIdentity=function(e){var t;return JSON.stringify([e.authorizeUrl,e.appTheme,e.label,e.variant,e.theme,null!==(t=e.platforms)&&void 0!==t?t:[],e.moreCount,e.description,e.accentColor,e.connectedLabel])},exports.registerConnectButton=v,exports.useOneConnect=o;
|
|
1
|
+
"use strict";const e="one_connect",t="one_connect_message";let n=!1;const o=o=>{if("undefined"!=typeof window&&!n){const r=function(n){var o;const r=new URLSearchParams(n),i=r.get(e);if("success"!==i&&"error"!==i)return null;const c=null!==(o=r.get(t))&&void 0!==o?o:void 0;return c?{status:i,message:c}:{status:i}}(window.location.search);if(r){n=!0;const i=()=>{(function(n){const o=new URLSearchParams(n);return o.has(e)||o.has(t)})(window.location.search)&&window.history.replaceState(null,"",function(n){const o=new URLSearchParams(n.search);o.delete(e),o.delete(t);const r=o.toString();return`${n.pathname}${r?`?${r}`:""}${n.hash}`}(window.location))};i();for(const e of[50,500,2e3])window.setTimeout(i,e);window.setTimeout(()=>{try{var e,t,n;"success"===r.status?null===(e=o.onSuccess)||void 0===e||e.call(o):null===(t=o.onError)||void 0===t||t.call(o,null!==(n=r.message)&&void 0!==n?n:"The connection was not completed.")}catch{}},0)}}return{open:()=>{"undefined"!=typeof window&&window.location.assign((()=>{try{const e=new URL(o.authorizeUrl,"undefined"==typeof window?void 0:window.location.origin);return o.appTheme&&(e.hash=`one_theme=${o.appTheme}`),e.toString()}catch{return o.authorizeUrl}})())}}};function r(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=typeof e||!e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var o=n.call(e,t);if("object"!=typeof o)return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:t+""}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function c(e){return e.split(/[-_]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function l(e){return e?`https://assets.withone.ai/connectors/${e}.svg`:""}function a(e){var t,n,o,r;const a="string"==typeof e?{slug:e}:e,s=null!==(t=a.slug)&&void 0!==t&&t.trim()?i(a.slug):null!==(n=a.name)&&void 0!==n&&n.trim()?i(a.name):"";return s?{slug:s,name:(null===(o=a.name)||void 0===o?void 0:o.trim())||c(s),imageUrl:(null===(r=a.imageUrl)||void 0===r?void 0:r.trim())||l(s)}:null}function s(e){return(null!=e?e:[]).map(a).filter(e=>null!==e)}function d(e){const t=null==e?void 0:e.trim();if(!t)return[];if(t.startsWith("["))try{const e=JSON.parse(t);return Array.isArray(e)?e.filter(e=>"string"==typeof e||"object"==typeof e&&null!==e):[]}catch{return[]}return t.split(",").map(e=>e.trim()).filter(Boolean)}const p="one-connect-button-styles",u="cubic-bezier(.2,.9,.25,1)",C="#CCFF00",h="#3FE3A5",m="#0A0C0B",f={buttonBackground:m,buttonForeground:"#FFFFFF",surface:"#FFFFFF",foreground:m,muted:"#6B7280",line:"#D1D5DB",shadow:"0 1px 2px rgba(10,12,11,.08), 0 4px 14px rgba(10,12,11,.06)"},b={buttonBackground:"#F2F5F3",buttonForeground:m,surface:"#101312",foreground:"#F2F5F3",muted:"#8A938E",line:"#2A302D",shadow:"0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35)"};const w=(e,t)=>{var n,r,i;!function(){if("undefined"==typeof document||document.getElementById(p))return;const e=document.createElement("style");e.id=p,e.textContent=[`.owcb{font:500 14.5px/1.2 inherit;font-family:inherit;letter-spacing:-.01em;cursor:pointer;border:0;display:inline-flex;align-items:center;gap:12px;padding:12px 16px 12px 14px;border-radius:12px;transition:transform .2s ${u},box-shadow .2s ${u},opacity .2s ${u}}`,".owcb:hover{transform:translateY(-1px)}",".owcb:active{transform:translateY(0)}",`.owcb:focus-visible{outline:2px solid ${h};outline-offset:3px}`,".owcb[disabled]{cursor:default;opacity:.75;transform:none}",".owcb-stack{display:inline-flex;align-items:center;flex-shrink:0}",`.owcb-chip{width:22px;height:22px;border-radius:6px;display:grid;place-items:center;overflow:hidden;margin-left:-7px;transition:margin-left .28s ${u};position:relative;background:#FFFFFF}`,".owcb-chip:first-child{margin-left:0}",".owcb-chip img{width:14px;height:14px;display:block;object-fit:contain}",`.owcb-chip.owcb-more{font-family:ui-monospace,monospace;font-size:9px;font-weight:500;letter-spacing:-.02em;color:${m}}`,`.owcb-chip.owcb-letter{font:600 10px ui-monospace,monospace;color:${m}}`,".owcb:hover .owcb-chip{margin-left:-2px}",".owcb:hover .owcb-chip:first-child{margin-left:0}",".owcb-label{white-space:nowrap}",`.owcb-arrow{opacity:.5;transition:transform .2s ${u},opacity .2s ${u}}`,".owcb:hover .owcb-arrow{transform:translateX(2px);opacity:.8}",".owcb-spinner{width:16px;height:16px;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;opacity:.7;animation:owcb-spin .7s linear infinite}","@keyframes owcb-spin{to{transform:rotate(360deg)}}",".owcb.owcb-block{display:flex;flex-direction:column;align-items:stretch;width:100%;max-width:420px;text-align:left;padding:16px;border-radius:14px;gap:0}",".owcb-block-top{display:flex;align-items:center;justify-content:space-between;gap:12px}",".owcb-block-title{font-size:15px;font-weight:600;letter-spacing:-.01em}",".owcb-block-sub{font-size:13px;line-height:1.5;margin-top:6px;font-weight:400}",".owcb-block-foot{margin-top:14px;padding-top:12px;display:flex;align-items:center;gap:6px;font-size:11.5px;font-weight:400}","@media (prefers-reduced-motion:reduce){.owcb,.owcb-chip,.owcb-arrow{transition:none}.owcb-spinner{animation-duration:1.4s}}"].join("\n"),document.head.appendChild(e)}();const c="dark"===t.theme?b:f,l=null!==(n=t.variant)&&void 0!==n?n:"default",a=null!==(r=t.label)&&void 0!==r?r:"Connect your apps",d=null!==(i=t.connectedLabel)&&void 0!==i?i:"Connected";let w="idle",g=!1;const v=o({...t.connect,onSuccess:()=>{var e,n;E("connected"),null===(e=(n=t.connect).onSuccess)||void 0===e||e.call(n)},onError:e=>{var n,o;E("idle"),null===(n=(o=t.connect).onError)||void 0===n||n.call(o,e)}}),x=document.createElement("button");x.type="button",x.className="block"===l?"owcb owcb-block":"owcb",x.addEventListener("click",()=>{"connecting"!==w&&(E("connecting"),v.open())});const y=()=>"connecting"===w?"Connecting…":"connected"===w?d:a,k=()=>{var e,n;const o="accent"===l,r="block"===l,i=r?c.surface:o?null!==(e=t.accentColor)&&void 0!==e?e:C:c.buttonBackground,a=r?c.foreground:o?m:c.buttonForeground,d=r?c.surface:i;x.style.background="connected"!==w||r?i:h,x.style.color="connected"!==w||r?a:m,x.style.boxShadow=r?`inset 0 0 0 1px ${c.line}`:"connected"===w?"none":o?`0 1px 2px rgba(10,12,11,.1), 0 6px 20px ${null!==(n=t.accentColor)&&void 0!==n?n:C}38`:c.shadow,x.disabled="connecting"===w,x.innerHTML="";const p=function(e,t){const n=s(e.platforms);if(0===n.length)return null;const o=document.createElement("span");o.className="owcb-stack",o.setAttribute("aria-hidden","true");for(const e of n.slice(0,3)){const n=document.createElement("span");n.className="owcb-chip",n.title=e.name,n.style.boxShadow=`0 0 0 1.5px ${t}`;const r=document.createElement("img");r.alt="",r.src=e.imageUrl,r.addEventListener("error",()=>{r.remove(),n.classList.add("owcb-letter"),n.textContent=e.name.charAt(0).toUpperCase()}),n.appendChild(r),o.appendChild(n)}const r=Math.max(0,n.length-3);if(r>0){const e=document.createElement("span");e.className="owcb-chip owcb-more",e.style.boxShadow=`0 0 0 1.5px ${t}`,e.textContent=`+${r}`,o.appendChild(e)}return o}(t,d);if(r){const e=document.createElement("span");e.className="owcb-block-top";const n=document.createElement("span");if(n.className="owcb-block-title",n.textContent=y(),e.appendChild(n),"connecting"===w){const t=document.createElement("span");t.className="owcb-spinner",e.appendChild(t)}else p&&e.appendChild(p);if(x.appendChild(e),t.description&&"idle"===w){const e=document.createElement("span");e.className="owcb-block-sub",e.style.color=c.muted,e.textContent=t.description,x.appendChild(e)}const o=document.createElement("span");return o.className="owcb-block-foot",o.style.color=c.muted,o.style.borderTop=`1px solid ${c.line}`,o.innerHTML=`<span style="font-size:11px;color:${c.muted}">Secured by</span><span style="display:inline-flex;color:${c.foreground}"><svg viewBox="0 0 3152 990" width="35" height="11" fill="none" aria-hidden="true" style="flex-shrink:0;display:block"><path d="M1698.02 793.203C1525.75 793.203 1401 666.834 1401 495.102C1401 323.909 1524.67 197 1698.02 197C1870.83 197 1996.12 323.909 1996.12 495.102C1996.12 666.834 1869.75 793.203 1698.02 793.203ZM1698.02 694.916C1801.71 694.916 1884.88 615.53 1884.88 495.102C1884.88 374.133 1801.71 295.827 1698.02 295.827C1594.87 295.827 1511.71 373.053 1511.71 495.102C1511.71 616.61 1595.41 694.916 1698.02 694.916Z" fill="currentColor"/><path d="M2326.71 197.54C2480.62 197.54 2565.95 289.887 2565.95 467.56V779.702H2455.24V468.64C2455.24 361.172 2411.5 292.587 2308.35 292.587C2217.63 292.587 2140.4 356.852 2140.4 484.841V779.702H2030.77V211.041H2140.4V272.065C2185.76 225.082 2248.41 197.54 2326.71 197.54Z" fill="currentColor"/><path d="M3151.98 493.481C3151.98 514.003 3150.36 520.483 3148.74 529.124H2700.51C2715.63 637.672 2794.47 686.815 2922.46 686.815C3031.01 686.815 3099.6 650.633 3116.88 643.072V747.84C3100.68 755.4 3023.45 793.203 2908.96 793.203C2699.97 793.203 2588.18 661.434 2588.18 497.802C2588.18 332.55 2702.67 197 2871.7 197C3031.55 197 3151.98 317.969 3151.98 493.481ZM2876.02 300.688C2784.75 300.688 2720.49 345.511 2702.67 434.617H3050.45C3034.79 344.971 2967.29 300.688 2876.02 300.688Z" fill="currentColor"/><path d="M537.494 782.175C641.011 827.7 767.922 858.83 880.501 839.875C983.155 822.599 1069.64 764.946 1094.39 659.271C1114.47 573.493 1088.54 481.273 1047.27 405.311C983.124 287.246 874.768 183.791 759.847 114.657C644.926 45.5225 507.181 -5.02605 372.102 0.398669C289.698 3.69665 205.938 26.536 146.544 85.6685C112.069 119.989 88.3818 171.231 83.1266 219.823C78.1642 265.64 84.3749 310.317 99.8478 354.007C168.243 547.14 354.009 701.467 537.51 782.191L537.494 782.175ZM751.956 818.053C583.05 796.2 415.7 707.108 292.472 593.004C222.536 528.246 159.366 444.979 125.985 355.471C102.961 293.734 96.3956 223.367 122.41 161.969C137.158 127.14 160.367 98.244 190.743 75.9133C275.581 13.5752 395.819 6.99464 495.977 26.8596C608.848 49.252 713.875 99.4615 806.712 166.515C907.085 239.025 999.537 339.922 1048.42 454.627C1072.45 511.001 1083.99 570.103 1075.64 630.869C1067.38 690.957 1034.57 742.353 983.987 775.379C917.626 818.715 830.861 828.255 751.972 818.053H751.956Z" fill="currentColor"/><path d="M1103.27 661.228C1086.68 755.313 1024.9 818.622 934.021 848.69C821.751 885.846 685.146 860.263 576.251 818.915C452.515 771.942 340.336 699.91 245.157 607.813C209.002 572.83 177.255 534.456 148.837 493.031C100.384 422.402 61.7024 335.19 65.8326 248.071C68.2213 197.769 83.987 159.241 111.403 116.938C45.8289 171.308 31.404 262.511 47.1543 342.002C53.4728 373.841 62.9815 403.23 76.528 432.958C100.338 485.202 130.914 532.992 167.747 577.238C255.452 682.573 367.83 765.13 491.073 824.124C614.317 883.118 769.307 920.691 900.471 889.128C985.788 868.601 1061.47 816.095 1090.83 730.856C1098.18 708.71 1104.61 686.564 1103.25 661.244L1103.27 661.228Z" fill="currentColor"/><path d="M883.297 963.502C923.15 947.074 960.877 926.022 988.309 893.967C954.265 917.839 920.931 934.807 882.403 944.993C751.747 979.53 595.123 942.589 474.731 886.57C422.102 862.082 372.617 834.018 325.181 800.591C205.128 715.984 89.4825 597.996 36.7762 458.756C20.2092 414.973 11.7793 370.404 11.5327 323.739C11.4249 302.641 15.4627 283.747 19.0226 263.004C-2.52219 310.486 -3.66265 380.915 5.21419 431.617C32.0605 584.881 147.151 721.146 266.988 813.983C343.443 873.224 436.403 924.913 528.424 954.672C620.444 984.431 713.851 999.426 808.491 983.598C834.459 979.252 858.362 973.781 883.297 963.502Z" fill="currentColor"/><path d="M1045.09 830.737C1053.55 816.158 1061.75 802.95 1066.99 787.061C1043.75 824.541 1013.3 854.408 974.29 874.982C947.752 888.991 920.228 899.27 890.577 905.219C864.578 910.428 839.673 914.481 812.781 914.851C739.716 915.852 668.717 903.092 599.459 880.854C418.686 822.8 238.422 697.6 127.985 543.165C94.8819 496.87 68.8679 447.847 50.7135 394.031C28.1978 324.45 25.7782 258.69 53.4104 189.078C36.1345 211.825 26.9956 237.793 20.4151 264.916C18.3654 282.732 14.713 299.345 15.0828 317.561C15.9613 360.774 23.5436 402.692 38.477 443.562C73.1522 538.464 134.936 621.869 207.368 691.913C307.34 788.587 426.762 862.268 558.127 907.993C689.491 953.718 871.975 973.768 992.614 885.446C1013.28 870.312 1029.39 851.773 1045.07 830.752L1045.09 830.737Z" fill="currentColor"/><path d="M552.552 745.664C671.079 797.831 835.825 835.526 954.768 769.12C999.137 744.354 1031.99 705.626 1047.99 657.312C1086.75 540.264 1020.28 405.817 947.124 315.631C855.536 202.745 729.688 114.516 592.76 66.0323C506.349 35.4412 418.968 22.0797 328.211 37.46C262.313 48.6177 195.29 81.7054 157.563 138.234C118.912 196.133 115.891 270.785 136.758 335.882C176.519 459.926 276.769 570.763 381.01 646.277C434.995 685.391 491.662 718.864 552.567 745.664H552.552ZM210.162 422.955C199.898 402.381 189.141 382.701 184.733 360.385C182.267 347.918 184.625 333.477 184.564 320.64C184.44 293.223 194.072 269.059 209.992 247.93C219.609 235.17 229.456 224.844 243.018 215.906C341.218 151.179 476.051 175.991 576.763 223.904C661.971 264.435 737.394 321.534 796.11 395.862C836.919 447.52 871.193 513.634 880.502 579.501C888.238 634.272 873.243 695.485 826.763 729.189C807.853 742.905 785.045 751.721 761.728 755.974C721.921 763.217 688.802 758.008 649.565 748.916C594.27 736.094 542.488 715.212 492.279 688.889C441.977 662.521 396.745 631.483 353.008 595.436C314.372 563.597 280.252 537.845 248.458 498.007L248.274 497.837L221.504 461.945L211.379 445.84C194.103 418.408 177.398 391.346 165.238 361.202C151.291 326.604 142.676 291.852 143.678 254.279C146.19 160.564 203.766 96.7467 290.238 67.82C434.394 19.5985 611.561 79.4246 735.298 157.636C825.099 214.411 904.435 286.581 963.321 375.134C989.767 414.894 1010.42 456.998 1023.78 502.646C1049.84 591.66 1036.79 685.884 957.404 742.659C890.257 790.68 785.6 793.146 707.295 776.486C680.403 769.736 654.065 766.007 629.222 754.818C732.107 783.93 882.397 797.723 963.938 715.35C990.599 688.412 1007.55 656.218 1013.75 618.537C1020.87 575.232 1016.12 531.634 1002.62 489.931C985.498 437.071 956.34 384.026 922.405 340.289C876.911 281.665 823.188 230.531 762.468 187.981C681.112 130.975 588.783 88.887 490.799 71.0872C425.595 59.2514 349.463 60.4072 287.557 85.0959C256.441 97.5019 228.501 114.5 205.924 138.85C158.303 190.2 147.176 256.036 161.539 323.383C166.856 348.334 175.456 371.296 185.596 394.737C194.488 415.295 222.676 461.883 236.022 477.649L226.729 456.196L210.162 423.001V422.955ZM832.342 286.936C756.904 211.544 666.44 154.076 564.896 120.249C526.553 107.473 487.948 99.4899 447.648 96.084C401.26 92.1696 356.167 96.7313 312.215 111.341C222.337 141.208 169.399 217.277 175.255 312.333C177.567 324.847 179.555 335.774 180.603 348.503C152.971 264.944 174.454 175.236 251.356 125.581C310.797 87.2072 387.39 78.2071 456.694 86.7603C503.714 92.5549 555.387 106.656 599.078 124.317C706.016 167.546 800.764 234.615 876.741 321.395C912.002 361.664 942.085 405.756 963.876 454.532C996.779 528.166 1011.85 610.046 965.895 681.369C952.379 702.359 935.427 718.818 914.283 731.871C884.432 747.421 852.947 758.671 818.872 761.06C839.523 755.959 859.404 750.981 878.637 740.424C929.186 717.061 964.601 671.567 974.818 616.488C987.671 547.245 964.693 475.429 930.881 415.357C903.834 367.305 871.471 326.049 832.311 286.905L832.342 286.936ZM890.673 420.18C861.623 370.387 825.515 327.621 783.288 287.999C721.659 230.161 650.028 185.345 570.598 156.033C513.623 135.013 454.506 124.394 393.786 128.108C353.316 130.59 315.728 139.867 280.498 159.516C245.268 179.166 216.819 204.918 201.023 240.394L188.617 268.242C204.583 180.445 278.325 132.054 361.931 119.355C412.757 111.634 462.658 116.643 512.421 129.048C596.905 150.116 674.1 189.923 743.45 242.028C816.607 296.999 885.526 373.531 924.578 456.982C949.713 510.675 965.094 571.61 949.837 630.234C939.018 671.768 912.511 706.566 875.308 727.987C847.013 744.292 816.191 754.679 783.242 758.101C825.915 745.926 863.75 731.917 895.327 698.799C913.512 679.72 926.535 657.62 933.054 631.714C951.347 558.989 927.49 483.274 890.642 420.134L890.673 420.18ZM726.19 279.369C625.678 198.799 493.404 141.917 362.655 156.973C343.854 159.131 326.547 164.17 308.639 169.195C301.18 171.29 295.201 177.732 286.493 176.253C378.313 130.081 492.063 149.545 583.143 188.119C684.102 230.87 775.952 302.825 840.664 391.069C871.794 433.527 895.805 479.652 909.506 530.386C918.382 563.273 920.232 596.654 911.941 629.294C905.622 654.153 895.481 677.778 875.786 694.9C893.987 666.327 905.452 637.693 907.086 604.128C908.72 570.562 903.141 536.565 891.567 503.57C861.392 417.53 797.066 336.19 726.205 279.384L726.19 279.369Z" fill="currentColor"/></svg></span>`,void x.appendChild(o)}if("connecting"===w){const e=document.createElement("span");e.className="owcb-spinner",x.appendChild(e)}else if("connected"===w){const e=document.createElement("span");e.innerHTML='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px"><path d="M3 8.5l3.5 3.5L13 5"/></svg>',e.style.display="inline-flex",x.appendChild(e)}else p&&x.appendChild(p);const u=document.createElement("span");if(u.className="owcb-label",u.textContent=y(),x.appendChild(u),"idle"===w){const e=document.createElement("span");e.innerHTML='<svg class="owcb-arrow" width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3l5 5-5 5"/></svg>',e.style.display="inline-flex",x.appendChild(e)}},E=e=>{g||(w=e,k())};return k(),e.appendChild(x),{setState:E,destroy:()=>{g=!0,x.remove()}}};function g(){if("undefined"!=typeof window&&"undefined"!=typeof customElements&&!customElements.get("one-connect-button")){class e extends HTMLElement{constructor(...e){super(...e),r(this,"onSuccess",null),r(this,"onError",null),r(this,"handle",null)}connectedCallback(){this.mount()}disconnectedCallback(){var e;null===(e=this.handle)||void 0===e||e.destroy(),this.handle=null}attributeChangedCallback(){this.isConnected&&this.handle&&this.mount()}mount(){var e,t,n,o,r;null===(e=this.handle)||void 0===e||e.destroy(),this.handle=null;const i=this.getAttribute("authorize-url");i&&(this.handle=w(this,{connect:{authorizeUrl:i,appTheme:v(this,"app-theme",["light","dark"]),onSuccess:()=>{var e;null===(e=this.onSuccess)||void 0===e||e.call(this),this.dispatchEvent(new CustomEvent("success"))},onError:e=>{var t;null===(t=this.onError)||void 0===t||t.call(this,e),this.dispatchEvent(new CustomEvent("error",{detail:e}))}},label:null!==(t=this.getAttribute("label"))&&void 0!==t?t:void 0,variant:v(this,"variant",["default","accent","block"]),theme:v(this,"theme",["light","dark"]),platforms:d(this.getAttribute("platforms")),description:null!==(n=this.getAttribute("description"))&&void 0!==n?n:void 0,accentColor:null!==(o=this.getAttribute("accent-color"))&&void 0!==o?o:void 0,connectedLabel:null!==(r=this.getAttribute("connected-label"))&&void 0!==r?r:void 0}))}}r(e,"observedAttributes",["authorize-url","app-theme","label","variant","theme","platforms","description","accent-color","connected-label"]),customElements.define("one-connect-button",e)}}function v(e,t,n){const o=e.getAttribute(t);return o&&n.includes(o)?o:void 0}g(),exports.mountConnectButton=w,exports.normalizePlatform=a,exports.normalizePlatforms=s,exports.optionsFromProps=function(e,t=e){return{connect:{authorizeUrl:e.authorizeUrl,appTheme:e.appTheme,onSuccess:()=>{var e;return null===(e=t.onSuccess)||void 0===e?void 0:e.call(t)},onError:e=>{var n;return null===(n=t.onError)||void 0===n?void 0:n.call(t,e)}},label:e.label,variant:e.variant,theme:e.theme,platforms:e.platforms,description:e.description,accentColor:e.accentColor,connectedLabel:e.connectedLabel}},exports.parsePlatformsAttribute=d,exports.propsIdentity=function(e){var t;return JSON.stringify([e.authorizeUrl,e.appTheme,e.label,e.variant,e.theme,null!==(t=e.platforms)&&void 0!==t?t:[],e.description,e.accentColor,e.connectedLabel])},exports.registerConnectButton=g,exports.useOneConnect=o;
|
package/dist/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e="one_connect",t="one_connect_message";let n=!1;const o=o=>{if("undefined"!=typeof window&&!n){const r=function(n){var o;const r=new URLSearchParams(n),i=r.get(e);if("success"!==i&&"error"!==i)return null;const c=null!==(o=r.get(t))&&void 0!==o?o:void 0;return c?{status:i,message:c}:{status:i}}(window.location.search);if(r){n=!0;const i=()=>{(function(n){const o=new URLSearchParams(n);return o.has(e)||o.has(t)})(window.location.search)&&window.history.replaceState(null,"",function(n){const o=new URLSearchParams(n.search);o.delete(e),o.delete(t);const r=o.toString();return`${n.pathname}${r?`?${r}`:""}${n.hash}`}(window.location))};i();for(const e of[50,500,2e3])window.setTimeout(i,e);window.setTimeout(()=>{try{var e,t,n;"success"===r.status?null===(e=o.onSuccess)||void 0===e||e.call(o):null===(t=o.onError)||void 0===t||t.call(o,null!==(n=r.message)&&void 0!==n?n:"The connection was not completed.")}catch{}},0)}}return{open:()=>{"undefined"!=typeof window&&window.location.assign((()=>{try{const e=new URL(o.authorizeUrl,"undefined"==typeof window?void 0:window.location.origin);return o.appTheme&&(e.hash=`one_theme=${o.appTheme}`),e.toString()}catch{return o.authorizeUrl}})())}}};function r(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=typeof e||!e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var o=n.call(e,t);if("object"!=typeof o)return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:t+""}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function c(e){return e.split(/[-_]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function l(e){return e?`https://assets.withone.ai/connectors/${e}.svg`:""}function a(e){var t,n,o,r;const a="string"==typeof e?{slug:e}:e,s=null!==(t=a.slug)&&void 0!==t&&t.trim()?i(a.slug):null!==(n=a.name)&&void 0!==n&&n.trim()?i(a.name):"";return s?{slug:s,name:(null===(o=a.name)||void 0===o?void 0:o.trim())||c(s),imageUrl:(null===(r=a.imageUrl)||void 0===r?void 0:r.trim())||l(s)}:null}function s(e){return(null!=e?e:[]).map(a).filter(e=>null!==e)}function d(e){const t=null==e?void 0:e.trim();if(!t)return[];if(t.startsWith("["))try{const e=JSON.parse(t);return Array.isArray(e)?e.filter(e=>"string"==typeof e||"object"==typeof e&&null!==e):[]}catch{return[]}return t.split(",").map(e=>e.trim()).filter(Boolean)}const u="one-connect-button-styles",p="cubic-bezier(.2,.9,.25,1)",m="#CCFF00",h="#3FE3A5",b="#0A0C0B",f={buttonBackground:b,buttonForeground:"#FFFFFF",surface:"#FFFFFF",foreground:b,muted:"#6B7280",line:"#D1D5DB",shadow:"0 1px 2px rgba(10,12,11,.08), 0 4px 14px rgba(10,12,11,.06)"},w={buttonBackground:"#F2F5F3",buttonForeground:b,surface:"#101312",foreground:"#F2F5F3",muted:"#8A938E",line:"#2A302D",shadow:"0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35)"};const g=(e,t)=>{var n,r,i;!function(){if("undefined"==typeof document||document.getElementById(u))return;const e=document.createElement("style");e.id=u,e.textContent=[`.owcb{font:500 14.5px/1.2 inherit;font-family:inherit;letter-spacing:-.01em;cursor:pointer;border:0;display:inline-flex;align-items:center;gap:12px;padding:12px 16px 12px 14px;border-radius:12px;transition:transform .2s ${p},box-shadow .2s ${p},opacity .2s ${p}}`,".owcb:hover{transform:translateY(-1px)}",".owcb:active{transform:translateY(0)}",`.owcb:focus-visible{outline:2px solid ${h};outline-offset:3px}`,".owcb[disabled]{cursor:default;opacity:.75;transform:none}",".owcb-stack{display:inline-flex;align-items:center;flex-shrink:0}",`.owcb-chip{width:22px;height:22px;border-radius:6px;display:grid;place-items:center;overflow:hidden;margin-left:-7px;transition:margin-left .28s ${p};position:relative;background:#FFFFFF}`,".owcb-chip:first-child{margin-left:0}",".owcb-chip img{width:14px;height:14px;display:block;object-fit:contain}",`.owcb-chip.owcb-more{font-family:ui-monospace,monospace;font-size:9px;font-weight:500;letter-spacing:-.02em;color:${b}}`,`.owcb-chip.owcb-letter{font:600 10px ui-monospace,monospace;color:${b}}`,".owcb:hover .owcb-chip{margin-left:-2px}",".owcb:hover .owcb-chip:first-child{margin-left:0}",".owcb-label{white-space:nowrap}",`.owcb-arrow{opacity:.5;transition:transform .2s ${p},opacity .2s ${p}}`,".owcb:hover .owcb-arrow{transform:translateX(2px);opacity:.8}",".owcb-spinner{width:16px;height:16px;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;opacity:.7;animation:owcb-spin .7s linear infinite}","@keyframes owcb-spin{to{transform:rotate(360deg)}}",".owcb.owcb-block{display:flex;flex-direction:column;align-items:stretch;width:100%;max-width:420px;text-align:left;padding:16px;border-radius:14px;gap:0}",".owcb-block-top{display:flex;align-items:center;justify-content:space-between;gap:12px}",".owcb-block-title{font-size:15px;font-weight:600;letter-spacing:-.01em}",".owcb-block-sub{font-size:13px;line-height:1.5;margin-top:6px;font-weight:400}",".owcb-block-foot{margin-top:14px;padding-top:12px;display:flex;align-items:center;gap:6px;font-size:11.5px;font-weight:400}","@media (prefers-reduced-motion:reduce){.owcb,.owcb-chip,.owcb-arrow{transition:none}.owcb-spinner{animation-duration:1.4s}}"].join("\n"),document.head.appendChild(e)}();const c="dark"===t.theme?w:f,l=null!==(n=t.variant)&&void 0!==n?n:"default",a=null!==(r=t.label)&&void 0!==r?r:"Connect your apps",d=null!==(i=t.connectedLabel)&&void 0!==i?i:"Connected";let g="idle",v=!1;const x=o({...t.connect,onSuccess:()=>{var e,n;E("connected"),null===(e=(n=t.connect).onSuccess)||void 0===e||e.call(n)},onError:e=>{var n,o;E("idle"),null===(n=(o=t.connect).onError)||void 0===n||n.call(o,e)}}),y=document.createElement("button");y.type="button",y.className="block"===l?"owcb owcb-block":"owcb",y.addEventListener("click",()=>{"connecting"!==g&&(E("connecting"),x.open())});const C=()=>"connecting"===g?"Connecting…":"connected"===g?d:a,k=()=>{var e,n;const o="accent"===l,r="block"===l,i=r?c.surface:o?null!==(e=t.accentColor)&&void 0!==e?e:m:c.buttonBackground,a=r?c.foreground:o?b:c.buttonForeground,d=r?c.surface:i;y.style.background="connected"!==g||r?i:h,y.style.color="connected"!==g||r?a:b,y.style.boxShadow=r?`inset 0 0 0 1px ${c.line}`:"connected"===g?"none":o?`0 1px 2px rgba(10,12,11,.1), 0 6px 20px ${null!==(n=t.accentColor)&&void 0!==n?n:m}38`:c.shadow,y.disabled="connecting"===g,y.innerHTML="";const u=function(e,t){var n;const o=s(e.platforms);if(0===o.length&&!e.moreCount)return null;const r=document.createElement("span");r.className="owcb-stack",r.setAttribute("aria-hidden","true");for(const e of o.slice(0,3)){const n=document.createElement("span");n.className="owcb-chip",n.title=e.name,n.style.boxShadow=`0 0 0 1.5px ${t}`;const o=document.createElement("img");o.alt="",o.src=e.imageUrl,o.addEventListener("error",()=>{o.remove(),n.classList.add("owcb-letter"),n.textContent=e.name.charAt(0).toUpperCase()}),n.appendChild(o),r.appendChild(n)}const i=(null!==(n=e.moreCount)&&void 0!==n?n:0)+Math.max(0,o.length-3);if(i>0){const e=document.createElement("span");e.className="owcb-chip owcb-more",e.style.boxShadow=`0 0 0 1.5px ${t}`,e.textContent=`+${i}`,r.appendChild(e)}return r}(t,d);if(r){const e=document.createElement("span");e.className="owcb-block-top";const n=document.createElement("span");if(n.className="owcb-block-title",n.textContent=C(),e.appendChild(n),"connecting"===g){const t=document.createElement("span");t.className="owcb-spinner",e.appendChild(t)}else u&&e.appendChild(u);if(y.appendChild(e),t.description&&"idle"===g){const e=document.createElement("span");e.className="owcb-block-sub",e.style.color=c.muted,e.textContent=t.description,y.appendChild(e)}const o=document.createElement("span");return o.className="owcb-block-foot",o.style.color=c.muted,o.style.borderTop=`1px solid ${c.line}`,o.innerHTML=`<span style="font-size:11px;color:${c.muted}">Secured by</span><span style="display:inline-flex;color:${c.foreground}"><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.4" style="flex-shrink:0"><circle cx="12" cy="12" r="8.5"/></svg></span><span style="font-size:12px;font-weight:600;letter-spacing:-0.02em;color:${c.foreground}">one</span>`,void y.appendChild(o)}if("connecting"===g){const e=document.createElement("span");e.className="owcb-spinner",y.appendChild(e)}else if("connected"===g){const e=document.createElement("span");e.innerHTML='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px"><path d="M3 8.5l3.5 3.5L13 5"/></svg>',e.style.display="inline-flex",y.appendChild(e)}else u&&y.appendChild(u);const p=document.createElement("span");if(p.className="owcb-label",p.textContent=C(),y.appendChild(p),"idle"===g){const e=document.createElement("span");e.innerHTML='<svg class="owcb-arrow" width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3l5 5-5 5"/></svg>',e.style.display="inline-flex",y.appendChild(e)}},E=e=>{v||(g=e,k())};return k(),e.appendChild(y),{setState:E,destroy:()=>{v=!0,y.remove()}}};function v(){if("undefined"!=typeof window&&"undefined"!=typeof customElements&&!customElements.get("one-connect-button")){class e extends HTMLElement{constructor(...e){super(...e),r(this,"onSuccess",null),r(this,"onError",null),r(this,"handle",null)}connectedCallback(){this.mount()}disconnectedCallback(){var e;null===(e=this.handle)||void 0===e||e.destroy(),this.handle=null}attributeChangedCallback(){this.isConnected&&this.handle&&this.mount()}mount(){var e,t,n,o,r;null===(e=this.handle)||void 0===e||e.destroy(),this.handle=null;const i=this.getAttribute("authorize-url");if(!i)return;const c=this.getAttribute("more-count"),l=c?parseInt(c,10):NaN;this.handle=g(this,{connect:{authorizeUrl:i,appTheme:x(this,"app-theme",["light","dark"]),onSuccess:()=>{var e;null===(e=this.onSuccess)||void 0===e||e.call(this),this.dispatchEvent(new CustomEvent("success"))},onError:e=>{var t;null===(t=this.onError)||void 0===t||t.call(this,e),this.dispatchEvent(new CustomEvent("error",{detail:e}))}},label:null!==(t=this.getAttribute("label"))&&void 0!==t?t:void 0,variant:x(this,"variant",["default","accent","block"]),theme:x(this,"theme",["light","dark"]),platforms:d(this.getAttribute("platforms")),moreCount:Number.isFinite(l)?l:void 0,description:null!==(n=this.getAttribute("description"))&&void 0!==n?n:void 0,accentColor:null!==(o=this.getAttribute("accent-color"))&&void 0!==o?o:void 0,connectedLabel:null!==(r=this.getAttribute("connected-label"))&&void 0!==r?r:void 0})}}r(e,"observedAttributes",["authorize-url","app-theme","label","variant","theme","platforms","more-count","description","accent-color","connected-label"]),customElements.define("one-connect-button",e)}}function x(e,t,n){const o=e.getAttribute(t);return o&&n.includes(o)?o:void 0}function y(e,t=e){return{connect:{authorizeUrl:e.authorizeUrl,appTheme:e.appTheme,onSuccess:()=>{var e;return null===(e=t.onSuccess)||void 0===e?void 0:e.call(t)},onError:e=>{var n;return null===(n=t.onError)||void 0===n?void 0:n.call(t,e)}},label:e.label,variant:e.variant,theme:e.theme,platforms:e.platforms,moreCount:e.moreCount,description:e.description,accentColor:e.accentColor,connectedLabel:e.connectedLabel}}function C(e){var t;return JSON.stringify([e.authorizeUrl,e.appTheme,e.label,e.variant,e.theme,null!==(t=e.platforms)&&void 0!==t?t:[],e.moreCount,e.description,e.accentColor,e.connectedLabel])}v();export{g as mountConnectButton,a as normalizePlatform,s as normalizePlatforms,y as optionsFromProps,d as parsePlatformsAttribute,C as propsIdentity,v as registerConnectButton,o as useOneConnect};
|
|
1
|
+
const e="one_connect",t="one_connect_message";let n=!1;const o=o=>{if("undefined"!=typeof window&&!n){const r=function(n){var o;const r=new URLSearchParams(n),i=r.get(e);if("success"!==i&&"error"!==i)return null;const c=null!==(o=r.get(t))&&void 0!==o?o:void 0;return c?{status:i,message:c}:{status:i}}(window.location.search);if(r){n=!0;const i=()=>{(function(n){const o=new URLSearchParams(n);return o.has(e)||o.has(t)})(window.location.search)&&window.history.replaceState(null,"",function(n){const o=new URLSearchParams(n.search);o.delete(e),o.delete(t);const r=o.toString();return`${n.pathname}${r?`?${r}`:""}${n.hash}`}(window.location))};i();for(const e of[50,500,2e3])window.setTimeout(i,e);window.setTimeout(()=>{try{var e,t,n;"success"===r.status?null===(e=o.onSuccess)||void 0===e||e.call(o):null===(t=o.onError)||void 0===t||t.call(o,null!==(n=r.message)&&void 0!==n?n:"The connection was not completed.")}catch{}},0)}}return{open:()=>{"undefined"!=typeof window&&window.location.assign((()=>{try{const e=new URL(o.authorizeUrl,"undefined"==typeof window?void 0:window.location.origin);return o.appTheme&&(e.hash=`one_theme=${o.appTheme}`),e.toString()}catch{return o.authorizeUrl}})())}}};function r(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=typeof e||!e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var o=n.call(e,t);if("object"!=typeof o)return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:t+""}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function c(e){return e.split(/[-_]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function l(e){return e?`https://assets.withone.ai/connectors/${e}.svg`:""}function a(e){var t,n,o,r;const a="string"==typeof e?{slug:e}:e,s=null!==(t=a.slug)&&void 0!==t&&t.trim()?i(a.slug):null!==(n=a.name)&&void 0!==n&&n.trim()?i(a.name):"";return s?{slug:s,name:(null===(o=a.name)||void 0===o?void 0:o.trim())||c(s),imageUrl:(null===(r=a.imageUrl)||void 0===r?void 0:r.trim())||l(s)}:null}function s(e){return(null!=e?e:[]).map(a).filter(e=>null!==e)}function d(e){const t=null==e?void 0:e.trim();if(!t)return[];if(t.startsWith("["))try{const e=JSON.parse(t);return Array.isArray(e)?e.filter(e=>"string"==typeof e||"object"==typeof e&&null!==e):[]}catch{return[]}return t.split(",").map(e=>e.trim()).filter(Boolean)}const p="one-connect-button-styles",u="cubic-bezier(.2,.9,.25,1)",C="#CCFF00",h="#3FE3A5",m="#0A0C0B",f={buttonBackground:m,buttonForeground:"#FFFFFF",surface:"#FFFFFF",foreground:m,muted:"#6B7280",line:"#D1D5DB",shadow:"0 1px 2px rgba(10,12,11,.08), 0 4px 14px rgba(10,12,11,.06)"},b={buttonBackground:"#F2F5F3",buttonForeground:m,surface:"#101312",foreground:"#F2F5F3",muted:"#8A938E",line:"#2A302D",shadow:"0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35)"};const w=(e,t)=>{var n,r,i;!function(){if("undefined"==typeof document||document.getElementById(p))return;const e=document.createElement("style");e.id=p,e.textContent=[`.owcb{font:500 14.5px/1.2 inherit;font-family:inherit;letter-spacing:-.01em;cursor:pointer;border:0;display:inline-flex;align-items:center;gap:12px;padding:12px 16px 12px 14px;border-radius:12px;transition:transform .2s ${u},box-shadow .2s ${u},opacity .2s ${u}}`,".owcb:hover{transform:translateY(-1px)}",".owcb:active{transform:translateY(0)}",`.owcb:focus-visible{outline:2px solid ${h};outline-offset:3px}`,".owcb[disabled]{cursor:default;opacity:.75;transform:none}",".owcb-stack{display:inline-flex;align-items:center;flex-shrink:0}",`.owcb-chip{width:22px;height:22px;border-radius:6px;display:grid;place-items:center;overflow:hidden;margin-left:-7px;transition:margin-left .28s ${u};position:relative;background:#FFFFFF}`,".owcb-chip:first-child{margin-left:0}",".owcb-chip img{width:14px;height:14px;display:block;object-fit:contain}",`.owcb-chip.owcb-more{font-family:ui-monospace,monospace;font-size:9px;font-weight:500;letter-spacing:-.02em;color:${m}}`,`.owcb-chip.owcb-letter{font:600 10px ui-monospace,monospace;color:${m}}`,".owcb:hover .owcb-chip{margin-left:-2px}",".owcb:hover .owcb-chip:first-child{margin-left:0}",".owcb-label{white-space:nowrap}",`.owcb-arrow{opacity:.5;transition:transform .2s ${u},opacity .2s ${u}}`,".owcb:hover .owcb-arrow{transform:translateX(2px);opacity:.8}",".owcb-spinner{width:16px;height:16px;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;opacity:.7;animation:owcb-spin .7s linear infinite}","@keyframes owcb-spin{to{transform:rotate(360deg)}}",".owcb.owcb-block{display:flex;flex-direction:column;align-items:stretch;width:100%;max-width:420px;text-align:left;padding:16px;border-radius:14px;gap:0}",".owcb-block-top{display:flex;align-items:center;justify-content:space-between;gap:12px}",".owcb-block-title{font-size:15px;font-weight:600;letter-spacing:-.01em}",".owcb-block-sub{font-size:13px;line-height:1.5;margin-top:6px;font-weight:400}",".owcb-block-foot{margin-top:14px;padding-top:12px;display:flex;align-items:center;gap:6px;font-size:11.5px;font-weight:400}","@media (prefers-reduced-motion:reduce){.owcb,.owcb-chip,.owcb-arrow{transition:none}.owcb-spinner{animation-duration:1.4s}}"].join("\n"),document.head.appendChild(e)}();const c="dark"===t.theme?b:f,l=null!==(n=t.variant)&&void 0!==n?n:"default",a=null!==(r=t.label)&&void 0!==r?r:"Connect your apps",d=null!==(i=t.connectedLabel)&&void 0!==i?i:"Connected";let w="idle",g=!1;const v=o({...t.connect,onSuccess:()=>{var e,n;E("connected"),null===(e=(n=t.connect).onSuccess)||void 0===e||e.call(n)},onError:e=>{var n,o;E("idle"),null===(n=(o=t.connect).onError)||void 0===n||n.call(o,e)}}),x=document.createElement("button");x.type="button",x.className="block"===l?"owcb owcb-block":"owcb",x.addEventListener("click",()=>{"connecting"!==w&&(E("connecting"),v.open())});const y=()=>"connecting"===w?"Connecting…":"connected"===w?d:a,k=()=>{var e,n;const o="accent"===l,r="block"===l,i=r?c.surface:o?null!==(e=t.accentColor)&&void 0!==e?e:C:c.buttonBackground,a=r?c.foreground:o?m:c.buttonForeground,d=r?c.surface:i;x.style.background="connected"!==w||r?i:h,x.style.color="connected"!==w||r?a:m,x.style.boxShadow=r?`inset 0 0 0 1px ${c.line}`:"connected"===w?"none":o?`0 1px 2px rgba(10,12,11,.1), 0 6px 20px ${null!==(n=t.accentColor)&&void 0!==n?n:C}38`:c.shadow,x.disabled="connecting"===w,x.innerHTML="";const p=function(e,t){const n=s(e.platforms);if(0===n.length)return null;const o=document.createElement("span");o.className="owcb-stack",o.setAttribute("aria-hidden","true");for(const e of n.slice(0,3)){const n=document.createElement("span");n.className="owcb-chip",n.title=e.name,n.style.boxShadow=`0 0 0 1.5px ${t}`;const r=document.createElement("img");r.alt="",r.src=e.imageUrl,r.addEventListener("error",()=>{r.remove(),n.classList.add("owcb-letter"),n.textContent=e.name.charAt(0).toUpperCase()}),n.appendChild(r),o.appendChild(n)}const r=Math.max(0,n.length-3);if(r>0){const e=document.createElement("span");e.className="owcb-chip owcb-more",e.style.boxShadow=`0 0 0 1.5px ${t}`,e.textContent=`+${r}`,o.appendChild(e)}return o}(t,d);if(r){const e=document.createElement("span");e.className="owcb-block-top";const n=document.createElement("span");if(n.className="owcb-block-title",n.textContent=y(),e.appendChild(n),"connecting"===w){const t=document.createElement("span");t.className="owcb-spinner",e.appendChild(t)}else p&&e.appendChild(p);if(x.appendChild(e),t.description&&"idle"===w){const e=document.createElement("span");e.className="owcb-block-sub",e.style.color=c.muted,e.textContent=t.description,x.appendChild(e)}const o=document.createElement("span");return o.className="owcb-block-foot",o.style.color=c.muted,o.style.borderTop=`1px solid ${c.line}`,o.innerHTML=`<span style="font-size:11px;color:${c.muted}">Secured by</span><span style="display:inline-flex;color:${c.foreground}"><svg viewBox="0 0 3152 990" width="35" height="11" fill="none" aria-hidden="true" style="flex-shrink:0;display:block"><path d="M1698.02 793.203C1525.75 793.203 1401 666.834 1401 495.102C1401 323.909 1524.67 197 1698.02 197C1870.83 197 1996.12 323.909 1996.12 495.102C1996.12 666.834 1869.75 793.203 1698.02 793.203ZM1698.02 694.916C1801.71 694.916 1884.88 615.53 1884.88 495.102C1884.88 374.133 1801.71 295.827 1698.02 295.827C1594.87 295.827 1511.71 373.053 1511.71 495.102C1511.71 616.61 1595.41 694.916 1698.02 694.916Z" fill="currentColor"/><path d="M2326.71 197.54C2480.62 197.54 2565.95 289.887 2565.95 467.56V779.702H2455.24V468.64C2455.24 361.172 2411.5 292.587 2308.35 292.587C2217.63 292.587 2140.4 356.852 2140.4 484.841V779.702H2030.77V211.041H2140.4V272.065C2185.76 225.082 2248.41 197.54 2326.71 197.54Z" fill="currentColor"/><path d="M3151.98 493.481C3151.98 514.003 3150.36 520.483 3148.74 529.124H2700.51C2715.63 637.672 2794.47 686.815 2922.46 686.815C3031.01 686.815 3099.6 650.633 3116.88 643.072V747.84C3100.68 755.4 3023.45 793.203 2908.96 793.203C2699.97 793.203 2588.18 661.434 2588.18 497.802C2588.18 332.55 2702.67 197 2871.7 197C3031.55 197 3151.98 317.969 3151.98 493.481ZM2876.02 300.688C2784.75 300.688 2720.49 345.511 2702.67 434.617H3050.45C3034.79 344.971 2967.29 300.688 2876.02 300.688Z" fill="currentColor"/><path d="M537.494 782.175C641.011 827.7 767.922 858.83 880.501 839.875C983.155 822.599 1069.64 764.946 1094.39 659.271C1114.47 573.493 1088.54 481.273 1047.27 405.311C983.124 287.246 874.768 183.791 759.847 114.657C644.926 45.5225 507.181 -5.02605 372.102 0.398669C289.698 3.69665 205.938 26.536 146.544 85.6685C112.069 119.989 88.3818 171.231 83.1266 219.823C78.1642 265.64 84.3749 310.317 99.8478 354.007C168.243 547.14 354.009 701.467 537.51 782.191L537.494 782.175ZM751.956 818.053C583.05 796.2 415.7 707.108 292.472 593.004C222.536 528.246 159.366 444.979 125.985 355.471C102.961 293.734 96.3956 223.367 122.41 161.969C137.158 127.14 160.367 98.244 190.743 75.9133C275.581 13.5752 395.819 6.99464 495.977 26.8596C608.848 49.252 713.875 99.4615 806.712 166.515C907.085 239.025 999.537 339.922 1048.42 454.627C1072.45 511.001 1083.99 570.103 1075.64 630.869C1067.38 690.957 1034.57 742.353 983.987 775.379C917.626 818.715 830.861 828.255 751.972 818.053H751.956Z" fill="currentColor"/><path d="M1103.27 661.228C1086.68 755.313 1024.9 818.622 934.021 848.69C821.751 885.846 685.146 860.263 576.251 818.915C452.515 771.942 340.336 699.91 245.157 607.813C209.002 572.83 177.255 534.456 148.837 493.031C100.384 422.402 61.7024 335.19 65.8326 248.071C68.2213 197.769 83.987 159.241 111.403 116.938C45.8289 171.308 31.404 262.511 47.1543 342.002C53.4728 373.841 62.9815 403.23 76.528 432.958C100.338 485.202 130.914 532.992 167.747 577.238C255.452 682.573 367.83 765.13 491.073 824.124C614.317 883.118 769.307 920.691 900.471 889.128C985.788 868.601 1061.47 816.095 1090.83 730.856C1098.18 708.71 1104.61 686.564 1103.25 661.244L1103.27 661.228Z" fill="currentColor"/><path d="M883.297 963.502C923.15 947.074 960.877 926.022 988.309 893.967C954.265 917.839 920.931 934.807 882.403 944.993C751.747 979.53 595.123 942.589 474.731 886.57C422.102 862.082 372.617 834.018 325.181 800.591C205.128 715.984 89.4825 597.996 36.7762 458.756C20.2092 414.973 11.7793 370.404 11.5327 323.739C11.4249 302.641 15.4627 283.747 19.0226 263.004C-2.52219 310.486 -3.66265 380.915 5.21419 431.617C32.0605 584.881 147.151 721.146 266.988 813.983C343.443 873.224 436.403 924.913 528.424 954.672C620.444 984.431 713.851 999.426 808.491 983.598C834.459 979.252 858.362 973.781 883.297 963.502Z" fill="currentColor"/><path d="M1045.09 830.737C1053.55 816.158 1061.75 802.95 1066.99 787.061C1043.75 824.541 1013.3 854.408 974.29 874.982C947.752 888.991 920.228 899.27 890.577 905.219C864.578 910.428 839.673 914.481 812.781 914.851C739.716 915.852 668.717 903.092 599.459 880.854C418.686 822.8 238.422 697.6 127.985 543.165C94.8819 496.87 68.8679 447.847 50.7135 394.031C28.1978 324.45 25.7782 258.69 53.4104 189.078C36.1345 211.825 26.9956 237.793 20.4151 264.916C18.3654 282.732 14.713 299.345 15.0828 317.561C15.9613 360.774 23.5436 402.692 38.477 443.562C73.1522 538.464 134.936 621.869 207.368 691.913C307.34 788.587 426.762 862.268 558.127 907.993C689.491 953.718 871.975 973.768 992.614 885.446C1013.28 870.312 1029.39 851.773 1045.07 830.752L1045.09 830.737Z" fill="currentColor"/><path d="M552.552 745.664C671.079 797.831 835.825 835.526 954.768 769.12C999.137 744.354 1031.99 705.626 1047.99 657.312C1086.75 540.264 1020.28 405.817 947.124 315.631C855.536 202.745 729.688 114.516 592.76 66.0323C506.349 35.4412 418.968 22.0797 328.211 37.46C262.313 48.6177 195.29 81.7054 157.563 138.234C118.912 196.133 115.891 270.785 136.758 335.882C176.519 459.926 276.769 570.763 381.01 646.277C434.995 685.391 491.662 718.864 552.567 745.664H552.552ZM210.162 422.955C199.898 402.381 189.141 382.701 184.733 360.385C182.267 347.918 184.625 333.477 184.564 320.64C184.44 293.223 194.072 269.059 209.992 247.93C219.609 235.17 229.456 224.844 243.018 215.906C341.218 151.179 476.051 175.991 576.763 223.904C661.971 264.435 737.394 321.534 796.11 395.862C836.919 447.52 871.193 513.634 880.502 579.501C888.238 634.272 873.243 695.485 826.763 729.189C807.853 742.905 785.045 751.721 761.728 755.974C721.921 763.217 688.802 758.008 649.565 748.916C594.27 736.094 542.488 715.212 492.279 688.889C441.977 662.521 396.745 631.483 353.008 595.436C314.372 563.597 280.252 537.845 248.458 498.007L248.274 497.837L221.504 461.945L211.379 445.84C194.103 418.408 177.398 391.346 165.238 361.202C151.291 326.604 142.676 291.852 143.678 254.279C146.19 160.564 203.766 96.7467 290.238 67.82C434.394 19.5985 611.561 79.4246 735.298 157.636C825.099 214.411 904.435 286.581 963.321 375.134C989.767 414.894 1010.42 456.998 1023.78 502.646C1049.84 591.66 1036.79 685.884 957.404 742.659C890.257 790.68 785.6 793.146 707.295 776.486C680.403 769.736 654.065 766.007 629.222 754.818C732.107 783.93 882.397 797.723 963.938 715.35C990.599 688.412 1007.55 656.218 1013.75 618.537C1020.87 575.232 1016.12 531.634 1002.62 489.931C985.498 437.071 956.34 384.026 922.405 340.289C876.911 281.665 823.188 230.531 762.468 187.981C681.112 130.975 588.783 88.887 490.799 71.0872C425.595 59.2514 349.463 60.4072 287.557 85.0959C256.441 97.5019 228.501 114.5 205.924 138.85C158.303 190.2 147.176 256.036 161.539 323.383C166.856 348.334 175.456 371.296 185.596 394.737C194.488 415.295 222.676 461.883 236.022 477.649L226.729 456.196L210.162 423.001V422.955ZM832.342 286.936C756.904 211.544 666.44 154.076 564.896 120.249C526.553 107.473 487.948 99.4899 447.648 96.084C401.26 92.1696 356.167 96.7313 312.215 111.341C222.337 141.208 169.399 217.277 175.255 312.333C177.567 324.847 179.555 335.774 180.603 348.503C152.971 264.944 174.454 175.236 251.356 125.581C310.797 87.2072 387.39 78.2071 456.694 86.7603C503.714 92.5549 555.387 106.656 599.078 124.317C706.016 167.546 800.764 234.615 876.741 321.395C912.002 361.664 942.085 405.756 963.876 454.532C996.779 528.166 1011.85 610.046 965.895 681.369C952.379 702.359 935.427 718.818 914.283 731.871C884.432 747.421 852.947 758.671 818.872 761.06C839.523 755.959 859.404 750.981 878.637 740.424C929.186 717.061 964.601 671.567 974.818 616.488C987.671 547.245 964.693 475.429 930.881 415.357C903.834 367.305 871.471 326.049 832.311 286.905L832.342 286.936ZM890.673 420.18C861.623 370.387 825.515 327.621 783.288 287.999C721.659 230.161 650.028 185.345 570.598 156.033C513.623 135.013 454.506 124.394 393.786 128.108C353.316 130.59 315.728 139.867 280.498 159.516C245.268 179.166 216.819 204.918 201.023 240.394L188.617 268.242C204.583 180.445 278.325 132.054 361.931 119.355C412.757 111.634 462.658 116.643 512.421 129.048C596.905 150.116 674.1 189.923 743.45 242.028C816.607 296.999 885.526 373.531 924.578 456.982C949.713 510.675 965.094 571.61 949.837 630.234C939.018 671.768 912.511 706.566 875.308 727.987C847.013 744.292 816.191 754.679 783.242 758.101C825.915 745.926 863.75 731.917 895.327 698.799C913.512 679.72 926.535 657.62 933.054 631.714C951.347 558.989 927.49 483.274 890.642 420.134L890.673 420.18ZM726.19 279.369C625.678 198.799 493.404 141.917 362.655 156.973C343.854 159.131 326.547 164.17 308.639 169.195C301.18 171.29 295.201 177.732 286.493 176.253C378.313 130.081 492.063 149.545 583.143 188.119C684.102 230.87 775.952 302.825 840.664 391.069C871.794 433.527 895.805 479.652 909.506 530.386C918.382 563.273 920.232 596.654 911.941 629.294C905.622 654.153 895.481 677.778 875.786 694.9C893.987 666.327 905.452 637.693 907.086 604.128C908.72 570.562 903.141 536.565 891.567 503.57C861.392 417.53 797.066 336.19 726.205 279.384L726.19 279.369Z" fill="currentColor"/></svg></span>`,void x.appendChild(o)}if("connecting"===w){const e=document.createElement("span");e.className="owcb-spinner",x.appendChild(e)}else if("connected"===w){const e=document.createElement("span");e.innerHTML='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px"><path d="M3 8.5l3.5 3.5L13 5"/></svg>',e.style.display="inline-flex",x.appendChild(e)}else p&&x.appendChild(p);const u=document.createElement("span");if(u.className="owcb-label",u.textContent=y(),x.appendChild(u),"idle"===w){const e=document.createElement("span");e.innerHTML='<svg class="owcb-arrow" width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3l5 5-5 5"/></svg>',e.style.display="inline-flex",x.appendChild(e)}},E=e=>{g||(w=e,k())};return k(),e.appendChild(x),{setState:E,destroy:()=>{g=!0,x.remove()}}};function g(){if("undefined"!=typeof window&&"undefined"!=typeof customElements&&!customElements.get("one-connect-button")){class e extends HTMLElement{constructor(...e){super(...e),r(this,"onSuccess",null),r(this,"onError",null),r(this,"handle",null)}connectedCallback(){this.mount()}disconnectedCallback(){var e;null===(e=this.handle)||void 0===e||e.destroy(),this.handle=null}attributeChangedCallback(){this.isConnected&&this.handle&&this.mount()}mount(){var e,t,n,o,r;null===(e=this.handle)||void 0===e||e.destroy(),this.handle=null;const i=this.getAttribute("authorize-url");i&&(this.handle=w(this,{connect:{authorizeUrl:i,appTheme:v(this,"app-theme",["light","dark"]),onSuccess:()=>{var e;null===(e=this.onSuccess)||void 0===e||e.call(this),this.dispatchEvent(new CustomEvent("success"))},onError:e=>{var t;null===(t=this.onError)||void 0===t||t.call(this,e),this.dispatchEvent(new CustomEvent("error",{detail:e}))}},label:null!==(t=this.getAttribute("label"))&&void 0!==t?t:void 0,variant:v(this,"variant",["default","accent","block"]),theme:v(this,"theme",["light","dark"]),platforms:d(this.getAttribute("platforms")),description:null!==(n=this.getAttribute("description"))&&void 0!==n?n:void 0,accentColor:null!==(o=this.getAttribute("accent-color"))&&void 0!==o?o:void 0,connectedLabel:null!==(r=this.getAttribute("connected-label"))&&void 0!==r?r:void 0}))}}r(e,"observedAttributes",["authorize-url","app-theme","label","variant","theme","platforms","description","accent-color","connected-label"]),customElements.define("one-connect-button",e)}}function v(e,t,n){const o=e.getAttribute(t);return o&&n.includes(o)?o:void 0}function x(e,t=e){return{connect:{authorizeUrl:e.authorizeUrl,appTheme:e.appTheme,onSuccess:()=>{var e;return null===(e=t.onSuccess)||void 0===e?void 0:e.call(t)},onError:e=>{var n;return null===(n=t.onError)||void 0===n?void 0:n.call(t,e)}},label:e.label,variant:e.variant,theme:e.theme,platforms:e.platforms,description:e.description,accentColor:e.accentColor,connectedLabel:e.connectedLabel}}function y(e){var t;return JSON.stringify([e.authorizeUrl,e.appTheme,e.label,e.variant,e.theme,null!==(t=e.platforms)&&void 0!==t?t:[],e.description,e.accentColor,e.connectedLabel])}g();export{w as mountConnectButton,a as normalizePlatform,s as normalizePlatforms,x as optionsFromProps,d as parsePlatformsAttribute,y as propsIdentity,g as registerConnectButton,o as useOneConnect};
|
package/dist/server/index.cjs.js
CHANGED
|
@@ -72,6 +72,18 @@ function tenancyHeaders(accessToken) {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/** When a refresh token stops working, in epoch milliseconds, from its
|
|
76
|
+
* `exp` claim. Null when the token carries no readable expiry, in which
|
|
77
|
+
* case only One can say whether it still works. */
|
|
78
|
+
function refreshTokenExpiresAt(refreshToken) {
|
|
79
|
+
try {
|
|
80
|
+
const payload = JSON.parse(Buffer.from(refreshToken.split(".")[1], "base64url").toString());
|
|
81
|
+
return typeof payload.exp === "number" && Number.isFinite(payload.exp) ? payload.exp * 1000 : null;
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
75
87
|
/** The scopes the token was granted, from its claims. Display only. */
|
|
76
88
|
function tokenScopes(accessToken) {
|
|
77
89
|
try {
|
|
@@ -91,8 +103,8 @@ function tokenScopes(accessToken) {
|
|
|
91
103
|
|
|
92
104
|
/**
|
|
93
105
|
* Where the app keeps each user's tokens: its database, a cache, an
|
|
94
|
-
* encrypted cookie. The SDK never sees a token outside these
|
|
95
|
-
*
|
|
106
|
+
* encrypted cookie. The SDK never sees a token outside these calls.
|
|
107
|
+
* `userId` is the app's own id for its user.
|
|
96
108
|
*/
|
|
97
109
|
|
|
98
110
|
/** The transaction cookie the authorize leg sets and the callback reads. */
|
|
@@ -101,6 +113,14 @@ function tokenScopes(accessToken) {
|
|
|
101
113
|
|
|
102
114
|
/** One catalog action for a platform. */
|
|
103
115
|
|
|
116
|
+
/**
|
|
117
|
+
* - `not_connected`: no tokens are stored for this user.
|
|
118
|
+
* - `refresh_failed`: One declared the grant dead (revoked, expired or
|
|
119
|
+
* reused). The tokens were cleared; ask the user to connect again.
|
|
120
|
+
* - `request_failed`: One answered with an error or could not be
|
|
121
|
+
* reached. During a refresh the tokens are kept, so retry later.
|
|
122
|
+
*/
|
|
123
|
+
|
|
104
124
|
class OneConnectError extends Error {
|
|
105
125
|
constructor(code, message, status) {
|
|
106
126
|
super(message);
|
|
@@ -128,12 +148,26 @@ class OneConnectError extends Error {
|
|
|
128
148
|
* const reply = await oneConnect.runAction(userId, { connectionKey, actionId, method, path });
|
|
129
149
|
*
|
|
130
150
|
* The Next.js and Node adapters turn the first two into route handlers.
|
|
151
|
+
*
|
|
152
|
+
* Refresh. One's access token lives an hour by default, its refresh token 30
|
|
153
|
+
* days; every refresh rotates both, and One treats a second use of a
|
|
154
|
+
* rotated refresh token as theft and revokes the whole grant. So the
|
|
155
|
+
* client refreshes one user at a time (in this process always, across
|
|
156
|
+
* processes through `tokenStore.withLock`), re-reads the store before
|
|
157
|
+
* spending a refresh token, clears tokens only when One declares the
|
|
158
|
+
* grant dead, and never lets a failing old pair delete a newer one.
|
|
131
159
|
*/
|
|
132
160
|
|
|
133
161
|
/** Refresh this long before expiry, so a call never races the clock. */
|
|
134
162
|
const REFRESH_MARGIN_MS = 60_000;
|
|
135
163
|
const CATALOG_PAGE_SIZE = 100;
|
|
136
164
|
const CATALOG_MAX_PAGES = 20;
|
|
165
|
+
|
|
166
|
+
/** What One's token endpoint answered. A network failure throws instead. */
|
|
167
|
+
|
|
168
|
+
/** The one refusal that means the grant is gone for good: revoked by the
|
|
169
|
+
* user, expired, or burned by a reused refresh token (RFC 6749 §5.2). */
|
|
170
|
+
const isDeadGrant = answer => !answer.ok && answer.status === 400 && answer.error === "invalid_grant";
|
|
137
171
|
function createOneConnect(config) {
|
|
138
172
|
const oneApiUrl = (config.oneApiUrl ?? DEFAULT_ONE_API_URL).replace(/\/+$/, "");
|
|
139
173
|
const authorizeUrl = `${oneApiUrl}/oauth/authorize`;
|
|
@@ -154,7 +188,7 @@ function createOneConnect(config) {
|
|
|
154
188
|
if (message) url.searchParams.set(RETURN_MESSAGE_PARAM, message);
|
|
155
189
|
return url.toString();
|
|
156
190
|
};
|
|
157
|
-
const
|
|
191
|
+
const postToken = async body => {
|
|
158
192
|
const response = await fetch(tokenUrl, {
|
|
159
193
|
method: "POST",
|
|
160
194
|
headers: {
|
|
@@ -163,10 +197,40 @@ function createOneConnect(config) {
|
|
|
163
197
|
},
|
|
164
198
|
body
|
|
165
199
|
});
|
|
166
|
-
if (
|
|
167
|
-
|
|
200
|
+
if (response.ok) return {
|
|
201
|
+
ok: true,
|
|
202
|
+
body: await response.json()
|
|
203
|
+
};
|
|
204
|
+
let error;
|
|
205
|
+
try {
|
|
206
|
+
const refusal = await response.json();
|
|
207
|
+
if (typeof refusal.error === "string") error = refusal.error;
|
|
208
|
+
} catch {
|
|
209
|
+
/* not an OAuth error body: a proxy page or a server error */
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
ok: false,
|
|
213
|
+
status: response.status,
|
|
214
|
+
error
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
const exchange = async body => {
|
|
218
|
+
const answer = await postToken(body);
|
|
219
|
+
if (!answer.ok) {
|
|
220
|
+
throw new OneConnectError("request_failed", `One refused the token request (HTTP ${answer.status}).`, answer.status);
|
|
168
221
|
}
|
|
169
|
-
return
|
|
222
|
+
return answer.body;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/** Runs `run` under the app's cross-process lock for this user, when
|
|
226
|
+
* the store has one. */
|
|
227
|
+
const locked = (userId, run) => tokenStore.withLock ? tokenStore.withLock(userId, run) : run();
|
|
228
|
+
|
|
229
|
+
/** Whether either token of the pair stops working within `withinMs`. */
|
|
230
|
+
const expiresWithin = (tokens, withinMs) => {
|
|
231
|
+
const horizon = Date.now() + withinMs;
|
|
232
|
+
const refreshExpiresAt = refreshTokenExpiresAt(tokens.refreshToken);
|
|
233
|
+
return tokens.expiresAt <= horizon || refreshExpiresAt !== null && refreshExpiresAt <= horizon;
|
|
170
234
|
};
|
|
171
235
|
const toTokens = response => ({
|
|
172
236
|
accessToken: response.access_token,
|
|
@@ -216,7 +280,9 @@ function createOneConnect(config) {
|
|
|
216
280
|
redirect_uri: config.redirectUri,
|
|
217
281
|
code_verifier: verifier
|
|
218
282
|
})));
|
|
219
|
-
|
|
283
|
+
// Under the lock, so a refresh in flight on another server cannot
|
|
284
|
+
// interleave with this save.
|
|
285
|
+
await locked(input.userId, () => tokenStore.saveTokens(input.userId, tokens));
|
|
220
286
|
} catch (error) {
|
|
221
287
|
const status = error instanceof OneConnectError ? error.status : undefined;
|
|
222
288
|
return fail("failed", status ? `One rejected the code exchange (HTTP ${status}).` : "One could not be reached to complete the connection.");
|
|
@@ -227,39 +293,81 @@ function createOneConnect(config) {
|
|
|
227
293
|
clearCookieName: cookieName
|
|
228
294
|
};
|
|
229
295
|
};
|
|
230
|
-
const
|
|
296
|
+
const notConnected = () => new OneConnectError("not_connected", "This user is not connected.");
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* The grant behind `failed` is dead. Clears it, unless a newer pair
|
|
300
|
+
* landed while it was failing (a reconnect's callback, another
|
|
301
|
+
* server's refresh): that pair is returned instead, because the user
|
|
302
|
+
* did nothing wrong and deleting it would disconnect them.
|
|
303
|
+
*/
|
|
304
|
+
const retire = async (userId, failed, status) => {
|
|
305
|
+
const latest = await tokenStore.loadTokens(userId);
|
|
306
|
+
if (latest && latest.refreshToken !== failed.refreshToken) return latest;
|
|
307
|
+
await tokenStore.clearTokens(userId, failed);
|
|
308
|
+
throw new OneConnectError("refresh_failed", "The connection to One has expired or was revoked. Ask the user to connect again.", status);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* The one place a refresh token is spent. Under the app's lock it
|
|
313
|
+
* re-reads the store, and refreshes only when `stillNeeded` says the
|
|
314
|
+
* stored pair still needs it: another process may have refreshed while
|
|
315
|
+
* this one waited, and spending the same refresh token twice makes One
|
|
316
|
+
* revoke the grant.
|
|
317
|
+
*/
|
|
318
|
+
const refreshUnderLock = (userId, stillNeeded) => locked(userId, async () => {
|
|
319
|
+
const current = await tokenStore.loadTokens(userId);
|
|
320
|
+
if (!current) throw notConnected();
|
|
321
|
+
if (!stillNeeded(current)) return current;
|
|
322
|
+
|
|
323
|
+
// An expired refresh token cannot work, and One answers one with a
|
|
324
|
+
// server error rather than invalid_grant, so settle it here.
|
|
325
|
+
const refreshExpiresAt = refreshTokenExpiresAt(current.refreshToken);
|
|
326
|
+
if (refreshExpiresAt !== null && refreshExpiresAt <= Date.now()) return retire(userId, current);
|
|
327
|
+
let answer;
|
|
328
|
+
try {
|
|
329
|
+
answer = await postToken(new URLSearchParams({
|
|
330
|
+
grant_type: "refresh_token",
|
|
331
|
+
refresh_token: current.refreshToken
|
|
332
|
+
}));
|
|
333
|
+
} catch {
|
|
334
|
+
throw new OneConnectError("request_failed", "One could not be reached to refresh the connection. The tokens were kept; try again.");
|
|
335
|
+
}
|
|
336
|
+
if (answer.ok) {
|
|
337
|
+
// Both tokens: One rotates the pair on every refresh.
|
|
338
|
+
const next = toTokens(answer.body);
|
|
339
|
+
await tokenStore.saveTokens(userId, next);
|
|
340
|
+
return next;
|
|
341
|
+
}
|
|
342
|
+
if (isDeadGrant(answer)) return retire(userId, current, answer.status);
|
|
343
|
+
// A server error, a rate limit, a misconfigured secret: nothing says
|
|
344
|
+
// the grant is gone, so keep the tokens and let the caller retry.
|
|
345
|
+
throw new OneConnectError("request_failed", `One could not refresh the connection (HTTP ${answer.status}). The tokens were kept; try again.`, answer.status);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
/** One refresh per user in this process; concurrent callers share it. */
|
|
349
|
+
const singleFlight = (userId, job) => {
|
|
231
350
|
const inFlight = refreshing.get(userId);
|
|
232
351
|
if (inFlight) return inFlight;
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
try {
|
|
237
|
-
const next = toTokens(await exchange(new URLSearchParams({
|
|
238
|
-
grant_type: "refresh_token",
|
|
239
|
-
refresh_token: current.refreshToken
|
|
240
|
-
})));
|
|
241
|
-
// Both tokens: One rotates the pair on every refresh.
|
|
242
|
-
await tokenStore.saveTokens(userId, next);
|
|
243
|
-
return next;
|
|
244
|
-
} catch (error) {
|
|
245
|
-
// The family is dead: revoked, expired or reused. Keeping the
|
|
246
|
-
// pair would only fail again; the user has to reconnect.
|
|
247
|
-
await tokenStore.clearTokens(userId);
|
|
248
|
-
const status = error instanceof OneConnectError ? error.status : undefined;
|
|
249
|
-
throw new OneConnectError("refresh_failed", "The connection to One has expired or was revoked. Ask the user to connect again.", status);
|
|
250
|
-
} finally {
|
|
251
|
-
refreshing.delete(userId);
|
|
252
|
-
}
|
|
253
|
-
})();
|
|
254
|
-
refreshing.set(userId, job);
|
|
255
|
-
return job;
|
|
352
|
+
const running = job().finally(() => refreshing.delete(userId));
|
|
353
|
+
refreshing.set(userId, running);
|
|
354
|
+
return running;
|
|
256
355
|
};
|
|
257
|
-
const
|
|
356
|
+
const refreshTokens = userId => singleFlight(userId, async () => {
|
|
357
|
+
const before = await tokenStore.loadTokens(userId);
|
|
358
|
+
if (!before) throw notConnected();
|
|
359
|
+
// Rotate the pair seen now; a pair someone else rotated since is
|
|
360
|
+
// already fresh.
|
|
361
|
+
return refreshUnderLock(userId, current => current.refreshToken === before.refreshToken);
|
|
362
|
+
});
|
|
363
|
+
const refreshIfExpiring = async (userId, options = {}) => {
|
|
364
|
+
const withinMs = options.withinMs ?? REFRESH_MARGIN_MS;
|
|
258
365
|
const tokens = await tokenStore.loadTokens(userId);
|
|
259
|
-
if (!tokens) throw
|
|
260
|
-
if (
|
|
261
|
-
return (
|
|
366
|
+
if (!tokens) throw notConnected();
|
|
367
|
+
if (!expiresWithin(tokens, withinMs)) return tokens;
|
|
368
|
+
return singleFlight(userId, () => refreshUnderLock(userId, current => expiresWithin(current, withinMs)));
|
|
262
369
|
};
|
|
370
|
+
const getAccessToken = async userId => (await refreshIfExpiring(userId)).accessToken;
|
|
263
371
|
const oneFetch = async (userId, path, init = {}) => {
|
|
264
372
|
const accessToken = await getAccessToken(userId);
|
|
265
373
|
const headers = new Headers(init.headers);
|
|
@@ -335,6 +443,7 @@ function createOneConnect(config) {
|
|
|
335
443
|
getAccessToken,
|
|
336
444
|
getTokens: userId => tokenStore.loadTokens(userId),
|
|
337
445
|
refreshTokens,
|
|
446
|
+
refreshIfExpiring,
|
|
338
447
|
disconnect: userId => tokenStore.clearTokens(userId),
|
|
339
448
|
listConnections,
|
|
340
449
|
listActions,
|
|
@@ -345,5 +454,6 @@ function createOneConnect(config) {
|
|
|
345
454
|
|
|
346
455
|
exports.OneConnectError = OneConnectError;
|
|
347
456
|
exports.createOneConnect = createOneConnect;
|
|
457
|
+
exports.refreshTokenExpiresAt = refreshTokenExpiresAt;
|
|
348
458
|
exports.tenancyHeaders = tenancyHeaders;
|
|
349
459
|
exports.tokenScopes = tokenScopes;
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type CompleteAuthorizationInput, type CompleteAuthorizationResult, type OneConnectServerConfig, type OneConnectTokens, type PlatformAction, type ReachableConnection, type RunActionInput, type RunActionResult, type StartAuthorizationInput, type StartAuthorizationResult } from "./types";
|
|
1
|
+
import { type CompleteAuthorizationInput, type CompleteAuthorizationResult, type OneConnectServerConfig, type OneConnectTokens, type PlatformAction, type ReachableConnection, type RefreshIfExpiringOptions, type RunActionInput, type RunActionResult, type StartAuthorizationInput, type StartAuthorizationResult } from "./types";
|
|
2
2
|
export * from "./types";
|
|
3
|
-
export { tenancyHeaders, tokenScopes } from "./oauth";
|
|
3
|
+
export { refreshTokenExpiresAt, tenancyHeaders, tokenScopes } from "./oauth";
|
|
4
4
|
export interface OneConnect {
|
|
5
5
|
/** The authorize leg: where to send the browser and the cookie to set. */
|
|
6
6
|
startAuthorization: (input?: StartAuthorizationInput) => StartAuthorizationResult;
|
|
@@ -14,8 +14,16 @@ export interface OneConnect {
|
|
|
14
14
|
getAccessToken: (userId: string) => Promise<string>;
|
|
15
15
|
/** The stored tokens, for display. Null when not connected. */
|
|
16
16
|
getTokens: (userId: string) => Promise<OneConnectTokens | null>;
|
|
17
|
-
/**
|
|
17
|
+
/** Refreshes now and returns the new pair. When another caller rotated
|
|
18
|
+
* the pair a moment earlier, returns that pair instead of rotating it
|
|
19
|
+
* a second time. */
|
|
18
20
|
refreshTokens: (userId: string) => Promise<OneConnectTokens>;
|
|
21
|
+
/** Refreshes only when the access token or the refresh token expires
|
|
22
|
+
* within `withinMs`, and returns the pair that is current afterwards.
|
|
23
|
+
* For background jobs: a frequent run keeps access tokens warm, and a
|
|
24
|
+
* daily run with a window of days keeps idle users' 30-day refresh
|
|
25
|
+
* tokens from running out. */
|
|
26
|
+
refreshIfExpiring: (userId: string, options?: RefreshIfExpiringOptions) => Promise<OneConnectTokens>;
|
|
19
27
|
/** Drops the app's copy of the tokens. The user revokes the grant
|
|
20
28
|
* itself from their One dashboard. */
|
|
21
29
|
disconnect: (userId: string) => Promise<void>;
|